Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / wiretap / catapult_dct2000.c
blob94e05511c251699731a17bf3caa98f7839a82c81
1 /* catapult_dct2000.c
3 * Wiretap Library
4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
9 #include "config.h"
10 #include "catapult_dct2000.h"
12 #include <errno.h>
13 #include <string.h>
14 #include <stdlib.h>
16 #include <wsutil/strtoi.h>
18 #include "wtap-int.h"
19 #include "file_wrappers.h"
21 #define MAX_FIRST_LINE_LENGTH 150
22 #define MAX_TIMESTAMP_LINE_LENGTH 50
23 #define MAX_LINE_LENGTH 131072
24 #define MAX_SECONDS_CHARS 16
25 #define MAX_TIMESTAMP_LEN (MAX_SECONDS_CHARS+5)
26 #define MAX_SUBSECOND_DECIMALS 4
27 #define MAX_CONTEXT_NAME 64
28 #define MAX_PROTOCOL_NAME 64
29 #define MAX_PORT_DIGITS 2
30 #define MAX_VARIANT_DIGITS 16
31 #define MAX_OUTHDR_NAME 256
32 #define AAL_HEADER_CHARS 12
34 /* 's' or 'r' of a packet as read from .out file */
35 typedef enum packet_direction_t
37 sent,
38 received
39 } packet_direction_t;
42 /***********************************************************************/
43 /* For each line, store (in case we need to dump): */
44 /* - String before time field */
45 /* - Whether or not " l " appears after timestamp */
46 typedef struct
48 char *before_time;
49 bool has_l;
50 } line_prefix_info_t;
53 /*******************************************************************/
54 /* Information stored external to a file (wtap) needed for reading and dumping */
55 typedef struct dct2000_file_externals
57 /* Remember the time at the start of capture */
58 time_t start_secs;
59 uint32_t start_usecs;
62 * The following information is needed only for dumping.
64 * XXX - Wiretap is not *supposed* to require that a packet being
65 * dumped come from a file of the same type that you currently have
66 * open; this should be fixed.
69 /* Buffer to hold first line, including magic and format number */
70 char firstline[MAX_FIRST_LINE_LENGTH];
71 int firstline_length;
73 /* Buffer to hold second line with formatted file creation data/time */
74 char secondline[MAX_TIMESTAMP_LINE_LENGTH];
75 int secondline_length;
77 /* Hash table to store text prefix data part of displayed packets.
78 Records (file offset -> line_prefix_info_t)
80 GHashTable *packet_prefix_table;
81 } dct2000_file_externals_t;
83 /* 'Magic number' at start of Catapult DCT2000 .out files. */
84 static const char catapult_dct2000_magic[] = "Session Transcript";
86 /************************************************************/
87 /* Functions called from wiretap core */
88 static bool catapult_dct2000_read(wtap *wth, wtap_rec *rec,
89 Buffer *buf, int *err, char **err_info,
90 int64_t *data_offset);
91 static bool catapult_dct2000_seek_read(wtap *wth, int64_t seek_off,
92 wtap_rec *rec,
93 Buffer *buf, int *err,
94 char **err_info);
95 static void catapult_dct2000_close(wtap *wth);
97 static bool catapult_dct2000_dump(wtap_dumper *wdh, const wtap_rec *rec,
98 const uint8_t *pd, int *err, char **err_info);
101 /************************************************************/
102 /* Private helper functions */
103 static bool read_new_line(FILE_T fh, int *length,
104 char *buf, size_t bufsize, int *err,
105 char **err_info);
106 static bool parse_line(char *linebuff, int line_length,
107 int *seconds, int *useconds,
108 long *before_time_offset, long *after_time_offset,
109 long *data_offset,
110 int *data_chars,
111 packet_direction_t *direction,
112 int *encap, bool *is_comment, bool *is_sprint,
113 char *aal_header_chars,
114 char *context_name, uint8_t *context_portp,
115 char *protocol_name, char *variant_name,
116 char *outhdr_name);
117 static bool process_parsed_line(wtap *wth,
118 const dct2000_file_externals_t *file_externals,
119 wtap_rec *rec,
120 Buffer *buf, int64_t file_offset,
121 char *linebuff, long dollar_offset,
122 int seconds, int useconds,
123 char *timestamp_string,
124 packet_direction_t direction, int encap,
125 char *context_name, uint8_t context_port,
126 char *protocol_name, char *variant_name,
127 char *outhdr_name, char *aal_header_chars,
128 bool is_comment, int data_chars,
129 int *err, char **err_info);
130 static uint8_t hex_from_char(char c);
131 static void prepare_hex_byte_from_chars_table(void);
132 static uint8_t hex_byte_from_chars(char *c);
133 static char char_from_hex(uint8_t hex);
135 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
136 packet_direction_t direction,
137 char *aal_header_chars);
138 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
139 packet_direction_t direction);
140 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
141 packet_direction_t direction);
143 static int packet_offset_equal(const void *v, const void *v2);
144 static unsigned packet_offset_hash_func(const void *v);
146 static bool get_file_time_stamp(const char *linebuff, time_t *secs, uint32_t *usecs);
147 static gboolean free_line_prefix_info(void *key, void *value, void *user_data);
149 static int dct2000_file_type_subtype = -1;
151 void register_dct2000(void);
154 /********************************************/
155 /* Open file (for reading) */
156 /********************************************/
157 wtap_open_return_val
158 catapult_dct2000_open(wtap *wth, int *err, char **err_info)
160 time_t timestamp;
161 uint32_t usecs;
162 int firstline_length = 0;
163 dct2000_file_externals_t *file_externals;
164 static char linebuff[MAX_LINE_LENGTH];
165 static bool hex_byte_table_values_set = false;
167 /* Clear errno before reading from the file */
168 errno = 0;
171 /********************************************************************/
172 /* First line needs to contain at least as many characters as magic */
174 if (!read_new_line(wth->fh, &firstline_length, linebuff,
175 sizeof linebuff, err, err_info)) {
176 if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
177 return WTAP_OPEN_ERROR;
179 else {
180 return WTAP_OPEN_NOT_MINE;
183 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
184 firstline_length >= MAX_FIRST_LINE_LENGTH) {
186 return WTAP_OPEN_NOT_MINE;
189 /* This file is not for us if it doesn't match our signature */
190 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
191 return WTAP_OPEN_NOT_MINE;
194 /* Make sure table is ready for use */
195 if (!hex_byte_table_values_set) {
196 prepare_hex_byte_from_chars_table();
197 hex_byte_table_values_set = true;
200 /*********************************************************************/
201 /* Need entry in file_externals table */
203 /* Allocate a new file_externals structure for this file */
204 file_externals = g_new0(dct2000_file_externals_t, 1);
206 /* Copy this first line into buffer so could write out later */
207 (void) g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
208 file_externals->firstline_length = firstline_length;
211 /***********************************************************/
212 /* Second line contains file timestamp */
213 /* Store this offset in file_externals */
215 if (!read_new_line(wth->fh, &(file_externals->secondline_length),
216 linebuff, sizeof linebuff, err, err_info)) {
217 g_free(file_externals);
218 if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
219 return WTAP_OPEN_ERROR;
221 else {
222 return WTAP_OPEN_NOT_MINE;
225 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
226 (!get_file_time_stamp(linebuff, &timestamp, &usecs))) {
228 /* Give up if file time line wasn't valid */
229 g_free(file_externals);
230 return WTAP_OPEN_NOT_MINE;
233 /* Fill in timestamp */
234 file_externals->start_secs = timestamp;
235 file_externals->start_usecs = usecs;
237 /* Copy this second line into buffer so could write out later */
238 (void) g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
241 /************************************************************/
242 /* File is for us. Fill in details so packets can be read */
244 /* Set our file type */
245 wth->file_type_subtype = dct2000_file_type_subtype;
247 /* Use our own encapsulation to send all packets to our stub dissector */
248 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
250 /* Callbacks for reading operations */
251 wth->subtype_read = catapult_dct2000_read;
252 wth->subtype_seek_read = catapult_dct2000_seek_read;
253 wth->subtype_close = catapult_dct2000_close;
255 /* Choose microseconds (have 4 decimal places...) */
256 wth->file_tsprec = WTAP_TSPREC_USEC;
259 /***************************************************************/
260 /* Initialise packet_prefix_table (index is offset into file) */
261 file_externals->packet_prefix_table =
262 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
264 /* Set this wtap to point to the file_externals */
265 wth->priv = (void*)file_externals;
267 *err = errno;
270 * Add an IDB; we don't know how many interfaces were
271 * involved, so we just say one interface, about which
272 * we only know the link-layer type, snapshot length,
273 * and time stamp resolution.
275 wtap_add_generated_idb(wth);
277 return WTAP_OPEN_MINE;
280 /* Ugly, but much faster than using snprintf! */
281 static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
283 int idx = 0;
285 /* Secs */
286 if (secs < 10) {
287 timestamp_string[idx++] = ((secs % 10)) + '0';
289 else if (secs < 100) {
290 timestamp_string[idx++] = ( secs / 10) + '0';
291 timestamp_string[idx++] = ((secs % 10)) + '0';
293 else if (secs < 1000) {
294 timestamp_string[idx++] = ((secs) / 100) + '0';
295 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
296 timestamp_string[idx++] = ((secs % 10)) + '0';
298 else if (secs < 10000) {
299 timestamp_string[idx++] = ((secs) / 1000) + '0';
300 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
301 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
302 timestamp_string[idx++] = ((secs % 10)) + '0';
304 else if (secs < 100000) {
305 timestamp_string[idx++] = ((secs) / 10000) + '0';
306 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
307 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
308 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
309 timestamp_string[idx++] = ((secs % 10)) + '0';
311 else if (secs < 1000000) {
312 timestamp_string[idx++] = ((secs) / 100000) + '0';
313 timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
314 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
315 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
316 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
317 timestamp_string[idx++] = ((secs % 10)) + '0';
319 else {
320 snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
321 return;
324 timestamp_string[idx++] = '.';
325 timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
326 timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
327 timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
328 timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
329 timestamp_string[idx] = '\0';
332 /**************************************************/
333 /* Read packet function. */
334 /* Look for and read the next usable packet */
335 /* - return true and details if found */
336 /**************************************************/
337 static bool
338 catapult_dct2000_read(wtap *wth, wtap_rec *rec, Buffer *buf,
339 int *err, char **err_info, int64_t *data_offset)
341 long dollar_offset, before_time_offset, after_time_offset;
342 packet_direction_t direction;
343 int encap;
345 /* Get wtap external structure for this wtap */
346 dct2000_file_externals_t *file_externals =
347 (dct2000_file_externals_t*)wth->priv;
349 /* Search for a line containing a usable packet */
350 while (1) {
351 int line_length, seconds, useconds, data_chars;
352 bool is_comment = false;
353 bool is_sprint = false;
354 int64_t this_offset;
355 static char linebuff[MAX_LINE_LENGTH+1];
356 char aal_header_chars[AAL_HEADER_CHARS];
357 char context_name[MAX_CONTEXT_NAME];
358 uint8_t context_port = 0;
359 char protocol_name[MAX_PROTOCOL_NAME+1];
360 char variant_name[MAX_VARIANT_DIGITS+1];
361 char outhdr_name[MAX_OUTHDR_NAME+1];
363 /* Get starting offset of the line we're about to read */
364 this_offset = file_tell(wth->fh);
366 /* Read a new line from file into linebuff */
367 if (!read_new_line(wth->fh, &line_length, linebuff,
368 sizeof linebuff, err, err_info)) {
369 if (*err != 0) {
370 return false; /* error */
372 /* No more lines can be read, so quit. */
373 break;
376 /* Try to parse the line as a frame record */
377 if (parse_line(linebuff, line_length, &seconds, &useconds,
378 &before_time_offset, &after_time_offset,
379 &dollar_offset,
380 &data_chars, &direction, &encap, &is_comment, &is_sprint,
381 aal_header_chars,
382 context_name, &context_port,
383 protocol_name, variant_name, outhdr_name)) {
384 line_prefix_info_t *line_prefix_info;
385 int64_t *pkey = NULL;
386 char timestamp_string[MAX_TIMESTAMP_LEN+1];
387 write_timestamp_string(timestamp_string, seconds, useconds/100);
389 /* Set data_offset to the beginning of the line we're returning.
390 This will be the seek_off parameter when this frame is re-read.
392 *data_offset = this_offset;
394 if (!process_parsed_line(wth, file_externals,
395 rec, buf, this_offset,
396 linebuff, dollar_offset,
397 seconds, useconds,
398 timestamp_string,
399 direction, encap,
400 context_name, context_port,
401 protocol_name, variant_name,
402 outhdr_name, aal_header_chars,
403 is_comment, data_chars,
404 err, err_info))
405 return false;
407 /* Store the packet prefix in the hash table */
408 line_prefix_info = g_new(line_prefix_info_t,1);
410 /* Create and use buffer for contents before time */
411 line_prefix_info->before_time = (char *)g_malloc(before_time_offset+1);
412 memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
413 line_prefix_info->before_time[before_time_offset] = '\0';
415 /* There is usually a ' l ' between the timestamp and the data. Set flag to record this. */
416 line_prefix_info->has_l = ((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
417 (strncmp(linebuff+after_time_offset, " l ", 3) == 0);
419 /* Add packet entry into table */
420 pkey = (int64_t *)g_malloc(sizeof(*pkey));
421 *pkey = this_offset;
422 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
424 /* OK, we have packet details to return */
425 return true;
429 /* No packet details to return... */
430 return false;
434 /**************************************************/
435 /* Read & seek function. */
436 /**************************************************/
437 static bool
438 catapult_dct2000_seek_read(wtap *wth, int64_t seek_off,
439 wtap_rec *rec, Buffer *buf,
440 int *err, char **err_info)
442 int length;
443 long dollar_offset, before_time_offset, after_time_offset;
444 static char linebuff[MAX_LINE_LENGTH+1];
445 char aal_header_chars[AAL_HEADER_CHARS];
446 char context_name[MAX_CONTEXT_NAME];
447 uint8_t context_port = 0;
448 char protocol_name[MAX_PROTOCOL_NAME+1];
449 char variant_name[MAX_VARIANT_DIGITS+1];
450 char outhdr_name[MAX_OUTHDR_NAME+1];
451 bool is_comment = false;
452 bool is_sprint = false;
453 packet_direction_t direction;
454 int encap;
455 int seconds, useconds, data_chars;
457 /* Get wtap external structure for this wtap */
458 dct2000_file_externals_t *file_externals =
459 (dct2000_file_externals_t*)wth->priv;
461 /* Reset errno */
462 *err = errno = 0;
464 /* Seek to beginning of packet */
465 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
466 return false;
469 /* Re-read whole line (this really should succeed) */
470 if (!read_new_line(wth->random_fh, &length, linebuff,
471 sizeof linebuff, err, err_info)) {
472 return false;
475 /* Try to parse this line again (should succeed as re-reading...) */
476 if (parse_line(linebuff, length, &seconds, &useconds,
477 &before_time_offset, &after_time_offset,
478 &dollar_offset,
479 &data_chars, &direction, &encap, &is_comment, &is_sprint,
480 aal_header_chars,
481 context_name, &context_port,
482 protocol_name, variant_name, outhdr_name)) {
484 char timestamp_string[MAX_TIMESTAMP_LEN+1];
485 write_timestamp_string(timestamp_string, seconds, useconds/100);
487 if (!process_parsed_line(wth, file_externals,
488 rec, buf, seek_off,
489 linebuff, dollar_offset,
490 seconds, useconds,
491 timestamp_string,
492 direction, encap,
493 context_name, context_port,
494 protocol_name, variant_name,
495 outhdr_name, aal_header_chars,
496 is_comment, data_chars,
497 err, err_info)) {
498 return false;
501 *err = errno = 0;
502 return true;
505 /* If get here, must have failed */
506 *err = errno;
507 *err_info = ws_strdup_printf("catapult dct2000: seek_read failed to read/parse "
508 "line at position %" PRId64,
509 seek_off);
510 return false;
514 /***************************************************************************/
515 /* Free dct2000-specific capture info from file that was open for reading */
516 /***************************************************************************/
517 static void
518 catapult_dct2000_close(wtap *wth)
520 /* Get externals for this file */
521 dct2000_file_externals_t *file_externals =
522 (dct2000_file_externals_t*)wth->priv;
524 /* Free up its line prefix values */
525 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
526 free_line_prefix_info, NULL);
527 /* Free up its line prefix table */
528 g_hash_table_destroy(file_externals->packet_prefix_table);
534 /***************************/
535 /* Dump functions */
536 /***************************/
538 typedef struct {
539 bool first_packet_written;
540 nstime_t start_time;
541 } dct2000_dump_t;
543 /*****************************************************/
544 /* The file that we are writing to has been opened. */
545 /* Set other dump callbacks. */
546 /*****************************************************/
547 static bool
548 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_)
550 /* Fill in other dump callbacks */
551 wdh->subtype_write = catapult_dct2000_dump;
553 return true;
556 /*********************************************************/
557 /* Respond to queries about which encap types we support */
558 /* writing to. */
559 /*********************************************************/
560 static int
561 catapult_dct2000_dump_can_write_encap(int encap)
563 switch (encap) {
564 case WTAP_ENCAP_CATAPULT_DCT2000:
565 /* We support this */
566 return 0;
568 default:
569 /* But can't write to any other formats... */
570 return WTAP_ERR_UNWRITABLE_ENCAP;
575 /*****************************************/
576 /* Write a single packet out to the file */
577 /*****************************************/
579 static bool
580 catapult_dct2000_dump(wtap_dumper *wdh, const wtap_rec *rec,
581 const uint8_t *pd, int *err, char **err_info _U_)
583 const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
584 uint32_t n;
585 line_prefix_info_t *prefix = NULL;
586 char time_string[MAX_TIMESTAMP_LEN];
587 bool is_comment;
588 bool is_sprint = false;
589 dct2000_dump_t *dct2000;
590 int consecutive_slashes=0;
591 char *p_c;
593 /******************************************************/
594 /* Get the file_externals structure for this file */
595 /* Find wtap external structure for this wtap */
596 dct2000_file_externals_t *file_externals =
597 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
599 /* We can only write packet records. */
600 if (rec->rec_type != REC_TYPE_PACKET) {
601 *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
602 return false;
606 * Make sure this packet doesn't have a link-layer type that
607 * differs from the one for the file (which should always
608 * be WTAP_ENCAP_CATAPULT_DCT2000).
610 if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
611 *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
612 return false;
615 dct2000 = (dct2000_dump_t *)wdh->priv;
616 if (dct2000 == NULL) {
618 /* Write out saved first line */
619 if (!wtap_dump_file_write(wdh, file_externals->firstline,
620 file_externals->firstline_length, err)) {
621 return false;
623 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
624 return false;
627 /* Also write out saved second line with timestamp corresponding to the
628 opening time of the log.
630 if (!wtap_dump_file_write(wdh, file_externals->secondline,
631 file_externals->secondline_length, err)) {
632 return false;
634 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
635 return false;
638 /* Allocate the dct2000-specific dump structure */
639 dct2000 = g_new(dct2000_dump_t, 1);
640 wdh->priv = (void *)dct2000;
642 /* Copy time of beginning of file */
643 dct2000->start_time.secs = file_externals->start_secs;
644 dct2000->start_time.nsecs =
645 (file_externals->start_usecs * 1000);
647 /* Set flag so don't write header out again */
648 dct2000->first_packet_written = true;
652 /******************************************************************/
653 /* Write out this packet's prefix, including calculated timestamp */
655 /* Look up line data prefix using stored offset */
656 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
657 (const void*)&(pseudo_header->dct2000.seek_off));
659 /* Write out text before timestamp */
660 if (!wtap_dump_file_write(wdh, prefix->before_time,
661 strlen(prefix->before_time), err)) {
662 return false;
665 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
666 /* This is much faster than strstr() for "/////" */
667 p_c = prefix->before_time;
668 while (p_c && (*p_c != '/')) {
669 p_c++;
671 while (p_c && (*p_c == '/')) {
672 consecutive_slashes++;
673 p_c++;
675 is_comment = (consecutive_slashes == 5);
677 /* Calculate time of this packet to write, relative to start of dump */
678 if (rec->ts.nsecs >= dct2000->start_time.nsecs) {
679 write_timestamp_string(time_string,
680 (int)(rec->ts.secs - dct2000->start_time.secs),
681 (rec->ts.nsecs - dct2000->start_time.nsecs) / 100000);
683 else {
684 write_timestamp_string(time_string,
685 (int)(rec->ts.secs - dct2000->start_time.secs-1),
686 ((1000000000 + (rec->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
689 /* Write out the calculated timestamp */
690 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
691 return false;
694 /* Write out text between timestamp and start of hex data */
695 if (prefix->has_l) {
696 if (!wtap_dump_file_write(wdh, " l ", 3, err)) {
697 return false;
701 /****************************************************************/
702 /* Need to skip stub header at start of pd before we reach data */
704 /* Context name */
705 for (n=0; pd[n] != '\0'; n++);
706 n++;
708 /* Context port number */
709 n++;
711 /* Timestamp */
712 for (; pd[n] != '\0'; n++);
713 n++;
715 /* Protocol name */
716 if (is_comment) {
717 is_sprint = (strcmp((const char *)pd+n, "sprint") == 0);
719 for (; pd[n] != '\0'; n++);
720 n++;
722 /* Variant number (as string) */
723 for (; pd[n] != '\0'; n++);
724 n++;
726 /* Outhdr (as string) */
727 for (; pd[n] != '\0'; n++);
728 n++;
730 /* Direction & encap */
731 n += 2;
734 /**************************************/
735 /* Remainder is encapsulated protocol */
736 if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
737 return false;
740 if (!is_comment) {
741 /* Each binary byte is written out as 2 hex string chars */
742 for (; n < rec->rec_header.packet_header.len; n++) {
743 char c[2];
744 c[0] = char_from_hex((uint8_t)(pd[n] >> 4));
745 c[1] = char_from_hex((uint8_t)(pd[n] & 0x0f));
747 /* Write both hex chars of byte together */
748 if (!wtap_dump_file_write(wdh, c, 2, err)) {
749 return false;
753 else {
754 /* Comment */
755 if (!wtap_dump_file_write(wdh, pd+n, rec->rec_header.packet_header.len-n, err)) {
756 return false;
760 /* End the line */
761 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
762 return false;
765 return true;
769 /****************************/
770 /* Private helper functions */
771 /****************************/
773 /**********************************************************************/
774 /* Read a new line from the file, starting at offset. */
775 /* - writes data to its argument linebuff */
776 /* - on return 'offset' will point to the next position to read from */
777 /* - return true if this read is successful */
778 /**********************************************************************/
779 static bool
780 read_new_line(FILE_T fh, int *length,
781 char *linebuff, size_t linebuffsize, int *err, char **err_info)
783 /* Read in a line */
784 int64_t pos_before = file_tell(fh);
786 if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
787 /* No characters found, or error */
788 *err = file_error(fh, err_info);
789 return false;
792 /* Set length (avoiding strlen()) and offset.. */
793 *length = (int)(file_tell(fh) - pos_before);
795 /* ...but don't want to include newline in line length */
796 if (*length > 0 && linebuff[*length-1] == '\n') {
797 linebuff[*length-1] = '\0';
798 *length = *length - 1;
800 /* Nor do we want '\r' (as will be written when log is created on windows) */
801 if (*length > 0 && linebuff[*length-1] == '\r') {
802 linebuff[*length-1] = '\0';
803 *length = *length - 1;
806 return true;
810 /**********************************************************************/
811 /* Parse a line from buffer, by identifying: */
812 /* - context, port and direction of packet */
813 /* - timestamp */
814 /* - data position and length */
815 /* Return true if this packet looks valid and can be displayed */
816 /**********************************************************************/
817 static bool
818 parse_line(char *linebuff, int line_length,
819 int *seconds, int *useconds,
820 long *before_time_offset, long *after_time_offset,
821 long *data_offset, int *data_chars,
822 packet_direction_t *direction,
823 int *encap, bool *is_comment, bool *is_sprint,
824 char *aal_header_chars,
825 char *context_name, uint8_t *context_portp,
826 char *protocol_name, char *variant_name,
827 char *outhdr_name)
829 int n = 0;
830 int port_digits;
831 char port_number_string[MAX_PORT_DIGITS+1];
832 int variant_digits;
833 int variant = 1;
834 int protocol_chars;
835 int outhdr_chars;
837 char seconds_buff[MAX_SECONDS_CHARS+1];
838 int seconds_chars;
839 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
840 int subsecond_decimals_chars;
841 bool skip_first_byte = false;
842 bool atm_header_present = false;
844 *is_comment = false;
845 *is_sprint = false;
847 /* Read context name until find '.' */
848 for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
849 if (linebuff[n] == '/') {
850 context_name[n] = '\0';
852 /* If not a comment (/////), not a valid line */
853 if (strncmp(linebuff+n, "/////", 5) != 0) {
854 return false;
857 /* There is no variant, outhdr, etc. Set protocol to be a comment */
858 (void) g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
859 *is_comment = true;
860 break;
862 if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
863 return false;
865 context_name[n] = linebuff[n];
867 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
868 return false;
871 /* Reset strings (that won't be set by comments) */
872 variant_name[0] = '\0';
873 outhdr_name[0] = '\0';
874 port_number_string[0] = '\0';
876 if (!(*is_comment)) {
877 /* '.' must follow context name */
878 if (linebuff[n] != '.') {
879 return false;
881 context_name[n] = '\0';
882 /* Skip it */
883 n++;
885 /* Now read port number */
886 for (port_digits = 0;
887 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
888 n++, port_digits++) {
890 if (!g_ascii_isdigit(linebuff[n])) {
891 return false;
893 port_number_string[port_digits] = linebuff[n];
895 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
896 return false;
899 /* Slash char must follow port number */
900 if (linebuff[n] != '/')
902 return false;
904 port_number_string[port_digits] = '\0';
905 if (port_digits == 1) {
906 *context_portp = port_number_string[0] - '0';
908 else {
909 /* Everything in here is a digit, so we don't need to check
910 whether what follows the number is anything other than
911 a '\0'. */
912 if (!ws_strtou8(port_number_string, NULL, context_portp)) {
913 return false;
916 /* Skip it */
917 n++;
919 /* Now for the protocol name */
920 for (protocol_chars = 0;
921 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
922 n++, protocol_chars++) {
924 if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '.')) {
925 return false;
927 protocol_name[protocol_chars] = linebuff[n];
929 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
930 /* If doesn't fit, fail rather than truncate */
931 return false;
933 protocol_name[protocol_chars] = '\0';
935 /* Slash char must follow protocol name */
936 if (linebuff[n] != '/') {
937 return false;
939 /* Skip it */
940 n++;
943 /* Following the / is the variant number. No digits indicate 1 */
944 for (variant_digits = 0;
945 (g_ascii_isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
946 n++, variant_digits++) {
948 if (!g_ascii_isdigit(linebuff[n])) {
949 return false;
951 variant_name[variant_digits] = linebuff[n];
953 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
954 return false;
957 if (variant_digits > 0) {
958 variant_name[variant_digits] = '\0';
959 if (variant_digits == 1) {
960 variant = variant_name[0] - '0';
962 else {
963 if (!ws_strtoi32(variant_name, NULL, &variant)) {
964 return false;
968 else {
969 variant_name[0] = '1';
970 variant_name[1] = '\0';
974 /* Outheader values may follow */
975 if (linebuff[n] == ',') {
976 /* Skip , */
977 n++;
979 for (outhdr_chars = 0;
980 (g_ascii_isdigit(linebuff[n]) || linebuff[n] == ',') &&
981 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
982 n++, outhdr_chars++) {
984 if (!g_ascii_isdigit(linebuff[n]) && (linebuff[n] != ',')) {
985 return false;
987 outhdr_name[outhdr_chars] = linebuff[n];
989 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
990 return false;
992 /* Terminate (possibly empty) string */
993 outhdr_name[outhdr_chars] = '\0';
998 /******************************************************************/
999 /* Now check whether we know how to use a packet of this protocol */
1001 if ((strcmp(protocol_name, "ip") == 0) ||
1002 (strcmp(protocol_name, "sctp") == 0) ||
1003 (strcmp(protocol_name, "gre") == 0) ||
1004 (strcmp(protocol_name, "mipv6") == 0) ||
1005 (strcmp(protocol_name, "igmp") == 0)) {
1007 *encap = WTAP_ENCAP_RAW_IP;
1010 /* FP may be carried over ATM, which has separate atm header to parse */
1011 else
1012 if ((strcmp(protocol_name, "fp") == 0) ||
1013 (strncmp(protocol_name, "fp_r", 4) == 0)) {
1015 if ((variant > 256) && (variant % 256 == 3)) {
1016 /* FP over udp is contained in IPPrim... */
1017 *encap = 0;
1019 else {
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;
1031 else
1032 if (strcmp(protocol_name, "ppp") == 0) {
1033 *encap = WTAP_ENCAP_PPP;
1035 else
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;
1041 else
1042 if (strcmp(protocol_name, "isdn_l2") == 0) {
1043 *encap = WTAP_ENCAP_ISDN;
1045 else
1046 if (strcmp(protocol_name, "ethernet") == 0) {
1047 *encap = WTAP_ENCAP_ETHERNET;
1049 else
1050 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1051 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1053 *encap = DCT2000_ENCAP_SSCOP;
1055 else
1056 if (strcmp(protocol_name, "frelay_l2") == 0) {
1057 *encap = WTAP_ENCAP_FRELAY;
1059 else
1060 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1061 *encap = DCT2000_ENCAP_MTP2;
1063 else
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;
1071 else {
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++);
1084 /* Skip it */
1085 n++;
1086 if (n+1 >= line_length) {
1087 return false;
1090 /* Read consecutive hex chars into atm header buffer */
1091 for (;
1092 ((n < line_length) &&
1093 (linebuff[n] >= '0') && (linebuff[n] <= '?') &&
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 (!g_ascii_isdigit(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) {
1105 return false;
1109 /* Skip next '/' */
1110 n++;
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 (g_ascii_isdigit(linebuff[n])) {
1116 while ((n+1 < line_length) && linebuff[n] != '/') {
1117 n++;
1121 /* Skip '/' */
1122 while ((n+1 < line_length) && linebuff[n] == '/') {
1123 n++;
1126 /* Skip a space that may happen here */
1127 if ((n+1 < line_length) && linebuff[n] == ' ') {
1128 n++;
1131 /* Next character gives direction of message (must be 's' or 'r') */
1132 if (!(*is_comment)) {
1133 if (linebuff[n] == 's') {
1134 *direction = sent;
1136 else
1137 if (linebuff[n] == 'r') {
1138 *direction = received;
1140 else {
1141 return false;
1143 /* Skip it */
1144 n++;
1146 else {
1147 *direction = sent;
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) {
1159 return false;
1162 for (; (n < line_length) && !g_ascii_isdigit(linebuff[n]); n++);
1163 if (n >= line_length) {
1164 return false;
1167 *before_time_offset = n;
1169 /* Seconds */
1170 for (seconds_chars = 0;
1171 (linebuff[n] != '.') &&
1172 (seconds_chars <= MAX_SECONDS_CHARS) &&
1173 (n < line_length);
1174 n++, seconds_chars++) {
1176 if (!g_ascii_isdigit(linebuff[n])) {
1177 /* Found a non-digit before decimal point. Fail */
1178 return false;
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 */
1184 return false;
1187 /* Convert found value into number */
1188 seconds_buff[seconds_chars] = '\0';
1189 /* Already know they are digits, so avoid expense of ws_strtoi32() */
1190 int multiplier = 1;
1191 *seconds = 0;
1192 for (int d=seconds_chars-1; d >= 0; d--) {
1193 *seconds += ((seconds_buff[d]-'0')*multiplier);
1194 multiplier *= 10;
1197 /* The decimal point must follow the last of the seconds digits */
1198 if (linebuff[n] != '.') {
1199 return false;
1201 /* Skip it */
1202 n++;
1204 /* Subsecond decimal digits (expect 4-digit accuracy) */
1205 for (subsecond_decimals_chars = 0;
1206 (linebuff[n] != ' ') &&
1207 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1208 (n < line_length);
1209 n++, subsecond_decimals_chars++) {
1211 if (!g_ascii_isdigit(linebuff[n])) {
1212 return false;
1214 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1216 if (subsecond_decimals_chars != MAX_SUBSECOND_DECIMALS || n >= line_length) {
1217 /* There should be exactly 4 subsecond digits - give up if not */
1218 return false;
1220 /* Convert found value into microseconds */
1221 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1222 /* Already know they are digits, so avoid expense of ws_strtoi32() */
1223 *useconds = ((subsecond_decimals_buff[0]-'0') * 100000) +
1224 ((subsecond_decimals_buff[1]-'0') * 10000) +
1225 ((subsecond_decimals_buff[2]-'0') * 1000) +
1226 ((subsecond_decimals_buff[3]-'0') * 100);
1228 /* Space character must follow end of timestamp */
1229 if (linebuff[n] != ' ') {
1230 return false;
1233 *after_time_offset = n++;
1235 /* If we have a string message, it could either be a comment (with '$') or
1236 a sprint line (no '$') */
1237 if (*is_comment) {
1238 if (strncmp(linebuff+n, "l $", 3) != 0) {
1239 *is_sprint = true;
1240 (void) g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
1244 if (!(*is_sprint)) {
1245 /* Now skip ahead to find start of data (marked by '$') */
1246 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1247 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1248 return false;
1250 /* Skip it */
1251 n++;
1254 /* Set offset to data start within line */
1255 *data_offset = n;
1257 /* Set number of chars that comprise the hex string protocol data */
1258 *data_chars = line_length - n;
1260 /* May need to skip first byte (2 hex string chars) */
1261 if (skip_first_byte) {
1262 *data_offset += 2;
1263 *data_chars -= 2;
1266 return true;
1269 /***********************************/
1270 /* Process results of parse_line() */
1271 /***********************************/
1272 static bool
1273 process_parsed_line(wtap *wth, const dct2000_file_externals_t *file_externals,
1274 wtap_rec *rec,
1275 Buffer *buf, int64_t file_offset,
1276 char *linebuff, long dollar_offset,
1277 int seconds, int useconds, char *timestamp_string,
1278 packet_direction_t direction, int encap,
1279 char *context_name, uint8_t context_port,
1280 char *protocol_name, char *variant_name,
1281 char *outhdr_name, char *aal_header_chars,
1282 bool is_comment, int data_chars,
1283 int *err, char **err_info)
1285 int n;
1286 int stub_offset = 0;
1287 size_t length;
1288 uint8_t *frame_buffer;
1290 rec->rec_type = REC_TYPE_PACKET;
1291 rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
1292 rec->presence_flags = WTAP_HAS_TS;
1294 /* Make sure all packets go to Catapult DCT2000 dissector */
1295 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
1297 /* Fill in timestamp (capture base + packet offset) */
1298 rec->ts.secs = file_externals->start_secs + seconds;
1299 if ((file_externals->start_usecs + useconds) >= 1000000) {
1300 rec->ts.secs++;
1302 rec->ts.nsecs =
1303 ((file_externals->start_usecs + useconds) % 1000000) *1000;
1306 * Calculate the length of the stub info and the packet data.
1307 * The packet data length is half bytestring length.
1309 rec->rec_header.packet_header.caplen = (unsigned)strlen(context_name)+1 + /* Context name */
1310 1 + /* port */
1311 (unsigned)strlen(timestamp_string)+1 + /* timestamp */
1312 (unsigned)strlen(variant_name)+1 + /* variant */
1313 (unsigned)strlen(outhdr_name)+1 + /* outhdr */
1314 (unsigned)strlen(protocol_name)+1 + /* Protocol name */
1315 1 + /* direction */
1316 1 + /* encap */
1317 (is_comment ? data_chars : (data_chars/2));
1318 if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
1320 * Probably a corrupt capture file; return an error,
1321 * so that our caller doesn't blow up trying to allocate
1322 * space for an immensely-large packet.
1324 *err = WTAP_ERR_BAD_FILE;
1325 *err_info = ws_strdup_printf("catapult dct2000: File has %u-byte packet, bigger than maximum of %u",
1326 rec->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
1327 return false;
1329 rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
1331 /*****************************/
1332 /* Get the data buffer ready */
1333 ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
1334 frame_buffer = ws_buffer_start_ptr(buf);
1336 /******************************************/
1337 /* Write the stub info to the data buffer */
1339 /* Context name */
1340 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1341 stub_offset += (int)(length + 1);
1343 /* Context port number */
1344 frame_buffer[stub_offset] = context_port;
1345 stub_offset++;
1347 /* Timestamp within file (terminated string) */
1348 length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1349 stub_offset += (int)(length + 1);
1351 /* Protocol name (terminated string) */
1352 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1353 stub_offset += (int)(length + 1);
1355 /* Protocol variant number (as terminated string) */
1356 length = g_strlcpy((char*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1357 stub_offset += (int)(length + 1);
1359 /* Outhdr (terminated string) */
1360 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1361 stub_offset += (int)(length + 1);
1363 /* Direction */
1364 frame_buffer[stub_offset++] = direction;
1366 /* Encap */
1367 frame_buffer[stub_offset++] = (uint8_t)encap;
1369 if (!is_comment) {
1370 /***********************************************************/
1371 /* Copy packet data into buffer, converting from ascii hex */
1372 for (n=0; n < data_chars; n+=2) {
1373 frame_buffer[stub_offset + n/2] =
1374 hex_byte_from_chars(linebuff+dollar_offset+n);
1377 else {
1378 /***********************************************************/
1379 /* Copy packet data into buffer, just copying ascii chars */
1380 for (n=0; n < data_chars; n++) {
1381 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
1385 /*****************************************/
1386 /* Set packet pseudo-header if necessary */
1387 rec->rec_header.packet_header.pseudo_header.dct2000.seek_off = file_offset;
1388 rec->rec_header.packet_header.pseudo_header.dct2000.wth = wth;
1390 switch (encap) {
1391 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1392 set_aal_info(&rec->rec_header.packet_header.pseudo_header, direction, aal_header_chars);
1393 break;
1394 case WTAP_ENCAP_ISDN:
1395 set_isdn_info(&rec->rec_header.packet_header.pseudo_header, direction);
1396 break;
1397 case WTAP_ENCAP_PPP:
1398 set_ppp_info(&rec->rec_header.packet_header.pseudo_header, direction);
1399 break;
1401 default:
1402 /* Other supported types don't need to set anything here... */
1403 break;
1406 return true;
1409 /*********************************************/
1410 /* Fill in atm pseudo-header with known info */
1411 /*********************************************/
1412 static void
1413 set_aal_info(union wtap_pseudo_header *pseudo_header,
1414 packet_direction_t direction,
1415 char *aal_header_chars)
1417 /* 'aal_head_chars' has this format (for AAL2 at least):
1418 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1419 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1420 Channel Identifier (8 bits) | ...
1423 /* Indicate that this is a reassembled PDU */
1424 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1426 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1427 TODO: Can we infer the correct value here?
1428 Meanwhile, just use the direction to make them distinguishable...
1430 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1432 /* Assume always AAL2 for FP */
1433 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1435 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1436 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1438 /* vpi is 8 bits (2nd & 3rd nibble) */
1439 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1440 hex_byte_from_chars(aal_header_chars+1);
1442 /* vci is next 16 bits */
1443 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1444 ((hex_from_char(aal_header_chars[3]) << 12) |
1445 (hex_from_char(aal_header_chars[4]) << 8) |
1446 (hex_from_char(aal_header_chars[5]) << 4) |
1447 hex_from_char(aal_header_chars[6]));
1449 /* 0 means we don't know how many cells the frame comprises. */
1450 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1452 /* cid is usually last byte. Unless last char is not hex digit, in which
1453 case cid is derived from last char in ascii */
1454 if (g_ascii_isalnum(aal_header_chars[11])) {
1455 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1456 hex_byte_from_chars(aal_header_chars+10);
1458 else {
1459 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1460 (int)aal_header_chars[11] - '0';
1465 /**********************************************/
1466 /* Fill in isdn pseudo-header with known info */
1467 /**********************************************/
1468 static void
1469 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1470 packet_direction_t direction)
1472 /* This field is used to set the 'Source' and 'Destination' columns to
1473 'User' or 'Network'. If we assume that we're simulating the network,
1474 treat Received messages as being destined for the network.
1476 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1478 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1479 everything else would be treated as a B-channel
1481 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1485 /*********************************************/
1486 /* Fill in ppp pseudo-header with known info */
1487 /*********************************************/
1488 static void
1489 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1490 packet_direction_t direction)
1492 /* Set direction. */
1493 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1497 /********************************************************/
1498 /* Return hex nibble equivalent of hex string character */
1499 /********************************************************/
1500 static uint8_t
1501 hex_from_char(char c)
1503 if ((c >= '0') && (c <= '9')) {
1504 return c - '0';
1507 if ((c >= 'a') && (c <= 'f')) {
1508 return 0x0a + (c - 'a');
1511 /* Not a valid hex string character */
1512 return 0xff;
1517 /* Table allowing fast lookup from a pair of ascii hex characters to a uint8_t */
1518 static uint8_t s_tableValues[256][256];
1520 /* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1521 static void prepare_hex_byte_from_chars_table(void)
1523 const unsigned char hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1524 'a', 'b', 'c', 'd', 'e', 'f' };
1526 int i, j;
1527 for (i=0; i < 16; i++) {
1528 for (j=0; j < 16; j++) {
1529 s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1534 /* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1535 static uint8_t hex_byte_from_chars(char *c)
1537 /* Return value from quick table lookup */
1538 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1543 /********************************************************/
1544 /* Return character corresponding to hex nibble value */
1545 /********************************************************/
1546 static char
1547 char_from_hex(uint8_t hex)
1549 static const char hex_lookup[16] =
1550 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1552 if (hex > 15) {
1553 return '?';
1556 return hex_lookup[hex];
1559 /***********************************************/
1560 /* Equality test for packet prefix hash tables */
1561 /***********************************************/
1562 static int
1563 packet_offset_equal(const void *v, const void *v2)
1565 /* Dereferenced pointers must have same int64_t offset value */
1566 return (*(const int64_t*)v == *(const int64_t*)v2);
1570 /********************************************/
1571 /* Hash function for packet-prefix hash table */
1572 /********************************************/
1573 static unsigned
1574 packet_offset_hash_func(const void *v)
1576 /* Use low-order bits of int64_t offset value */
1577 return (unsigned)(*(const int64_t*)v);
1581 /************************************************************************/
1582 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1583 /* Set secs and usecs as output */
1584 /* Return false if no valid time can be read */
1585 /************************************************************************/
1586 static bool
1587 get_file_time_stamp(const char *linebuff, time_t *secs, uint32_t *usecs)
1589 struct tm tm;
1590 #define MAX_MONTH_LETTERS 9
1591 char month[MAX_MONTH_LETTERS+1];
1593 int day, year, hour, minute, second;
1594 int scan_found;
1596 /* If line longer than expected, file is probably not correctly formatted */
1597 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1598 return false;
1601 /********************************************************/
1602 /* Scan for all fields */
1603 scan_found = sscanf(linebuff, "%9s %2d, %4d %2d:%2d:%2d.%4u",
1604 month, &day, &year, &hour, &minute, &second, usecs);
1605 if (scan_found != 7) {
1606 /* Give up if not all found */
1607 return false;
1610 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1611 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1612 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1613 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1614 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1615 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1616 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1617 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1618 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1619 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1620 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1621 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1622 else {
1623 /* Give up if not found a properly-formatted date */
1624 return false;
1627 /******************************************************/
1628 /* Fill in remaining fields and return it in a time_t */
1629 tm.tm_year = year - 1900;
1630 tm.tm_mday = day;
1631 tm.tm_hour = hour;
1632 tm.tm_min = minute;
1633 tm.tm_sec = second;
1634 tm.tm_isdst = -1; /* daylight saving time info not known */
1636 /* Get seconds from this time */
1637 *secs = mktime(&tm);
1639 /* Multiply 4 digits given to get micro-seconds */
1640 *usecs = *usecs * 100;
1642 return true;
1645 /* Free the data allocated inside a line_prefix_info_t */
1646 static gboolean
1647 free_line_prefix_info(void *key, void *value,
1648 void *user_data _U_)
1650 line_prefix_info_t *info = (line_prefix_info_t*)value;
1652 /* Free the 64-bit key value */
1653 g_free(key);
1655 /* Free string */
1656 g_free(info->before_time);
1658 /* And the structure itself */
1659 g_free(info);
1661 /* Item will always be removed from table */
1662 return true;
1665 static const struct supported_block_type dct2000_blocks_supported[] = {
1667 * We support packet blocks, with no comments or other options.
1669 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1672 static const struct file_type_subtype_info dct2000_info = {
1673 "Catapult DCT2000 trace (.out format)", "dct2000", "out", NULL,
1674 false, BLOCKS_SUPPORTED(dct2000_blocks_supported),
1675 catapult_dct2000_dump_can_write_encap, catapult_dct2000_dump_open, NULL
1678 void register_dct2000(void)
1680 dct2000_file_type_subtype = wtap_register_file_type_subtype(&dct2000_info);
1683 * Register name for backwards compatibility with the
1684 * wtap_filetypes table in Lua.
1686 wtap_register_backwards_compatibility_lua_name("CATAPULT_DCT2000",
1687 dct2000_file_type_subtype);
1691 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1693 * Local variables:
1694 * c-basic-offset: 4
1695 * tab-width: 8
1696 * indent-tabs-mode: nil
1697 * End:
1699 * vi: set shiftwidth=4 tabstop=8 expandtab:
1700 * :indentSize=4:tabSize=8:noTabs=true: