Coverage Report

Created: 2026-07-12 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/catapult_dct2000.c
Line
Count
Source
1
/* catapult_dct2000.c
2
 *
3
 * Wiretap Library
4
 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
5
 *
6
 * SPDX-License-Identifier: GPL-2.0-or-later
7
 */
8
9
#include "config.h"
10
#include "catapult_dct2000.h"
11
12
#include <errno.h>
13
#include <string.h>
14
#include <stdlib.h>
15
16
#include <wsutil/strtoi.h>
17
18
#include "wtap_module.h"
19
#include "file_wrappers.h"
20
21
/*
22
 * Catapult DCT2000 (.out files)
23
 *
24
 * DCT2000 test systems produce ascii text-based .out files for ports
25
 * that have logging enabled. When being read, the data part of the message is
26
 * prefixed with a short header that provides some context (context+port,
27
 * direction, original timestamp, etc).
28
 *
29
 * You can choose to suppress the reading of non-standard protocols
30
 * (i.e. messages between layers rather than the well-known link-level protocols
31
 * usually found on board ports).
32
 */
33
34
0
#define MAX_FIRST_LINE_LENGTH      150
35
0
#define MAX_TIMESTAMP_LINE_LENGTH  50
36
#define MAX_LINE_LENGTH            131072
37
0
#define MAX_SECONDS_CHARS          16
38
0
#define MAX_TIMESTAMP_LEN          (MAX_SECONDS_CHARS+5)
39
0
#define MAX_SUBSECOND_DECIMALS     4
40
0
#define MAX_CONTEXT_NAME           64
41
0
#define MAX_PROTOCOL_NAME          64
42
0
#define MAX_PORT_DIGITS            2
43
0
#define MAX_VARIANT_DIGITS         16
44
0
#define MAX_OUTHDR_NAME            256
45
0
#define AAL_HEADER_CHARS           12
46
47
/* 's' or 'r' of a packet as read from .out file */
48
typedef enum packet_direction_t
49
{
50
    sent,
51
    received
52
} packet_direction_t;
53
54
55
/***********************************************************************/
56
/* For each line, store (in case we need to dump):                     */
57
/* - String before time field                                          */
58
/* - Whether or not " l " appears after timestamp                      */
59
typedef struct
60
{
61
    char *before_time;
62
    bool has_l;
63
} line_prefix_info_t;
64
65
66
/*******************************************************************/
67
/* Information stored external to a file (wtap) needed for reading and dumping */
68
typedef struct dct2000_file_externals
69
{
70
    /* Remember the time at the start of capture */
71
    time_t  start_secs;
72
    uint32_t start_usecs;
73
74
    /*
75
     * The following information is needed only for dumping.
76
     *
77
     * XXX - Wiretap is not *supposed* to require that a packet being
78
     * dumped come from a file of the same type that you currently have
79
     * open; this should be fixed.
80
     */
81
82
    /* Buffer to hold first line, including magic and format number */
83
    char firstline[MAX_FIRST_LINE_LENGTH];
84
    int  firstline_length;
85
86
    /* Buffer to hold second line with formatted file creation data/time */
87
    char secondline[MAX_TIMESTAMP_LINE_LENGTH];
88
    int  secondline_length;
89
90
    /* Hash table to store text prefix data part of displayed packets.
91
       Records (file offset -> line_prefix_info_t)
92
    */
93
    GHashTable *packet_prefix_table;
94
    char linebuff[MAX_LINE_LENGTH + 1];
95
} dct2000_file_externals_t;
96
97
/* 'Magic number' at start of Catapult DCT2000 .out files. */
98
static const char catapult_dct2000_magic[] = "Session Transcript";
99
100
/************************************************************/
101
/* Functions called from wiretap core                       */
102
static bool catapult_dct2000_read(wtap *wth, wtap_rec *rec,
103
                                  int *err, char **err_info,
104
                                  int64_t *data_offset);
105
static bool catapult_dct2000_seek_read(wtap *wth, int64_t seek_off,
106
                                       wtap_rec *rec,
107
                                       int *err, char **err_info);
108
static void catapult_dct2000_close(wtap *wth);
109
110
static bool catapult_dct2000_dump(wtap_dumper *wdh, const wtap_rec *rec,
111
                                  int *err, char **err_info);
112
113
114
/************************************************************/
115
/* Private helper functions                                 */
116
static bool read_new_line(FILE_T fh, int *length,
117
                              char *buf, size_t bufsize, int *err,
118
                              char **err_info);
119
static bool parse_line(char *linebuff, int line_length,
120
                           int *seconds, int *useconds,
121
                           long *before_time_offset, long *after_time_offset,
122
                           long *data_offset,
123
                           int *data_chars,
124
                           packet_direction_t *direction,
125
                           int *encap, bool *is_comment, bool *is_sprint,
126
                           char *aal_header_chars,
127
                           char *context_name, uint8_t *context_portp,
128
                           char *protocol_name, char *variant_name,
129
                           char *outhdr_name);
130
static bool process_parsed_line(wtap *wth,
131
                                    const dct2000_file_externals_t *file_externals,
132
                                    wtap_rec *rec,
133
                                    int64_t file_offset, long dollar_offset,
134
                                    int seconds, int useconds,
135
                                    char *timestamp_string,
136
                                    packet_direction_t direction, int encap,
137
                                    char *context_name, uint8_t context_port,
138
                                    char *protocol_name, char *variant_name,
139
                                    char *outhdr_name, char *aal_header_chars,
140
                                    bool is_comment, int data_chars,
141
                                    int *err, char **err_info);
142
static uint8_t hex_from_char(char c);
143
static void   prepare_hex_byte_from_chars_table(void);
144
static uint8_t hex_byte_from_chars(const char *c);
145
static char char_from_hex(uint8_t hex);
146
147
static void set_aal_info(union wtap_pseudo_header *pseudo_header,
148
                         packet_direction_t direction,
149
                         char *aal_header_chars);
150
static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
151
                          packet_direction_t direction);
152
static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
153
                         packet_direction_t direction);
154
155
static int packet_offset_equal(const void *v, const void *v2);
156
static unsigned packet_offset_hash_func(const void *v);
157
158
static bool get_file_time_stamp(const char *linebuff, time_t *secs, uint32_t *usecs);
159
static gboolean free_line_prefix_info(void *key, void *value, void *user_data);
160
161
static int dct2000_file_type_subtype = -1;
162
163
void register_dct2000(void);
164
165
166
/********************************************/
167
/* Open file (for reading)                 */
168
/********************************************/
169
wtap_open_return_val
170
catapult_dct2000_open(wtap *wth, int *err, char **err_info)
171
0
{
172
0
    time_t  timestamp;
173
0
    uint32_t usecs;
174
0
    int firstline_length = 0;
175
0
    dct2000_file_externals_t *file_externals;
176
0
    char linebuff[MAX(MAX_FIRST_LINE_LENGTH, MAX_TIMESTAMP_LINE_LENGTH) + 1];
177
0
    static bool hex_byte_table_values_set = false;
178
179
    /* Clear errno before reading from the file */
180
0
    errno = 0;
181
182
183
    /********************************************************************/
184
    /* First line needs to contain at least as many characters as magic */
185
186
0
    if (!read_new_line(wth->fh, &firstline_length, linebuff,
187
0
                       sizeof linebuff, err, err_info)) {
188
0
        if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
189
0
            return WTAP_OPEN_ERROR;
190
0
        }
191
0
        else {
192
0
            return WTAP_OPEN_NOT_MINE;
193
0
        }
194
0
    }
195
0
    if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
196
0
        firstline_length >= MAX_FIRST_LINE_LENGTH) {
197
198
0
        return WTAP_OPEN_NOT_MINE;
199
0
    }
200
201
    /* This file is not for us if it doesn't match our signature */
202
0
    if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
203
0
        return WTAP_OPEN_NOT_MINE;
204
0
    }
205
206
    /* Make sure table is ready for use */
207
0
    if (!hex_byte_table_values_set) {
208
0
        prepare_hex_byte_from_chars_table();
209
0
        hex_byte_table_values_set = true;
210
0
    }
211
212
    /*********************************************************************/
213
    /* Need entry in file_externals table                                */
214
215
    /* Allocate a new file_externals structure for this file */
216
0
    file_externals = g_new0(dct2000_file_externals_t, 1);
217
218
    /* Copy this first line into buffer so could write out later */
219
0
    (void) g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
220
0
    file_externals->firstline_length = firstline_length;
221
222
223
    /***********************************************************/
224
    /* Second line contains file timestamp                     */
225
    /* Store this offset in file_externals                     */
226
227
0
    if (!read_new_line(wth->fh, &(file_externals->secondline_length),
228
0
                       linebuff, sizeof linebuff, err, err_info)) {
229
0
        g_free(file_externals);
230
0
        if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
231
0
            return WTAP_OPEN_ERROR;
232
0
        }
233
0
        else {
234
0
            return WTAP_OPEN_NOT_MINE;
235
0
        }
236
0
    }
237
0
    if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
238
0
        (!get_file_time_stamp(linebuff, &timestamp, &usecs))) {
239
240
        /* Give up if file time line wasn't valid */
241
0
        g_free(file_externals);
242
0
        return WTAP_OPEN_NOT_MINE;
243
0
    }
244
245
    /* Fill in timestamp */
246
0
    file_externals->start_secs = timestamp;
247
0
    file_externals->start_usecs = usecs;
248
249
    /* Copy this second line into buffer so could write out later */
250
0
    (void) g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
251
252
253
    /************************************************************/
254
    /* File is for us. Fill in details so packets can be read   */
255
256
    /* Set our file type */
257
0
    wth->file_type_subtype = dct2000_file_type_subtype;
258
259
    /* Use our own encapsulation to send all packets to our stub dissector */
260
0
    wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
261
262
    /* Callbacks for reading operations */
263
0
    wth->subtype_read = catapult_dct2000_read;
264
0
    wth->subtype_seek_read = catapult_dct2000_seek_read;
265
0
    wth->subtype_close = catapult_dct2000_close;
266
267
    /* Choose microseconds (have 4 decimal places...) */
268
0
    wth->file_tsprec = WTAP_TSPREC_USEC;
269
270
271
    /***************************************************************/
272
    /* Initialise packet_prefix_table (index is offset into file)  */
273
0
    file_externals->packet_prefix_table =
274
0
        g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
275
276
    /* Set this wtap to point to the file_externals */
277
0
    wth->priv = (void*)file_externals;
278
279
0
    *err = errno;
280
281
    /*
282
     * Add an IDB; we don't know how many interfaces were
283
     * involved, so we just say one interface, about which
284
     * we only know the link-layer type, snapshot length,
285
     * and time stamp resolution.
286
     */
287
0
    wtap_add_generated_idb(wth);
288
289
0
    return WTAP_OPEN_MINE;
290
0
}
291
292
/* Ugly, but much faster than using snprintf! */
293
static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
294
0
{
295
0
    int idx = 0;
296
297
    /* Secs */
298
0
    if (secs < 10) {
299
0
        timestamp_string[idx++] = ((secs % 10))           + '0';
300
0
    }
301
0
    else if (secs < 100) {
302
0
        timestamp_string[idx++] = ( secs          / 10)   + '0';
303
0
        timestamp_string[idx++] = ((secs % 10))           + '0';
304
0
    }
305
0
    else if (secs < 1000) {
306
0
        timestamp_string[idx++] = ((secs)         / 100)   + '0';
307
0
        timestamp_string[idx++] = ((secs % 100))  / 10     + '0';
308
0
        timestamp_string[idx++] = ((secs % 10))            + '0';
309
0
    }
310
0
    else if (secs < 10000) {
311
0
        timestamp_string[idx++] = ((secs)          / 1000)   + '0';
312
0
        timestamp_string[idx++] = ((secs % 1000))  / 100     + '0';
313
0
        timestamp_string[idx++] = ((secs % 100))   / 10      + '0';
314
0
        timestamp_string[idx++] = ((secs % 10))              + '0';
315
0
    }
316
0
    else if (secs < 100000) {
317
0
        timestamp_string[idx++] = ((secs)          / 10000)   + '0';
318
0
        timestamp_string[idx++] = ((secs % 10000)) / 1000     + '0';
319
0
        timestamp_string[idx++] = ((secs % 1000))  / 100      + '0';
320
0
        timestamp_string[idx++] = ((secs % 100))   / 10       + '0';
321
0
        timestamp_string[idx++] = ((secs % 10))               + '0';
322
0
    }
323
0
    else if (secs < 1000000) {
324
0
        timestamp_string[idx++] = ((secs)           / 100000) + '0';
325
0
        timestamp_string[idx++] = ((secs % 100000)) / 10000   + '0';
326
0
        timestamp_string[idx++] = ((secs % 10000))  / 1000    + '0';
327
0
        timestamp_string[idx++] = ((secs % 1000))   / 100     + '0';
328
0
        timestamp_string[idx++] = ((secs % 100))    / 10      + '0';
329
0
        timestamp_string[idx++] = ((secs % 10))               + '0';
330
0
    }
331
0
    else {
332
0
        snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
333
0
        return;
334
0
    }
335
336
0
    timestamp_string[idx++] = '.';
337
0
    timestamp_string[idx++] = ( tenthousandths          / 1000) + '0';
338
0
    timestamp_string[idx++] = ((tenthousandths % 1000)  / 100)  + '0';
339
0
    timestamp_string[idx++] = ((tenthousandths % 100)   / 10)   + '0';
340
0
    timestamp_string[idx++] = ((tenthousandths % 10))           + '0';
341
0
    timestamp_string[idx]   = '\0';
342
0
}
343
344
/**************************************************/
345
/* Read packet function.                          */
346
/* Look for and read the next usable packet       */
347
/* - return true and details if found             */
348
/**************************************************/
349
static bool
350
catapult_dct2000_read(wtap *wth, wtap_rec *rec,
351
                      int *err, char **err_info, int64_t *data_offset)
352
0
{
353
0
    long dollar_offset, before_time_offset, after_time_offset;
354
0
    packet_direction_t direction;
355
0
    int encap;
356
357
    /* Get wtap external structure for this wtap */
358
0
    dct2000_file_externals_t *file_externals =
359
0
        (dct2000_file_externals_t*)wth->priv;
360
361
    /* Search for a line containing a usable packet */
362
0
    while (1) {
363
0
        int line_length, seconds, useconds, data_chars;
364
0
        bool is_comment = false;
365
0
        bool is_sprint = false;
366
0
        int64_t this_offset;
367
0
        char aal_header_chars[AAL_HEADER_CHARS];
368
0
        char context_name[MAX_CONTEXT_NAME];
369
0
        uint8_t context_port = 0;
370
0
        char protocol_name[MAX_PROTOCOL_NAME+1];
371
0
        char variant_name[MAX_VARIANT_DIGITS+1];
372
0
        char outhdr_name[MAX_OUTHDR_NAME+1];
373
374
        /* Get starting offset of the line we're about to read */
375
0
        this_offset = file_tell(wth->fh);
376
377
        /* Read a new line from file into linebuff */
378
0
        if (!read_new_line(wth->fh, &line_length, file_externals->linebuff,
379
0
                           sizeof file_externals->linebuff, err, err_info)) {
380
0
            if (*err != 0) {
381
0
                return false;  /* error */
382
0
            }
383
            /* No more lines can be read, so quit. */
384
0
            break;
385
0
        }
386
387
        /* Try to parse the line as a frame record */
388
0
        if (parse_line(file_externals->linebuff, line_length, &seconds, &useconds,
389
0
                       &before_time_offset, &after_time_offset,
390
0
                       &dollar_offset,
391
0
                       &data_chars, &direction, &encap, &is_comment, &is_sprint,
392
0
                       aal_header_chars,
393
0
                       context_name, &context_port,
394
0
                       protocol_name, variant_name, outhdr_name)) {
395
0
            line_prefix_info_t *line_prefix_info;
396
0
            int64_t *pkey = NULL;
397
0
            char timestamp_string[MAX_TIMESTAMP_LEN+1];
398
0
            write_timestamp_string(timestamp_string, seconds, useconds/100);
399
400
            /* Set data_offset to the beginning of the line we're returning.
401
               This will be the seek_off parameter when this frame is re-read.
402
            */
403
0
            *data_offset = this_offset;
404
405
0
            if (!process_parsed_line(wth, file_externals,
406
0
                                     rec, this_offset,
407
0
                                     dollar_offset,
408
0
                                     seconds, useconds,
409
0
                                     timestamp_string,
410
0
                                     direction, encap,
411
0
                                     context_name, context_port,
412
0
                                     protocol_name, variant_name,
413
0
                                     outhdr_name, aal_header_chars,
414
0
                                     is_comment, data_chars,
415
0
                                     err, err_info))
416
0
                return false;
417
418
            /* Store the packet prefix in the hash table */
419
0
            line_prefix_info = g_new(line_prefix_info_t,1);
420
421
            /* Create and use buffer for contents before time */
422
0
            line_prefix_info->before_time = (char *)g_malloc(before_time_offset+1);
423
0
            memcpy(line_prefix_info->before_time, file_externals->linebuff, before_time_offset);
424
0
            line_prefix_info->before_time[before_time_offset] = '\0';
425
426
            /* There is usually a ' l ' between the timestamp and the data.  Set flag to record this. */
427
0
            line_prefix_info->has_l =  ((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
428
0
                                        (strncmp(file_externals->linebuff+after_time_offset, " l ", 3) == 0);
429
430
            /* Add packet entry into table */
431
0
            pkey = (int64_t *)g_malloc(sizeof(*pkey));
432
0
            *pkey = this_offset;
433
0
            g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
434
435
            /* OK, we have packet details to return */
436
0
            return true;
437
0
        }
438
0
    }
439
440
    /* No packet details to return... */
441
0
    return false;
442
0
}
443
444
445
/**************************************************/
446
/* Read & seek function.                          */
447
/**************************************************/
448
static bool
449
catapult_dct2000_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
450
                           int *err, char **err_info)
451
0
{
452
0
    int length;
453
0
    long dollar_offset, before_time_offset, after_time_offset;
454
0
    char aal_header_chars[AAL_HEADER_CHARS];
455
0
    char context_name[MAX_CONTEXT_NAME];
456
0
    uint8_t context_port = 0;
457
0
    char protocol_name[MAX_PROTOCOL_NAME+1];
458
0
    char variant_name[MAX_VARIANT_DIGITS+1];
459
0
    char outhdr_name[MAX_OUTHDR_NAME+1];
460
0
    bool is_comment = false;
461
0
    bool is_sprint = false;
462
0
    packet_direction_t direction;
463
0
    int encap;
464
0
    int seconds, useconds, data_chars;
465
466
    /* Get wtap external structure for this wtap */
467
0
    dct2000_file_externals_t *file_externals =
468
0
        (dct2000_file_externals_t*)wth->priv;
469
470
    /* Reset errno */
471
0
    *err = errno = 0;
472
473
    /* Seek to beginning of packet */
474
0
    if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
475
0
        return false;
476
0
    }
477
478
    /* Re-read whole line (this really should succeed) */
479
0
    if (!read_new_line(wth->random_fh, &length, file_externals->linebuff,
480
0
                      sizeof file_externals->linebuff, err, err_info)) {
481
0
        return false;
482
0
    }
483
484
    /* Try to parse this line again (should succeed as re-reading...) */
485
0
    if (parse_line(file_externals->linebuff, length, &seconds, &useconds,
486
0
                   &before_time_offset, &after_time_offset,
487
0
                   &dollar_offset,
488
0
                   &data_chars, &direction, &encap, &is_comment, &is_sprint,
489
0
                   aal_header_chars,
490
0
                   context_name, &context_port,
491
0
                   protocol_name, variant_name, outhdr_name)) {
492
493
0
        char timestamp_string[MAX_TIMESTAMP_LEN+1];
494
0
        write_timestamp_string(timestamp_string, seconds, useconds/100);
495
496
0
        if (!process_parsed_line(wth, file_externals,
497
0
                                 rec, seek_off,
498
0
                                 dollar_offset,
499
0
                                 seconds, useconds,
500
0
                                 timestamp_string,
501
0
                                 direction, encap,
502
0
                                 context_name, context_port,
503
0
                                 protocol_name, variant_name,
504
0
                                 outhdr_name, aal_header_chars,
505
0
                                 is_comment, data_chars,
506
0
                                 err, err_info)) {
507
0
            return false;
508
0
        }
509
510
0
        *err = errno = 0;
511
0
        return true;
512
0
    }
513
514
    /* If get here, must have failed */
515
0
    *err = errno;
516
0
    *err_info = ws_strdup_printf("catapult dct2000: seek_read failed to read/parse "
517
0
                                "line at position %" PRId64,
518
0
                                seek_off);
519
0
    return false;
520
0
}
521
522
523
/***************************************************************************/
524
/* Free dct2000-specific capture info from file that was open for reading  */
525
/***************************************************************************/
526
static void
527
catapult_dct2000_close(wtap *wth)
528
0
{
529
    /* Get externals for this file */
530
0
    dct2000_file_externals_t *file_externals =
531
0
        (dct2000_file_externals_t*)wth->priv;
532
533
    /* Free up its line prefix values */
534
0
    g_hash_table_foreach_remove(file_externals->packet_prefix_table,
535
0
                                free_line_prefix_info, NULL);
536
    /* Free up its line prefix table */
537
0
    g_hash_table_destroy(file_externals->packet_prefix_table);
538
0
}
539
540
541
542
543
/***************************/
544
/* Dump functions          */
545
/***************************/
546
547
typedef struct {
548
    bool       first_packet_written;
549
    nstime_t   start_time;
550
} dct2000_dump_t;
551
552
/*****************************************************/
553
/* The file that we are writing to has been opened.  */
554
/* Set other dump callbacks.                         */
555
/*****************************************************/
556
static bool
557
catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_)
558
0
{
559
    /* Fill in other dump callbacks */
560
0
    wdh->subtype_write = catapult_dct2000_dump;
561
562
0
    return true;
563
0
}
564
565
/*********************************************************/
566
/* Respond to queries about which encap types we support */
567
/* writing to.                                           */
568
/*********************************************************/
569
static int
570
catapult_dct2000_dump_can_write_encap(int encap)
571
0
{
572
0
    switch (encap) {
573
0
        case WTAP_ENCAP_CATAPULT_DCT2000:
574
            /* We support this */
575
0
            return 0;
576
577
0
        default:
578
            /* But can't write to any other formats... */
579
0
            return WTAP_ERR_UNWRITABLE_ENCAP;
580
0
    }
581
0
}
582
583
584
/*****************************************/
585
/* Write a single packet out to the file */
586
/*****************************************/
587
588
static bool
589
catapult_dct2000_dump(wtap_dumper *wdh, const wtap_rec *rec,
590
                      int *err, char **err_info _U_)
591
0
{
592
0
    const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
593
0
    uint32_t n;
594
0
    line_prefix_info_t *prefix = NULL;
595
0
    char time_string[MAX_TIMESTAMP_LEN];
596
0
    bool is_comment;
597
0
    bool is_sprint = false;
598
0
    dct2000_dump_t *dct2000;
599
0
    int consecutive_slashes=0;
600
0
    char *p_c;
601
602
    /******************************************************/
603
    /* Get the file_externals structure for this file */
604
    /* Find wtap external structure for this wtap */
605
0
    dct2000_file_externals_t *file_externals =
606
0
        (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
607
608
    /* We can only write packet records. */
609
0
    if (rec->rec_type != REC_TYPE_PACKET) {
610
0
        *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
611
0
        *err_info = wtap_unwritable_rec_type_err_string(rec);
612
0
        return false;
613
0
    }
614
615
    /*
616
     * Make sure this packet doesn't have a link-layer type that
617
     * differs from the one for the file (which should always
618
     * be WTAP_ENCAP_CATAPULT_DCT2000).
619
     */
620
0
    if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
621
0
        *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
622
0
        return false;
623
0
    }
624
625
0
    dct2000 = (dct2000_dump_t *)wdh->priv;
626
0
    if (dct2000 == NULL) {
627
628
        /* Write out saved first line */
629
0
        if (!wtap_dump_file_write(wdh, file_externals->firstline,
630
0
                                  file_externals->firstline_length, err)) {
631
0
            return false;
632
0
        }
633
0
        if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
634
0
            return false;
635
0
        }
636
637
        /* Also write out saved second line with timestamp corresponding to the
638
           opening time of the log.
639
        */
640
0
        if (!wtap_dump_file_write(wdh, file_externals->secondline,
641
0
                                  file_externals->secondline_length, err)) {
642
0
            return false;
643
0
        }
644
0
        if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
645
0
            return false;
646
0
        }
647
648
        /* Allocate the dct2000-specific dump structure */
649
0
        dct2000 = g_new(dct2000_dump_t, 1);
650
0
        wdh->priv = (void *)dct2000;
651
652
        /* Copy time of beginning of file */
653
0
        dct2000->start_time.secs = file_externals->start_secs;
654
0
        dct2000->start_time.nsecs =
655
0
            (file_externals->start_usecs * 1000);
656
657
        /* Set flag so don't write header out again */
658
0
        dct2000->first_packet_written = true;
659
0
    }
660
661
662
    /******************************************************************/
663
    /* Write out this packet's prefix, including calculated timestamp */
664
665
    /* Look up line data prefix using stored offset */
666
0
    prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
667
0
                                                      (const void*)&(pseudo_header->dct2000.seek_off));
668
669
    /* Write out text before timestamp */
670
0
    if (!wtap_dump_file_write(wdh, prefix->before_time,
671
0
                              strlen(prefix->before_time), err)) {
672
0
        return false;
673
0
    }
674
675
    /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
676
    /* This is much faster than strstr() for "/////" */
677
0
    p_c = prefix->before_time;
678
0
    while (p_c && (*p_c != '/')) {
679
0
        p_c++;
680
0
    }
681
0
    while (p_c && (*p_c == '/')) {
682
0
        consecutive_slashes++;
683
0
        p_c++;
684
0
    }
685
0
    is_comment = (consecutive_slashes == 5);
686
687
    /* Calculate time of this packet to write, relative to start of dump */
688
0
    if (rec->ts.nsecs >= dct2000->start_time.nsecs) {
689
0
        write_timestamp_string(time_string,
690
0
                               (int)(rec->ts.secs - dct2000->start_time.secs),
691
0
                               (rec->ts.nsecs - dct2000->start_time.nsecs) / 100000);
692
0
    }
693
0
    else {
694
0
        write_timestamp_string(time_string,
695
0
                               (int)(rec->ts.secs - dct2000->start_time.secs-1),
696
0
                               ((1000000000 + (rec->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
697
0
    }
698
699
    /* Write out the calculated timestamp */
700
0
    if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
701
0
        return false;
702
0
    }
703
704
    /* Write out text between timestamp and start of hex data */
705
0
    if (prefix->has_l) {
706
0
        if (!wtap_dump_file_write(wdh, " l ", 3, err)) {
707
0
            return false;
708
0
        }
709
0
    }
710
711
0
    const uint8_t *pd = ws_buffer_start_ptr(&rec->data);
712
713
    /****************************************************************/
714
    /* Need to skip stub header at start of pd before we reach data */
715
716
    /* Context name */
717
0
    for (n=0; pd[n] != '\0'; n++);
718
0
    n++;
719
720
    /* Context port number */
721
0
    n++;
722
723
    /* Timestamp */
724
0
    for (; pd[n] != '\0'; n++);
725
0
    n++;
726
727
    /* Protocol name */
728
0
    if (is_comment) {
729
0
        is_sprint = (strcmp((const char *)pd+n, "sprint") == 0);
730
0
    }
731
0
    for (; pd[n] != '\0'; n++);
732
0
    n++;
733
734
    /* Variant number (as string) */
735
0
    for (; pd[n] != '\0'; n++);
736
0
    n++;
737
738
    /* Outhdr (as string) */
739
0
    for (; pd[n] != '\0'; n++);
740
0
    n++;
741
742
    /* Direction & encap */
743
0
    n += 2;
744
745
746
    /**************************************/
747
    /* Remainder is encapsulated protocol */
748
0
    if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
749
0
        return false;
750
0
    }
751
752
0
    if (!is_comment) {
753
        /* Each binary byte is written out as 2 hex string chars */
754
0
        for (; n < rec->rec_header.packet_header.len; n++) {
755
0
            char c[2];
756
0
            c[0] = char_from_hex((uint8_t)(pd[n] >> 4));
757
0
            c[1] = char_from_hex((uint8_t)(pd[n] & 0x0f));
758
759
            /* Write both hex chars of byte together */
760
0
            if (!wtap_dump_file_write(wdh, c, 2, err)) {
761
0
                return false;
762
0
            }
763
0
        }
764
0
    }
765
0
    else {
766
        /* Comment */
767
0
        if (!wtap_dump_file_write(wdh, pd+n, rec->rec_header.packet_header.len-n, err)) {
768
0
            return false;
769
0
        }
770
0
    }
771
772
    /* End the line */
773
0
    if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
774
0
        return false;
775
0
    }
776
777
0
    return true;
778
0
}
779
780
781
/****************************/
782
/* Private helper functions */
783
/****************************/
784
785
/**********************************************************************/
786
/* Read a new line from the file, starting at offset.                 */
787
/* - writes data to its argument linebuff                             */
788
/* - on return 'offset' will point to the next position to read from  */
789
/* - return true if this read is successful                           */
790
/**********************************************************************/
791
static bool
792
read_new_line(FILE_T fh, int *length,
793
              char *linebuff, size_t linebuffsize, int *err, char **err_info)
794
0
{
795
    /* Read in a line */
796
0
    int64_t pos_before = file_tell(fh);
797
798
0
    if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
799
        /* No characters found, or error */
800
0
        *err = file_error(fh, err_info);
801
0
        return false;
802
0
    }
803
804
    /* Set length (avoiding strlen()) and offset.. */
805
0
    *length = (int)(file_tell(fh) - pos_before);
806
807
    /* ...but don't want to include newline in line length */
808
0
    if (*length > 0 && linebuff[*length-1] == '\n') {
809
0
        linebuff[*length-1] = '\0';
810
0
        *length = *length - 1;
811
0
    }
812
    /* Nor do we want '\r' (as will be written when log is created on windows) */
813
0
    if (*length > 0 && linebuff[*length-1] == '\r') {
814
0
        linebuff[*length-1] = '\0';
815
0
        *length = *length - 1;
816
0
    }
817
818
0
    return true;
819
0
}
820
821
822
/**********************************************************************/
823
/* Parse a line from buffer, by identifying:                          */
824
/* - context, port and direction of packet                            */
825
/* - timestamp                                                        */
826
/* - data position and length                                         */
827
/* Return true if this packet looks valid and can be displayed        */
828
/**********************************************************************/
829
static bool
830
parse_line(char *linebuff, int line_length,
831
           int *seconds, int *useconds,
832
           long *before_time_offset, long *after_time_offset,
833
           long *data_offset, int *data_chars,
834
           packet_direction_t *direction,
835
           int *encap, bool *is_comment, bool *is_sprint,
836
           char *aal_header_chars,
837
           char *context_name, uint8_t *context_portp,
838
           char *protocol_name, char *variant_name,
839
           char *outhdr_name)
840
0
{
841
0
    int  n = 0;
842
0
    int  port_digits;
843
0
    char port_number_string[MAX_PORT_DIGITS+1];
844
0
    int  variant_digits;
845
0
    int  variant = 1;
846
0
    int  protocol_chars;
847
0
    int  outhdr_chars;
848
849
0
    char seconds_buff[MAX_SECONDS_CHARS+1];
850
0
    int  seconds_chars;
851
0
    char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
852
0
    int  subsecond_decimals_chars;
853
0
    bool skip_first_byte = false;
854
0
    bool atm_header_present = false;
855
856
0
    *is_comment = false;
857
0
    *is_sprint = false;
858
859
    /* Read context name until find '.' */
860
0
    for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
861
0
        if (linebuff[n] == '/') {
862
0
            context_name[n] = '\0';
863
864
            /* If not a comment (/////), not a valid line */
865
0
            if (strncmp(linebuff+n, "/////", 5) != 0) {
866
0
                return false;
867
0
            }
868
869
            /* There is no variant, outhdr, etc.  Set protocol to be a comment */
870
0
            (void) g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
871
0
            *is_comment = true;
872
0
            break;
873
0
        }
874
0
        if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
875
0
            return false;
876
0
        }
877
0
        context_name[n] = linebuff[n];
878
0
    }
879
0
    if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
880
0
        return false;
881
0
    }
882
883
    /* Reset strings (that won't be set by comments) */
884
0
    variant_name[0] = '\0';
885
0
    outhdr_name[0] = '\0';
886
0
    port_number_string[0] = '\0';
887
888
0
    if (!(*is_comment)) {
889
        /* '.' must follow context name */
890
0
        if (linebuff[n] != '.') {
891
0
            return false;
892
0
        }
893
0
        context_name[n] = '\0';
894
        /* Skip it */
895
0
        n++;
896
897
        /* Now read port number */
898
0
        for (port_digits = 0;
899
0
             (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
900
0
             n++, port_digits++) {
901
902
0
            if (!g_ascii_isdigit(linebuff[n])) {
903
0
                return false;
904
0
            }
905
0
            port_number_string[port_digits] = linebuff[n];
906
0
        }
907
0
        if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
908
0
            return false;
909
0
        }
910
911
        /* Slash char must follow port number */
912
0
        if (linebuff[n] != '/')
913
0
        {
914
0
            return false;
915
0
        }
916
0
        port_number_string[port_digits] = '\0';
917
0
        if (port_digits == 1) {
918
0
            *context_portp = port_number_string[0] - '0';
919
0
        }
920
0
        else {
921
            /* Everything in here is a digit, so we don't need to check
922
               whether what follows the number is anything other than
923
               a '\0'. */
924
0
            if (!ws_strtou8(port_number_string, NULL, context_portp)) {
925
0
              return false;
926
0
            }
927
0
        }
928
        /* Skip it */
929
0
        n++;
930
931
        /* Now for the protocol name */
932
0
        for (protocol_chars = 0;
933
0
             (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
934
0
             n++, protocol_chars++) {
935
936
0
            if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '.')) {
937
0
                return false;
938
0
            }
939
0
            protocol_name[protocol_chars] = linebuff[n];
940
0
        }
941
0
        if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
942
            /* If doesn't fit, fail rather than truncate */
943
0
            return false;
944
0
        }
945
0
        protocol_name[protocol_chars] = '\0';
946
947
        /* Slash char must follow protocol name */
948
0
        if (linebuff[n] != '/') {
949
0
            return false;
950
0
        }
951
        /* Skip it */
952
0
        n++;
953
954
955
        /* Following the / is the variant number.  No digits indicate 1 */
956
0
        for (variant_digits = 0;
957
0
             (g_ascii_isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
958
0
             n++, variant_digits++) {
959
960
0
            if (!g_ascii_isdigit(linebuff[n])) {
961
0
                return false;
962
0
            }
963
0
            variant_name[variant_digits] = linebuff[n];
964
0
        }
965
0
        if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
966
0
            return false;
967
0
        }
968
969
0
        if (variant_digits > 0) {
970
0
            variant_name[variant_digits] = '\0';
971
0
            if (variant_digits == 1) {
972
0
                variant = variant_name[0] - '0';
973
0
            }
974
0
            else {
975
0
                if (!ws_strtoi32(variant_name, NULL, &variant)) {
976
0
                  return false;
977
0
                }
978
0
            }
979
0
        }
980
0
        else {
981
0
            variant_name[0] = '1';
982
0
            variant_name[1] = '\0';
983
0
        }
984
985
986
        /* Outheader values may follow */
987
0
        if (linebuff[n] == ',') {
988
            /* Skip , */
989
0
            n++;
990
991
0
            for (outhdr_chars = 0;
992
0
                 (g_ascii_isdigit(linebuff[n]) || linebuff[n] == ',') &&
993
0
                 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
994
0
                 n++, outhdr_chars++) {
995
996
0
                if (!g_ascii_isdigit(linebuff[n]) && (linebuff[n] != ',')) {
997
0
                    return false;
998
0
                }
999
0
                outhdr_name[outhdr_chars] = linebuff[n];
1000
0
            }
1001
0
            if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
1002
0
                return false;
1003
0
            }
1004
            /* Terminate (possibly empty) string */
1005
0
            outhdr_name[outhdr_chars] = '\0';
1006
0
        }
1007
0
    }
1008
1009
1010
    /******************************************************************/
1011
    /* Now check whether we know how to use a packet of this protocol */
1012
1013
0
    if ((strcmp(protocol_name, "ip") == 0) ||
1014
0
        (strcmp(protocol_name, "sctp") == 0) ||
1015
0
        (strcmp(protocol_name, "gre") == 0) ||
1016
0
        (strcmp(protocol_name, "mipv6") == 0) ||
1017
0
        (strcmp(protocol_name, "igmp") == 0)) {
1018
1019
0
        *encap = WTAP_ENCAP_RAW_IP;
1020
0
    }
1021
1022
    /* FP may be carried over ATM, which has separate atm header to parse */
1023
0
    else
1024
0
    if ((strcmp(protocol_name, "fp") == 0) ||
1025
0
        (strncmp(protocol_name, "fp_r", 4) == 0)) {
1026
1027
0
        if ((variant > 256) && (variant % 256 == 3)) {
1028
            /* FP over udp is contained in IPPrim... */
1029
0
            *encap = 0;
1030
0
        }
1031
0
        else {
1032
            /* FP over AAL0 or AAL2 */
1033
0
            *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1034
0
            atm_header_present = true;
1035
0
        }
1036
0
    }
1037
0
    else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1038
        /* FP (IuR) over AAL2 */
1039
0
        *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1040
0
        atm_header_present = true;
1041
0
    }
1042
1043
0
    else
1044
0
    if (strcmp(protocol_name, "ppp") == 0) {
1045
0
        *encap = WTAP_ENCAP_PPP;
1046
0
    }
1047
0
    else
1048
0
    if (strcmp(protocol_name, "isdn_l3") == 0) {
1049
       /* TODO: find out what this byte means... */
1050
0
        skip_first_byte = true;
1051
0
        *encap = WTAP_ENCAP_ISDN;
1052
0
    }
1053
0
    else
1054
0
    if (strcmp(protocol_name, "isdn_l2") == 0) {
1055
0
        *encap = WTAP_ENCAP_ISDN;
1056
0
    }
1057
0
    else
1058
0
    if (strcmp(protocol_name, "ethernet") == 0) {
1059
0
        *encap = WTAP_ENCAP_ETHERNET;
1060
0
    }
1061
0
    else
1062
0
    if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1063
0
        (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1064
1065
0
        *encap = DCT2000_ENCAP_SSCOP;
1066
0
    }
1067
0
    else
1068
0
    if (strcmp(protocol_name, "frelay_l2") == 0) {
1069
0
        *encap = WTAP_ENCAP_FRELAY;
1070
0
    }
1071
0
    else
1072
0
    if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1073
0
        *encap = DCT2000_ENCAP_MTP2;
1074
0
    }
1075
0
    else
1076
0
    if ((strcmp(protocol_name, "nbap") == 0) ||
1077
0
        (strcmp(protocol_name, "nbap_r4") == 0) ||
1078
0
        (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1079
1080
        /* The entire message in these cases is nbap, so use an encap value. */
1081
0
        *encap = DCT2000_ENCAP_NBAP;
1082
0
    }
1083
0
    else {
1084
        /* Not a supported board port protocol/encap, but can show as raw data or
1085
           in some cases find protocol embedded inside primitive */
1086
0
        *encap = DCT2000_ENCAP_UNHANDLED;
1087
0
    }
1088
1089
1090
    /* Find separate ATM header if necessary */
1091
0
    if (atm_header_present) {
1092
0
        int header_chars_seen = 0;
1093
1094
        /* Scan ahead to the next $ */
1095
0
        for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1096
        /* Skip it */
1097
0
        n++;
1098
0
        if (n+1 >= line_length) {
1099
0
            return false;
1100
0
        }
1101
1102
        /* Read consecutive hex chars into atm header buffer */
1103
0
        for (;
1104
0
             ((n < line_length) &&
1105
0
              (linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1106
0
              (header_chars_seen < AAL_HEADER_CHARS));
1107
0
             n++, header_chars_seen++) {
1108
1109
0
            aal_header_chars[header_chars_seen] = linebuff[n];
1110
            /* Next 6 characters after '9' are mapped to a->f */
1111
0
            if (!g_ascii_isdigit(linebuff[n])) {
1112
0
                aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1113
0
            }
1114
0
        }
1115
1116
0
        if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1117
0
            return false;
1118
0
        }
1119
0
    }
1120
1121
    /* Skip next '/' */
1122
0
    n++;
1123
1124
    /* If there is a number, skip all info to next '/'.
1125
       TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
1126
       and display dct2000 dissector... */
1127
0
    if (g_ascii_isdigit(linebuff[n])) {
1128
0
        while ((n+1 < line_length) && linebuff[n] != '/') {
1129
0
            n++;
1130
0
        }
1131
0
    }
1132
1133
    /* Skip '/' */
1134
0
    while ((n+1 < line_length) && linebuff[n] == '/') {
1135
0
        n++;
1136
0
    }
1137
1138
    /* Skip a space that may happen here */
1139
0
    if ((n+1 < line_length) && linebuff[n] == ' ') {
1140
0
        n++;
1141
0
    }
1142
1143
    /* Next character gives direction of message (must be 's' or 'r') */
1144
0
    if (!(*is_comment)) {
1145
0
        if (linebuff[n] == 's') {
1146
0
            *direction = sent;
1147
0
        }
1148
0
        else
1149
0
        if (linebuff[n] == 'r') {
1150
0
            *direction = received;
1151
0
        }
1152
0
        else {
1153
0
            return false;
1154
0
        }
1155
        /* Skip it */
1156
0
        n++;
1157
0
    }
1158
0
    else {
1159
0
        *direction = sent;
1160
0
    }
1161
1162
1163
    /*********************************************************************/
1164
    /* Find and read the timestamp                                       */
1165
1166
    /* Now scan to the next digit, which should be the start of the timestamp */
1167
    /* This will involve skipping " tm "                                      */
1168
1169
0
    for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1170
0
    if (n >= line_length) {
1171
0
        return false;
1172
0
    }
1173
1174
0
    for (; (n < line_length) && !g_ascii_isdigit(linebuff[n]); n++);
1175
0
    if (n >= line_length) {
1176
0
        return false;
1177
0
    }
1178
1179
0
    *before_time_offset = n;
1180
1181
    /* Seconds */
1182
0
    for (seconds_chars = 0;
1183
0
         (linebuff[n] != '.') &&
1184
0
         (seconds_chars <= MAX_SECONDS_CHARS) &&
1185
0
         (n < line_length);
1186
0
         n++, seconds_chars++) {
1187
1188
0
        if (!g_ascii_isdigit(linebuff[n])) {
1189
            /* Found a non-digit before decimal point. Fail */
1190
0
            return false;
1191
0
        }
1192
0
        seconds_buff[seconds_chars] = linebuff[n];
1193
0
    }
1194
0
    if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1195
        /* Didn't fit in buffer.  Fail rather than use truncated */
1196
0
        return false;
1197
0
    }
1198
1199
    /* Convert found value into number */
1200
0
    seconds_buff[seconds_chars] = '\0';
1201
    /* Already know they are digits, so avoid expense of ws_strtoi32() */
1202
0
    int multiplier = 1;
1203
0
    *seconds = 0;
1204
0
    for (int d=seconds_chars-1; d >= 0; d--) {
1205
0
        *seconds += ((seconds_buff[d]-'0')*multiplier);
1206
0
        multiplier *= 10;
1207
0
    }
1208
1209
    /* The decimal point must follow the last of the seconds digits */
1210
0
    if (linebuff[n] != '.') {
1211
0
        return false;
1212
0
    }
1213
    /* Skip it */
1214
0
    n++;
1215
1216
    /* Subsecond decimal digits (expect 4-digit accuracy) */
1217
0
    for (subsecond_decimals_chars = 0;
1218
0
         (linebuff[n] != ' ') &&
1219
0
         (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1220
0
         (n < line_length);
1221
0
         n++, subsecond_decimals_chars++) {
1222
1223
0
        if (!g_ascii_isdigit(linebuff[n])) {
1224
0
            return false;
1225
0
        }
1226
0
        subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1227
0
    }
1228
0
    if (subsecond_decimals_chars != MAX_SUBSECOND_DECIMALS || n >= line_length) {
1229
        /* There should be exactly 4 subsecond digits - give up if not */
1230
0
        return false;
1231
0
    }
1232
    /* Convert found value into microseconds */
1233
0
    subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1234
    /* Already know they are digits, so avoid expense of ws_strtoi32() */
1235
0
    *useconds = ((subsecond_decimals_buff[0]-'0') * 100000) +
1236
0
                ((subsecond_decimals_buff[1]-'0') * 10000) +
1237
0
                ((subsecond_decimals_buff[2]-'0') * 1000) +
1238
0
                ((subsecond_decimals_buff[3]-'0') * 100);
1239
1240
    /* Space character must follow end of timestamp */
1241
0
    if (linebuff[n] != ' ') {
1242
0
        return false;
1243
0
    }
1244
1245
0
    *after_time_offset = n++;
1246
1247
    /* If we have a string message, it could either be a comment (with '$') or
1248
       a sprint line (no '$') */
1249
0
    if (*is_comment) {
1250
0
        if (strncmp(linebuff+n, "l $", 3) != 0) {
1251
0
            *is_sprint = true;
1252
0
            (void) g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
1253
0
        }
1254
0
    }
1255
1256
0
    if (!(*is_sprint)) {
1257
        /* Now skip ahead to find start of data (marked by '$') */
1258
0
        for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1259
0
        if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1260
0
            return false;
1261
0
        }
1262
        /* Skip it */
1263
0
        n++;
1264
0
    }
1265
1266
    /* Set offset to data start within line */
1267
0
    *data_offset = n;
1268
1269
    /* Set number of chars that comprise the hex string protocol data */
1270
0
    *data_chars = line_length - n;
1271
1272
    /* May need to skip first byte (2 hex string chars) */
1273
0
    if (skip_first_byte) {
1274
0
        *data_offset += 2;
1275
0
        *data_chars -= 2;
1276
0
    }
1277
1278
0
    return true;
1279
0
}
1280
1281
/***********************************/
1282
/* Process results of parse_line() */
1283
/***********************************/
1284
static bool
1285
process_parsed_line(wtap *wth, const dct2000_file_externals_t *file_externals,
1286
                    wtap_rec *rec,
1287
                    int64_t file_offset, long dollar_offset,
1288
                    int seconds, int useconds, char *timestamp_string,
1289
                    packet_direction_t direction, int encap,
1290
                    char *context_name, uint8_t context_port,
1291
                    char *protocol_name, char *variant_name,
1292
                    char *outhdr_name, char *aal_header_chars,
1293
                    bool is_comment, int data_chars,
1294
                    int *err, char **err_info)
1295
0
{
1296
0
    int n;
1297
0
    unsigned stub_offset = 0;
1298
0
    size_t length;
1299
0
    uint8_t *frame_buffer;
1300
1301
0
    wtap_setup_packet_rec(rec, wth->file_encap);
1302
0
    rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
1303
0
    rec->presence_flags = WTAP_HAS_TS;
1304
1305
    /* Fill in timestamp (capture base + packet offset) */
1306
0
    rec->ts.secs = file_externals->start_secs + seconds;
1307
0
    if ((file_externals->start_usecs + useconds) >= 1000000) {
1308
0
        rec->ts.secs++;
1309
0
    }
1310
0
    rec->ts.nsecs =
1311
0
        ((file_externals->start_usecs + useconds) % 1000000) *1000;
1312
1313
    /*
1314
     * Calculate the length of the stub info and the packet data.
1315
     * The packet data length is half bytestring length.
1316
     */
1317
0
    rec->rec_header.packet_header.caplen = (unsigned)strlen(context_name)+1 +     /* Context name */
1318
0
                   1 +                                 /* port */
1319
0
                   (unsigned)strlen(timestamp_string)+1 + /* timestamp */
1320
0
                   (unsigned)strlen(variant_name)+1 +     /* variant */
1321
0
                   (unsigned)strlen(outhdr_name)+1 +      /* outhdr */
1322
0
                   (unsigned)strlen(protocol_name)+1 +    /* Protocol name */
1323
0
                   1 +                                 /* direction */
1324
0
                   1 +                                 /* encap */
1325
0
                   (is_comment ? data_chars : (data_chars/2));
1326
0
    if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
1327
        /*
1328
         * Probably a corrupt capture file; return an error,
1329
         * so that our caller doesn't blow up trying to allocate
1330
         * space for an immensely-large packet.
1331
         */
1332
0
        *err = WTAP_ERR_BAD_FILE;
1333
0
        *err_info = ws_strdup_printf("catapult dct2000: File has %u-byte packet, bigger than maximum of %u",
1334
0
                                    rec->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
1335
0
        return false;
1336
0
    }
1337
0
    rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
1338
1339
    /*****************************/
1340
    /* Get the data buffer ready */
1341
0
    ws_buffer_assure_space(&rec->data, rec->rec_header.packet_header.caplen);
1342
0
    frame_buffer = ws_buffer_start_ptr(&rec->data);
1343
1344
    /******************************************/
1345
    /* Write the stub info to the data buffer */
1346
1347
    /* Context name */
1348
0
    length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1349
0
    stub_offset += (unsigned)MIN(length, MAX_CONTEXT_NAME) + 1;
1350
1351
    /* Context port number */
1352
0
    frame_buffer[stub_offset] = context_port;
1353
0
    stub_offset++;
1354
1355
    /* Timestamp within file (terminated string) */
1356
0
    length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1357
0
    stub_offset += (unsigned)MIN(length, MAX_TIMESTAMP_LEN) + 1;
1358
1359
    /* Protocol name (terminated string) */
1360
0
    length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1361
0
    stub_offset += (unsigned)MIN(length, MAX_PROTOCOL_NAME) + 1;
1362
1363
    /* Protocol variant number (as terminated string) */
1364
0
    length = g_strlcpy((char*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1365
0
    stub_offset += (unsigned)MIN(length, MAX_VARIANT_DIGITS) + 1;
1366
1367
    /* Outhdr (terminated string) */
1368
0
    length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1369
0
    stub_offset += (unsigned)MIN(length, MAX_OUTHDR_NAME) + 1;
1370
1371
    /* Direction */
1372
0
    frame_buffer[stub_offset++] = direction;
1373
1374
    /* Encap */
1375
0
    frame_buffer[stub_offset++] = (uint8_t)encap;
1376
0
    ws_buffer_increase_length(&rec->data, stub_offset);
1377
1378
0
    if (!is_comment) {
1379
        /***********************************************************/
1380
        /* Copy packet data into buffer, converting from ascii hex */
1381
0
        for (n=0; n < data_chars; n+=2) {
1382
0
            frame_buffer[stub_offset + n/2] =
1383
0
                hex_byte_from_chars(file_externals->linebuff+dollar_offset+n);
1384
0
        }
1385
0
        ws_buffer_increase_length(&rec->data, data_chars/2);
1386
0
    }
1387
0
    else {
1388
        /***********************************************************/
1389
        /* Copy packet data into buffer, just copying ascii chars  */
1390
0
        ws_buffer_append(&rec->data, (uint8_t*)&file_externals->linebuff[dollar_offset], data_chars);
1391
0
    }
1392
1393
    /*****************************************/
1394
    /* Set packet pseudo-header if necessary */
1395
0
    rec->rec_header.packet_header.pseudo_header.dct2000.seek_off = file_offset;
1396
0
    rec->rec_header.packet_header.pseudo_header.dct2000.wth = wth;
1397
1398
0
    switch (encap) {
1399
0
        case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1400
0
            set_aal_info(&rec->rec_header.packet_header.pseudo_header, direction, aal_header_chars);
1401
0
            break;
1402
0
        case WTAP_ENCAP_ISDN:
1403
0
            set_isdn_info(&rec->rec_header.packet_header.pseudo_header, direction);
1404
0
            break;
1405
0
        case WTAP_ENCAP_PPP:
1406
0
            set_ppp_info(&rec->rec_header.packet_header.pseudo_header, direction);
1407
0
            break;
1408
1409
0
        default:
1410
            /* Other supported types don't need to set anything here... */
1411
0
            break;
1412
0
    }
1413
1414
0
    return true;
1415
0
}
1416
1417
/*********************************************/
1418
/* Fill in atm pseudo-header with known info */
1419
/*********************************************/
1420
static void
1421
set_aal_info(union wtap_pseudo_header *pseudo_header,
1422
             packet_direction_t direction,
1423
             char *aal_header_chars)
1424
0
{
1425
    /* 'aal_head_chars' has this format (for AAL2 at least):
1426
       Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1427
       Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1428
       Channel Identifier (8 bits) | ...
1429
    */
1430
1431
    /* Indicate that this is a reassembled PDU */
1432
0
    pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1433
1434
    /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1435
       TODO: Can we infer the correct value here?
1436
       Meanwhile, just use the direction to make them distinguishable...
1437
    */
1438
0
    pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1439
1440
    /* Assume always AAL2 for FP */
1441
0
    pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1442
1443
0
    pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1444
0
    pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1445
1446
    /* vpi is 8 bits (2nd & 3rd nibble) */
1447
0
    pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1448
0
        hex_byte_from_chars(aal_header_chars+1);
1449
1450
    /* vci is next 16 bits */
1451
0
    pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1452
0
        ((hex_from_char(aal_header_chars[3]) << 12) |
1453
0
         (hex_from_char(aal_header_chars[4]) << 8) |
1454
0
         (hex_from_char(aal_header_chars[5]) << 4) |
1455
0
         hex_from_char(aal_header_chars[6]));
1456
1457
    /* 0 means we don't know how many cells the frame comprises. */
1458
0
    pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1459
1460
    /* cid is usually last byte.  Unless last char is not hex digit, in which
1461
       case cid is derived from last char in ascii */
1462
0
    if (g_ascii_isalnum(aal_header_chars[11])) {
1463
0
        pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1464
0
            hex_byte_from_chars(aal_header_chars+10);
1465
0
    }
1466
0
    else {
1467
0
        pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1468
0
            (int)aal_header_chars[11] - '0';
1469
0
    }
1470
0
}
1471
1472
1473
/**********************************************/
1474
/* Fill in isdn pseudo-header with known info */
1475
/**********************************************/
1476
static void
1477
set_isdn_info(union wtap_pseudo_header *pseudo_header,
1478
              packet_direction_t direction)
1479
0
{
1480
    /* This field is used to set the 'Source' and 'Destination' columns to
1481
       'User' or 'Network'. If we assume that we're simulating the network,
1482
       treat Received messages as being destined for the network.
1483
    */
1484
0
    pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1485
1486
    /* This corresponds to the circuit ID.  0 is treated as LAPD,
1487
       everything else would be treated as a B-channel
1488
    */
1489
0
    pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1490
0
}
1491
1492
1493
/*********************************************/
1494
/* Fill in ppp pseudo-header with known info */
1495
/*********************************************/
1496
static void
1497
set_ppp_info(union wtap_pseudo_header *pseudo_header,
1498
             packet_direction_t direction)
1499
0
{
1500
    /* Set direction. */
1501
0
    pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1502
0
}
1503
1504
1505
/********************************************************/
1506
/* Return hex nibble equivalent of hex string character */
1507
/********************************************************/
1508
static uint8_t
1509
hex_from_char(char c)
1510
0
{
1511
0
    if ((c >= '0') && (c <= '9')) {
1512
0
        return c - '0';
1513
0
    }
1514
1515
0
    if ((c >= 'a') && (c <= 'f')) {
1516
0
        return 0x0a + (c - 'a');
1517
0
    }
1518
1519
    /* Not a valid hex string character */
1520
0
    return 0xff;
1521
0
}
1522
1523
1524
1525
/* Table allowing fast lookup from a pair of ascii hex characters to a uint8_t */
1526
static uint8_t s_tableValues[256][256];
1527
1528
/* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1529
static void  prepare_hex_byte_from_chars_table(void)
1530
0
{
1531
0
    const unsigned char hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1532
0
                                        'a', 'b', 'c', 'd', 'e', 'f' };
1533
1534
0
    int i, j;
1535
0
    for (i=0; i < 16; i++) {
1536
0
        for (j=0; j < 16; j++) {
1537
0
            s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1538
0
        }
1539
0
    }
1540
0
}
1541
1542
/* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1543
static uint8_t hex_byte_from_chars(const char *c)
1544
0
{
1545
    /* Return value from quick table lookup */
1546
0
    return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1547
0
}
1548
1549
1550
1551
/********************************************************/
1552
/* Return character corresponding to hex nibble value   */
1553
/********************************************************/
1554
static char
1555
char_from_hex(uint8_t hex)
1556
0
{
1557
0
    static const char hex_lookup[16] =
1558
0
    { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1559
1560
0
    if (hex > 15) {
1561
0
        return '?';
1562
0
    }
1563
1564
0
    return hex_lookup[hex];
1565
0
}
1566
1567
/***********************************************/
1568
/* Equality test for packet prefix hash tables */
1569
/***********************************************/
1570
static int
1571
packet_offset_equal(const void *v, const void *v2)
1572
0
{
1573
    /* Dereferenced pointers must have same int64_t offset value */
1574
0
    return (*(const int64_t*)v == *(const int64_t*)v2);
1575
0
}
1576
1577
1578
/********************************************/
1579
/* Hash function for packet-prefix hash table */
1580
/********************************************/
1581
static unsigned
1582
packet_offset_hash_func(const void *v)
1583
0
{
1584
    /* Use low-order bits of int64_t offset value */
1585
0
    return (unsigned)(*(const int64_t*)v);
1586
0
}
1587
1588
1589
/************************************************************************/
1590
/* Parse year, month, day, hour, minute, seconds out of formatted line. */
1591
/* Set secs and usecs as output                                         */
1592
/* Return false if no valid time can be read                            */
1593
/************************************************************************/
1594
static bool
1595
get_file_time_stamp(const char *linebuff, time_t *secs, uint32_t *usecs)
1596
0
{
1597
0
    struct tm tm;
1598
0
    #define MAX_MONTH_LETTERS 9
1599
0
    char month[MAX_MONTH_LETTERS+1];
1600
1601
0
    int day, year, hour, minute, second;
1602
0
    int scan_found;
1603
1604
    /* If line longer than expected, file is probably not correctly formatted */
1605
0
    if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1606
0
        return false;
1607
0
    }
1608
1609
    /********************************************************/
1610
    /* Scan for all fields                                  */
1611
0
    scan_found = sscanf(linebuff, "%9s %2d, %4d     %2d:%2d:%2d.%4u",
1612
0
                        month, &day, &year, &hour, &minute, &second, usecs);
1613
0
    if (scan_found != 7) {
1614
        /* Give up if not all found */
1615
0
        return false;
1616
0
    }
1617
1618
0
    if      (strcmp(month, "January"  ) == 0)  tm.tm_mon = 0;
1619
0
    else if (strcmp(month, "February" ) == 0)  tm.tm_mon = 1;
1620
0
    else if (strcmp(month, "March"    ) == 0)  tm.tm_mon = 2;
1621
0
    else if (strcmp(month, "April"    ) == 0)  tm.tm_mon = 3;
1622
0
    else if (strcmp(month, "May"      ) == 0)  tm.tm_mon = 4;
1623
0
    else if (strcmp(month, "June"     ) == 0)  tm.tm_mon = 5;
1624
0
    else if (strcmp(month, "July"     ) == 0)  tm.tm_mon = 6;
1625
0
    else if (strcmp(month, "August"   ) == 0)  tm.tm_mon = 7;
1626
0
    else if (strcmp(month, "September") == 0)  tm.tm_mon = 8;
1627
0
    else if (strcmp(month, "October"  ) == 0)  tm.tm_mon = 9;
1628
0
    else if (strcmp(month, "November" ) == 0)  tm.tm_mon = 10;
1629
0
    else if (strcmp(month, "December" ) == 0)  tm.tm_mon = 11;
1630
0
    else {
1631
        /* Give up if not found a properly-formatted date */
1632
0
        return false;
1633
0
    }
1634
1635
    /******************************************************/
1636
    /* Fill in remaining fields and return it in a time_t */
1637
0
    tm.tm_year = year - 1900;
1638
0
    tm.tm_mday = day;
1639
0
    tm.tm_hour = hour;
1640
0
    tm.tm_min = minute;
1641
0
    tm.tm_sec = second;
1642
0
    tm.tm_isdst = -1;    /* daylight saving time info not known */
1643
1644
    /* Get seconds from this time */
1645
0
    *secs = mktime(&tm);
1646
1647
    /* Multiply 4 digits given to get micro-seconds */
1648
0
    *usecs = *usecs * 100;
1649
1650
0
    return true;
1651
0
}
1652
1653
/* Free the data allocated inside a line_prefix_info_t */
1654
static gboolean
1655
free_line_prefix_info(void *key, void *value,
1656
                      void *user_data _U_)
1657
0
{
1658
0
    line_prefix_info_t *info = (line_prefix_info_t*)value;
1659
1660
    /* Free the 64-bit key value */
1661
0
    g_free(key);
1662
1663
    /* Free string */
1664
0
    g_free(info->before_time);
1665
1666
    /* And the structure itself */
1667
0
    g_free(info);
1668
1669
    /* Item will always be removed from table */
1670
0
    return true;
1671
0
}
1672
1673
static const struct supported_block_type dct2000_blocks_supported[] = {
1674
    /*
1675
     * We support packet blocks, with no comments or other options.
1676
     */
1677
    { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1678
};
1679
1680
static const struct file_type_subtype_info dct2000_info = {
1681
    "Catapult DCT2000 trace (.out format)", "dct2000", "out", NULL,
1682
    false, BLOCKS_SUPPORTED(dct2000_blocks_supported),
1683
    catapult_dct2000_dump_can_write_encap, catapult_dct2000_dump_open, NULL
1684
};
1685
1686
void register_dct2000(void)
1687
15
{
1688
15
    dct2000_file_type_subtype = wtap_register_file_type_subtype(&dct2000_info);
1689
1690
    /*
1691
     * Register name for backwards compatibility with the
1692
     * wtap_filetypes table in Lua.
1693
     */
1694
15
    wtap_register_backwards_compatibility_lua_name("CATAPULT_DCT2000",
1695
15
                                                   dct2000_file_type_subtype);
1696
15
}
1697
1698
/*
1699
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1700
 *
1701
 * Local variables:
1702
 * c-basic-offset: 4
1703
 * tab-width: 8
1704
 * indent-tabs-mode: nil
1705
 * End:
1706
 *
1707
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1708
 * :indentSize=4:tabSize=8:noTabs=true:
1709
 */