Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/wiretap/cllog.c
Line
Count
Source (jump to first uncovered line)
1
/* cllog.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
/*
10
 * Reads log files from CLX000 CAN loggers from CSS Electronics:
11
 *
12
 *    https://canlogger.csselectronics.com/clx000-docs/cl1000/log/index.html
13
 *    https://canlogger.csselectronics.com/clx000-docs/cl2000/log/index.html
14
 *
15
 * Based on the cCLLog.c, cCLLog.h, and wtap-cllog.c source files from
16
 * the WS_v2.4-Plugin_v7.1.zip version of the CSS Electronics plugin at
17
 *
18
 *    https://canlogger.csselectronics.com/downloads.php?q=wireshark
19
 *
20
 * with the files combined into one source file, modernized to
21
 * fit into an up-to-date version of Wireshark, and cleaned up
22
 * not to, for example, do seeks by rewinding and reading to
23
 * get to the seek target.
24
 *
25
 * It could probably use some further cleanup.
26
 */
27
28
#include "config.h"
29
30
#include <stdio.h>
31
#include <stdbool.h>
32
#include <stdint.h>
33
#include <string.h>
34
#include <stdlib.h>
35
#include <time.h>
36
37
#include <wsutil/str_util.h>
38
#include <wsutil/strtoi.h>
39
40
#include "wtap-int.h"
41
#include "file_wrappers.h"
42
43
/***********************************************************************************************************************
44
 * Public definitions
45
 **********************************************************************************************************************/
46
0
#define MAX_LOG_LINE_FIELDS 7 /*( seqNo, timestamp, lost, SE, ID, length, data) */
47
/***********************************************************************************************************************
48
 * Public type declarations
49
 **********************************************************************************************************************/
50
/* Time stamp structure type (sec since start + ms resolution) */
51
typedef struct { time_t epoch; uint16_t ms; } cCLLog_timeStamp_t;
52
53
 /* Message type */
54
typedef enum
55
{
56
    msg_rx_standard_e = 0,
57
    msg_rx_extended_e = 1,
58
    msg_tx_standard_e = 7,
59
    msg_tx_extended_e = 8,
60
} cCLLog_messageType_t;
61
62
/* Typedef CAN-bus message type */
63
typedef struct
64
{
65
    cCLLog_timeStamp_t timestamp;
66
    uint32_t lost;
67
    cCLLog_messageType_t msgType;
68
    uint32_t id;
69
    uint8_t length;
70
    uint8_t data[ 8 ];
71
} cCLLog_message_t;
72
73
/* Silent-mode*/
74
typedef enum { silent_disabled_e = 0, silent_enabled_e } cCLLog_silentMode_t;
75
76
/* Cyclic-mode*/
77
typedef enum { cyclic_disabled_e = 0, cyclic_enabled_e } cCLLog_cyclicMode_t;
78
79
/* Logger type */
80
typedef enum { type_CL1000_e = 0, type_CL2000_e, type_CL3000_e } cCLLog_loggerType_t;
81
82
typedef char * (*CLLog_gets_t)(char *s, int size, void *stream);
83
typedef int (*CLLog_rewind_t)(void *stream);
84
85
typedef struct cLLog_private cCLLog_logFileInfo_t;
86
87
/* Type used to parse a field in a log line */
88
typedef bool (*parseFieldFunc_t)(cCLLog_logFileInfo_t *pInfo, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info);
89
90
/* Log file information */
91
struct cLLog_private
92
{
93
    uint32_t firstLogRow;
94
    cCLLog_loggerType_t loggerType;
95
    char hwrev[5];
96
    char fwrev[5];
97
    char id[20];
98
    uint32_t sessionNo;
99
    uint32_t splitNo;
100
    cCLLog_timeStamp_t logStartTime;
101
    char logStartTimeString[ 20 ];
102
    char separator;
103
    uint8_t timeFormat;
104
    char timeSeparator;
105
    char timeSeparatorMs;
106
    char dateSeparator;
107
    char dateAndTimeSeparator;
108
    uint32_t bitRate;
109
    cCLLog_silentMode_t silentMode;
110
    cCLLog_cyclicMode_t cyclicMode;
111
112
    parseFieldFunc_t parseFieldFunc[ MAX_LOG_LINE_FIELDS ];
113
114
    /* First log time stamp as relative offset */
115
    cCLLog_timeStamp_t firstTimeStampAbs;
116
};
117
118
/***********************************************************************************************************************
119
 * Private definitions
120
 **********************************************************************************************************************/
121
0
#define HEADER_LINE_PARSE_MAPPING_LENGTH array_length(headerLineParseMapping)
122
#define MAX_LOG_LINE_LENGTH 200
123
#define TIME_STAMP_STRING_MAX_LENGTH ( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) )
124
0
#define TIME_STAMP_STRING_STRIPPED_MAX_LENGTH ( sizeof( "YYYYMMDDhhmmsskkk" ) )
125
126
/***********************************************************************************************************************
127
 * Private type definitions
128
 **********************************************************************************************************************/
129
/* Function type to parse a single log file line */
130
typedef bool (*parseFunc_t)(cCLLog_logFileInfo_t *pInfo, char *pLine, int *err, char **err_info);
131
132
/* Structure of the header parse mapping. A match string is paired with a parse function */
133
typedef struct
134
{
135
    const char *pMatchString;
136
    parseFunc_t parseFunc;
137
} headerLineParseMapping_t;
138
139
/***********************************************************************************************************************
140
 * Private function declarations
141
 **********************************************************************************************************************/
142
static bool parseColumnHeaderFields( cCLLog_logFileInfo_t *pInfo, char *pColLine );
143
static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t *pInfo, char *pTimeStampString );
144
145
/* Parse header lines functions */
146
static bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
147
static bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
148
static bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
149
static bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
150
static bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
151
static bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
152
static bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
153
static bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
154
static bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
155
static bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
156
static bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
157
static bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
158
static bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
159
static bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
160
static bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
161
static bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
162
/***********************************************************************************************************************
163
 * Private variable definitions
164
 **********************************************************************************************************************/
165
166
/* Array of header line match strings and associated parse functions */
167
static const headerLineParseMapping_t headerLineParseMapping[] =
168
{
169
    { .pMatchString = "Logger type: ", .parseFunc = parseLogFileHeaderLine_type},
170
    { .pMatchString = "HW rev: ", .parseFunc = parseLogFileHeaderLine_hwrev },
171
    { .pMatchString = "FW rev: ", .parseFunc = parseLogFileHeaderLine_fwrev },
172
    { .pMatchString = "Logger ID: ", .parseFunc = parseLogFileHeaderLine_id},
173
    { .pMatchString = "Session No.: ", .parseFunc = parseLogFileHeaderLine_sessionNo},
174
    { .pMatchString = "Split No.: ", .parseFunc = parseLogFileHeaderLine_splitNo},
175
    { .pMatchString = "Time: ", .parseFunc = parseLogFileHeaderLine_time},
176
    { .pMatchString = "Value separator: ", .parseFunc = parseLogFileHeaderLine_valueSeparator},
177
    { .pMatchString = "Time format: ", .parseFunc = parseLogFileHeaderLine_timeFormat},
178
    { .pMatchString = "Time separator: ", .parseFunc = parseLogFileHeaderLine_timeSeparator},
179
    { .pMatchString = "Time separator ms: ", .parseFunc = parseLogFileHeaderLine_timeSeparatorMs},
180
    { .pMatchString = "Date separator: ", .parseFunc = parseLogFileHeaderLine_dateSeparator},
181
    { .pMatchString = "Time and date separator: ", .parseFunc = parseLogFileHeaderLine_timeAndDateSeparator},
182
    { .pMatchString = "Bit-rate: ", .parseFunc = parseLogFileHeaderLine_bitRate},
183
    { .pMatchString = "Silent mode: ", .parseFunc = parseLogFileHeaderLine_silentMode},
184
    { .pMatchString = "Cyclic mode: ", .parseFunc = parseLogFileHeaderLine_cyclicMode},
185
};
186
187
/*
188
 * Do a string copy to a buffer of a specified length.
189
 * If the string will fit, return true.
190
 * If the string won't fit, return false.
191
 */
192
static bool
193
checked_strcpy(char *dest, size_t destlen, const char *src)
194
0
{
195
0
    size_t srclen;
196
197
0
    srclen = strlen(src) + 1; // count the trailing '\0'
198
0
    if (srclen > destlen)
199
0
        return false;
200
0
    memcpy(dest, src, srclen);
201
0
    return true;
202
0
}
203
204
/* TODO: Does not support separators set to numbers (will remove part of the time stamp also */
205
/* TODO: Does not support time stamps without ms, as given in the header */
206
/* TODO: Alot of copying slows down the parsing */
207
static bool parseFieldTS(cCLLog_logFileInfo_t *pInfo, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
208
0
{
209
0
    struct tm tm;
210
0
    int ms;
211
212
    /* Copy the string to not modify the original */
213
0
    char timeStampCopy[TIME_STAMP_STRING_MAX_LENGTH];
214
0
    if (!checked_strcpy(timeStampCopy, sizeof timeStampCopy, pField))
215
0
    {
216
0
        *err = WTAP_ERR_BAD_FILE;
217
0
        *err_info = g_strdup("cllog: time stamp is too long");
218
0
        return false;
219
0
    }
220
221
    /* Copy the header time stamp string to not modify the original */
222
0
    char timeStampHeaderCopy[TIME_STAMP_STRING_MAX_LENGTH];
223
0
    if (!checked_strcpy(timeStampHeaderCopy, sizeof timeStampHeaderCopy, pInfo->logStartTimeString))
224
0
    {
225
0
        *err = WTAP_ERR_BAD_FILE;
226
0
        *err_info = g_strdup("cllog: header time stamp too long");
227
0
        return false;
228
0
    }
229
230
    /* Strip the delimiters from the time strings */
231
0
    uint8_t msgTimeStrippedLen = stripTimeStamp(pInfo, timeStampCopy);
232
0
    if (msgTimeStrippedLen > TIME_STAMP_STRING_STRIPPED_MAX_LENGTH - 1) {
233
0
        *err = WTAP_ERR_BAD_FILE;
234
0
        *err_info = g_strdup("cllog: time stamp incorrectly formatted");
235
0
        return false;
236
0
    }
237
238
0
    uint8_t headerTimeStrippedLen = stripTimeStamp(pInfo, timeStampHeaderCopy);
239
0
    if (headerTimeStrippedLen > TIME_STAMP_STRING_STRIPPED_MAX_LENGTH - 1) {
240
0
        *err = WTAP_ERR_BAD_FILE;
241
0
        *err_info = g_strdup("cllog: header time stamp incorrectly formatted");
242
0
        return false;
243
0
    }
244
245
    /* Set time string (YYYYMMDDhhmmsskkk) to the epoch */
246
0
    char timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH] = "19700101000000000";
247
248
    /* Copy the header time to the template */
249
0
    memcpy(timeStampStringFull, timeStampHeaderCopy, headerTimeStrippedLen);
250
251
    /* Copy the stripped timestamp into the full template */
252
0
    memcpy(&timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH - 1 - msgTimeStrippedLen], timeStampCopy, msgTimeStrippedLen);
253
0
    timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH - 1] = '\0';
254
255
0
    memset(&tm, 0, sizeof tm);
256
257
    /* YYYYMMDDThhmmss */
258
0
    sscanf(timeStampStringFull, "%4u%2u%2u%2u%2u%2u%3d",
259
0
            &tm.tm_year,
260
0
            &tm.tm_mon,
261
0
            &tm.tm_mday,
262
0
            &tm.tm_hour,
263
0
            &tm.tm_min,
264
0
            &tm.tm_sec,
265
0
            &ms
266
0
            );
267
0
    tm.tm_mon -= 1;
268
0
    tm.tm_year -= 1900;
269
270
    /* To Epoch (mktime converts to epoch from local (!!!) timezone) */
271
0
    pLogEntry->timestamp.epoch = mktime(&tm);
272
0
    pLogEntry->timestamp.ms = ms;
273
274
    /* Is first time stamp ? */
275
0
    if (pInfo->firstTimeStampAbs.epoch == 0 && pInfo->firstTimeStampAbs.ms == 0)
276
0
    {
277
0
        pInfo->firstTimeStampAbs.epoch = pLogEntry->timestamp.epoch;
278
0
        pInfo->firstTimeStampAbs.ms = pLogEntry->timestamp.ms;
279
0
    }
280
281
0
    return true;
282
0
}
283
284
static bool parseFieldLost(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
285
0
{
286
0
    uint32_t lost;
287
288
0
    if (!ws_strtou32(pField, NULL, &lost)) {
289
0
        *err = WTAP_ERR_BAD_FILE;
290
0
        *err_info = g_strdup_printf("cllog: lost packet count value is not valid");
291
0
        return false;
292
0
    }
293
0
    pLogEntry->lost = lost;
294
0
    return true;
295
0
}
296
297
static bool parseFieldMsgType(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
298
0
{
299
0
     switch (pField[0])
300
0
    {
301
0
        case '0':
302
0
            pLogEntry->msgType = msg_rx_standard_e;
303
0
            return true;
304
0
        case '1':
305
0
            pLogEntry->msgType = msg_rx_extended_e;
306
0
            return true;
307
0
        case '8':
308
0
            pLogEntry->msgType = msg_tx_standard_e;
309
0
            return true;
310
0
        case '9':
311
0
            pLogEntry->msgType = msg_tx_extended_e;
312
0
            return true;
313
0
        default:
314
0
            *err = WTAP_ERR_BAD_FILE;
315
0
            *err_info = g_strdup("cllog: unknown message type");
316
0
            return false;
317
0
    }
318
0
}
319
320
static bool parseFieldID(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
321
0
{
322
0
    uint32_t id;
323
324
0
    if (!ws_hexstrtou32(pField, NULL, &id)) {
325
0
        *err = WTAP_ERR_BAD_FILE;
326
0
        *err_info = g_strdup_printf("cllog: ID value is not valid");
327
0
        return false;
328
0
    }
329
0
    pLogEntry->id = id;
330
0
    return true;
331
0
}
332
333
static bool parseFieldLength(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
334
0
{
335
0
    uint32_t length;
336
337
0
    if (!ws_strtou32(pField, NULL, &length)) {
338
0
        *err = WTAP_ERR_BAD_FILE;
339
0
        *err_info = g_strdup_printf("cllog: length value is not valid");
340
0
        return false;
341
0
    }
342
0
    if (length > array_length(pLogEntry->data)) {
343
0
        *err = WTAP_ERR_BAD_FILE;
344
0
        *err_info = g_strdup_printf("cllog: length value %u > maximum length %zu",
345
0
            length, array_length(pLogEntry->data));
346
0
        return false;
347
0
    }
348
0
    pLogEntry->length = length;
349
0
    return true;
350
0
}
351
352
static bool parseFieldData(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
353
0
{
354
0
    char *pFieldStart = pField;
355
356
    /* Set data length in case length field is not set explicitly in the log file */
357
0
    pLogEntry->length = 0;
358
359
    /* Loop all data bytes */
360
0
    while (pLogEntry->length < array_length(pLogEntry->data))
361
0
    {
362
0
        int hexdigit;
363
0
        uint8_t data;
364
365
0
        if (*pFieldStart == '\n' || *pFieldStart == '\r' || *pFieldStart == '\0')
366
0
        {
367
0
            break;
368
0
        }
369
370
0
        hexdigit = ws_xton(*pFieldStart);
371
0
        if (hexdigit < 0) {
372
0
            *err = WTAP_ERR_BAD_FILE;
373
0
            *err_info = g_strdup_printf("cllog: packet byte value 0x%02x is not valid", *pFieldStart);
374
0
            return false;
375
0
        }
376
0
        data = (uint8_t)hexdigit << 4U;
377
0
        pFieldStart++;
378
0
        hexdigit = ws_xton(*pFieldStart);
379
0
        if (hexdigit < 0) {
380
0
            *err = WTAP_ERR_BAD_FILE;
381
0
            *err_info = g_strdup_printf("cllog: packet byte value 0x%02x is not valid", *pFieldStart);
382
0
            return false;
383
0
        }
384
0
        data = data | (uint8_t)hexdigit;
385
0
        pFieldStart++;
386
0
        pLogEntry->data[pLogEntry->length++] = data;
387
0
    }
388
0
    return true;
389
0
}
390
391
static bool parseLogLine(cCLLog_logFileInfo_t *pInfo, char *pLine, cCLLog_message_t *pLogEntry, int *err, char **err_info)
392
0
{
393
0
    char *pFieldStart = pLine;
394
395
    /* Loop all fields in log line */
396
0
    for (unsigned int fieldNo = 0, finalField = 0; fieldNo < MAX_LOG_LINE_FIELDS && finalField == 0; fieldNo++)
397
0
    {
398
        /* Find field end by separator */
399
0
        char *pFieldEnd = strchr(pFieldStart, pInfo->separator);
400
401
        /* If final field, then EOL marks the end of the field */
402
0
        if (pFieldEnd == NULL)
403
0
        {
404
0
            pFieldEnd = strchr(pFieldStart, '\n');
405
0
            finalField = 1;
406
0
        }
407
408
        /* Replace separator or terminator with string termination */
409
0
        if (pFieldEnd != NULL)
410
0
        {
411
0
            *pFieldEnd = '\0';
412
0
        }
413
414
        /* Is parse function assigned to field? */
415
0
        if (pInfo->parseFieldFunc[fieldNo] != NULL)
416
0
        {
417
            /* Parse field */
418
0
            if (!pInfo->parseFieldFunc[fieldNo](pInfo, pFieldStart, pLogEntry, err, err_info))
419
0
            {
420
0
                return false;
421
0
            }
422
0
        }
423
424
        /* Skip over the separator */
425
0
        pFieldStart = pFieldEnd + 1;
426
0
    }
427
0
    return true;
428
0
}
429
430
/***********************************************************************************************************************
431
 * parseColumnHeaderFields
432
 *
433
 * Parse the column fields and determine which fields are present and the position of the fields
434
 *
435
 * @param[ in ]         pInfo           Pointer to the CLLog object
436
 * @param[ in ]         pColLine        The column line
437
 **********************************************************************************************************************/
438
static bool parseColumnHeaderFields( cCLLog_logFileInfo_t *pInfo, char *pColLine )
439
0
{
440
0
    bool resultFlag = false;
441
442
    /* Initialise field start */
443
0
    char *pFieldStart = pColLine;
444
445
    /* Loop all fields in line */
446
0
    for ( uint8_t fieldNo = 0, finalField = 0 ; fieldNo < MAX_LOG_LINE_FIELDS && finalField == 0 ; fieldNo++ )
447
0
    {
448
        /* Find field end */
449
0
        char *pFieldEnd = strchr( pFieldStart, pInfo->separator );
450
451
        /* If final field, then EOL marks the end of the field */
452
0
        if( pFieldEnd == NULL )
453
0
        {
454
0
            pFieldEnd = strchr( pFieldStart, '\n' );
455
0
            finalField = 1;
456
0
        }
457
458
        /* Replace separator or terminator with string termination */
459
0
        if (pFieldEnd != NULL)
460
0
        {
461
0
            *pFieldEnd = '\0';
462
0
        }
463
464
        /* Set field number */
465
0
        if( strcmp( pFieldStart, "Timestamp" ) == 0 )  { pInfo->parseFieldFunc[ fieldNo ] = parseFieldTS; resultFlag = true; }
466
0
        if( strcmp( pFieldStart, "Lost" ) == 0 )       { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLost; resultFlag = true; }
467
0
        if( strcmp( pFieldStart, "Type" ) == 0 )       { pInfo->parseFieldFunc[ fieldNo ] = parseFieldMsgType; resultFlag = true; }
468
0
        if( strcmp( pFieldStart, "ID" ) == 0 )         { pInfo->parseFieldFunc[ fieldNo ] = parseFieldID; resultFlag = true; }
469
0
        if( strcmp( pFieldStart, "Length" ) == 0 )     { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLength; resultFlag = true; }
470
0
        if( strcmp( pFieldStart, "Data" ) == 0 )       { pInfo->parseFieldFunc[ fieldNo ] = parseFieldData; resultFlag = true; }
471
472
        /* Set start of next field to end of previous + 1 */
473
0
        pFieldStart = pFieldEnd + 1;
474
0
    }
475
476
0
    return resultFlag;
477
0
}
478
479
/***********************************************************************************************************************
480
 * stripTimeStamp
481
 *
482
 * Strips a time stamp string for any delimiters
483
 **********************************************************************************************************************/
484
static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t *pInfo, char *pTimeStampString )
485
0
{
486
0
    uint8_t strippedLength = 0U;
487
488
    /* Char by char, strip the delimiters from the time stamp string */
489
0
    size_t timeStampStringLen = strlen( pTimeStampString );
490
0
    for (size_t i = 0U; i < timeStampStringLen; i++ )
491
0
    {
492
        /* Get char */
493
0
        char charTmp = pTimeStampString[i];
494
495
        /* If delimiter, skip */
496
0
        if( charTmp == pInfo->separator ){ continue; }
497
0
        if( charTmp == pInfo->timeSeparator ){ continue; }
498
0
        if( charTmp == pInfo->timeSeparatorMs ){ continue; }
499
0
        if( charTmp == pInfo->dateSeparator ){ continue; }
500
0
        if( charTmp == pInfo->dateAndTimeSeparator ){ continue; }
501
502
        /* Not a delimiter, keep char */
503
0
        pTimeStampString[ strippedLength++ ] = charTmp;
504
0
    }
505
0
    pTimeStampString[ strippedLength ] = '\0';
506
507
0
    return strippedLength;
508
0
}
509
510
static bool parseString(const char *pFieldValue, char *valuep, size_t valueSize, char *fieldName, int *err, char **err_info)
511
0
{
512
0
    if (!checked_strcpy(valuep, valueSize, pFieldValue))
513
0
    {
514
0
        *err = WTAP_ERR_BAD_FILE;
515
0
        *err_info = ws_strdup_printf("cllog: %s is too long",
516
0
                                     fieldName);
517
0
        return false;
518
0
    }
519
0
    return true;
520
0
}
521
522
static bool parseUnsigned(const char *pFieldValue, uint32_t *valuep, char *fieldName, int *err, char **err_info)
523
0
{
524
0
    uint32_t value;
525
526
0
    if (!ws_strtou32(pFieldValue, NULL, &value)) {
527
0
        *err = WTAP_ERR_BAD_FILE;
528
0
        *err_info = ws_strdup_printf("cllog: %s value is not valid",
529
0
                                     fieldName);
530
0
        return false;
531
0
    }
532
0
    *valuep = value;
533
0
    return true;
534
0
}
535
536
static bool parseSeparator(const char *pFieldValue, char *separatorp, char *fieldName, int *err, char **err_info)
537
0
{
538
0
    char separator = '\0';
539
540
    /* Separator field is if set e.g. ";" - that is 3 chars. Else it is "" */
541
0
    if (strlen( pFieldValue) == 3)
542
0
    {
543
0
        if (pFieldValue[0] != '"' || !g_ascii_isprint(pFieldValue[1]) ||
544
0
            pFieldValue[2] != '"')
545
0
        {
546
0
            *err = WTAP_ERR_BAD_FILE;
547
0
            *err_info = ws_strdup_printf("cllog: %s separator is not valid",
548
0
                                         fieldName);
549
0
            return false;
550
0
        }
551
0
        separator = pFieldValue[1];
552
0
    }
553
0
    *separatorp = separator;
554
0
    return true;
555
0
}
556
557
static bool parseBoolean(const char *pFieldValue, bool *value, char *fieldName, int *err, char **err_info)
558
0
{
559
0
    if (strcmp(pFieldValue, "true") == 0)
560
0
    {
561
0
        *value = true;
562
0
    }
563
0
    else if (strcmp(pFieldValue, "false") == 0)
564
0
    {
565
0
        *value = false;
566
0
    }
567
0
    else
568
0
    {
569
0
        *err = WTAP_ERR_BAD_FILE;
570
0
        *err_info = ws_strdup_printf("cllog: %s value is not valid",
571
0
                                     fieldName);
572
0
        return false;
573
0
    }
574
0
    return true;
575
0
}
576
577
static bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
578
0
{
579
0
    if (strcmp(pFieldValue, "CANLogger1000") == 0 || strcmp(pFieldValue, "CL1000") == 0)
580
0
    {
581
0
        pInfo->loggerType = type_CL1000_e;
582
0
    }
583
0
    else if (strcmp(pFieldValue, "CANLogger2000") == 0 || strcmp(pFieldValue, "CL2000") == 0)
584
0
    {
585
0
        pInfo->loggerType = type_CL2000_e;
586
0
    }
587
0
    else if (strcmp(pFieldValue, "CANLogger3000") == 0 || strcmp(pFieldValue, "CL3000") == 0)
588
0
    {
589
0
        pInfo->loggerType = type_CL3000_e;
590
0
    }
591
0
    else
592
0
    {
593
0
        *err = WTAP_ERR_BAD_FILE;
594
0
        *err_info = g_strdup("cllog: logger type value is not valid");
595
0
        return false;
596
0
    }
597
0
    return true;
598
0
}
599
600
static bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
601
0
{
602
0
    return parseString(pFieldValue, pInfo->hwrev, sizeof pInfo->hwrev, "hardware revision", err, err_info);
603
0
}
604
605
static bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
606
0
{
607
0
    return parseString(pFieldValue, pInfo->fwrev, sizeof pInfo->fwrev, "firmware revision", err, err_info);
608
0
}
609
610
static bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
611
0
{
612
0
    return parseString(pFieldValue, pInfo->id, sizeof pInfo->id, "ID", err, err_info);
613
0
}
614
615
static bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
616
0
{
617
0
    return parseUnsigned(pFieldValue, &pInfo->sessionNo, "session number", err, err_info);
618
0
}
619
620
static bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
621
0
{
622
0
    return parseUnsigned(pFieldValue, &pInfo->splitNo, "split number", err, err_info);
623
0
}
624
625
static bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
626
0
{
627
0
    struct tm tm;
628
629
0
    memset(&tm, 0, sizeof tm);
630
    /* YYYYMMDDThhmmss */
631
0
    sscanf(pFieldValue,
632
0
           "%4u%2u%2uT%2u%2u%2u",
633
0
           &tm.tm_year,
634
0
           &tm.tm_mon,
635
0
           &tm.tm_mday,
636
0
           &tm.tm_hour,
637
0
           &tm.tm_min,
638
0
           &tm.tm_sec);
639
0
    tm.tm_mon -= 1;
640
0
    tm.tm_year -= 1900;
641
642
    /* To Epoch ( mktime converts to epoch from local (!!!) timezone )*/
643
0
    pInfo->logStartTime.epoch = mktime(&tm);
644
0
    pInfo->logStartTime.ms = 0;
645
646
0
    if (!checked_strcpy(pInfo->logStartTimeString, sizeof pInfo->logStartTimeString, pFieldValue))
647
0
    {
648
0
        *err = WTAP_ERR_BAD_FILE;
649
0
        *err_info = g_strdup("cllog: time is too long");
650
0
        return false;
651
0
    }
652
0
    return true;
653
0
}
654
655
static bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
656
0
{
657
0
    return parseSeparator(pFieldValue, &pInfo->separator, "value", err, err_info);
658
0
}
659
660
static bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
661
0
{
662
0
    uint32_t format;
663
664
0
    if (!ws_strtou32(pFieldValue, NULL, &format))
665
0
    {
666
0
        *err = WTAP_ERR_BAD_FILE;
667
0
        *err_info = g_strdup("cllog: time format value is not valid");
668
0
        return false;
669
0
    }
670
0
    if (format > 6)
671
0
    {
672
0
        *err = WTAP_ERR_BAD_FILE;
673
0
        *err_info = g_strdup("cllog: time format value is not valid");
674
0
        return false;
675
0
    }
676
0
    pInfo->timeFormat = (uint8_t)format;
677
0
    return true;
678
0
}
679
680
static bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
681
0
{
682
0
    return parseSeparator(pFieldValue, &pInfo->timeSeparator, "time", err, err_info);
683
0
}
684
685
static bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
686
0
{
687
0
    return parseSeparator(pFieldValue, &pInfo->timeSeparatorMs, "time millisecond", err, err_info);
688
0
}
689
690
static bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
691
0
{
692
0
    return parseSeparator(pFieldValue, &pInfo->dateSeparator, "date", err, err_info);
693
0
}
694
695
static bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
696
0
{
697
0
    return parseSeparator(pFieldValue, &pInfo->dateAndTimeSeparator, "date and time", err, err_info);
698
0
}
699
700
static bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
701
0
{
702
0
    return parseUnsigned(pFieldValue, &pInfo->bitRate, "bit rate", err, err_info);
703
0
}
704
705
static bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
706
0
{
707
0
    bool silentMode;
708
709
0
    if (!parseBoolean(pFieldValue, &silentMode, "silent mode", err, err_info))
710
0
    {
711
0
        return false;
712
0
    }
713
714
0
    if (silentMode)
715
0
    {
716
0
        pInfo->silentMode = silent_enabled_e;
717
0
    }
718
0
    else
719
0
    {
720
0
        pInfo->silentMode = silent_disabled_e;
721
0
    }
722
0
    return true;
723
0
}
724
725
static bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
726
0
{
727
0
    bool cyclicMode;
728
729
0
    if (!parseBoolean(pFieldValue, &cyclicMode, "silent mode", err, err_info))
730
0
    {
731
0
        return false;
732
0
    }
733
734
0
    if (cyclicMode)
735
0
    {
736
0
        pInfo->cyclicMode = cyclic_enabled_e;
737
0
    }
738
0
    else
739
0
    {
740
0
        pInfo->cyclicMode = cyclic_disabled_e;
741
0
    }
742
0
    return true;
743
0
}
744
745
/*
746
747
         c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(248): warning C4
748
       477: 'sscanf' : format string '%i' requires an argument of type 'int *',
749
        but variadic argument 1 has type 'uint8_t *'
750
         c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(274): warning C4
751
       477: 'sscanf' : format string '%i' requires an argument of type 'int *',
752
        but variadic argument 1 has type 'uint8_t *'
753
         c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(288): warning C4
754
       477: 'sscanf' : format string '%2x' requires an argument of type 'unsign
755
       ed int *', but variadic argument 1 has type 'uint8_t *
756
757
758
*/
759
760
#include "cllog.h"
761
762
static int cllog_file_type_subtype = -1;
763
764
0
#define CAN_EFF_MASK 0x1FFFFFFF /* extended frame format (EFF) */
765
0
#define CAN_SFF_MASK 0x000007FF /* standard frame format (SFF) */
766
767
static bool
768
cllog_read_common(wtap *wth, FILE_T fh, wtap_rec *rec, int *err, char **err_info)
769
0
{
770
0
    cCLLog_logFileInfo_t *clLog = (cCLLog_logFileInfo_t *) wth->priv;
771
0
    char line[MAX_LOG_LINE_LENGTH];
772
0
    cCLLog_message_t logEntry;
773
0
    uint8_t *can_data;
774
775
    /* Read a line */
776
0
    if (file_gets(line, sizeof(line), fh) == NULL)
777
0
    {
778
        /* EOF or error. */
779
0
        *err = file_error(fh, err_info);
780
0
        return false;
781
0
    }
782
783
    /* Default the log entry structure */
784
0
    memset(&logEntry, 0, sizeof(logEntry));
785
786
    /* Parse the line */
787
0
    if (!parseLogLine(clLog, line, &logEntry, err, err_info))
788
0
    {
789
0
        return false;
790
0
    }
791
792
0
    rec->rec_type = REC_TYPE_PACKET;
793
0
    rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
794
0
    rec->presence_flags = WTAP_HAS_TS;
795
796
0
    rec->ts.secs = logEntry.timestamp.epoch;
797
0
    rec->ts.nsecs = logEntry.timestamp.ms * 1000U * 1000U;
798
799
0
    rec->rec_header.packet_header.caplen = 8 + logEntry.length;
800
0
    rec->rec_header.packet_header.len = 8 + logEntry.length;
801
802
0
    if (logEntry.msgType == msg_tx_standard_e || logEntry.msgType == msg_tx_extended_e)
803
0
    {
804
0
        wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, PACK_FLAGS_DIRECTION_OUTBOUND);
805
0
    }
806
0
    else if (logEntry.msgType == msg_rx_standard_e || logEntry.msgType == msg_rx_extended_e)
807
0
    {
808
0
        wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, PACK_FLAGS_DIRECTION_INBOUND);
809
0
    }
810
811
0
    ws_buffer_assure_space(&rec->data, rec->rec_header.packet_header.caplen);
812
0
    can_data = ws_buffer_start_ptr(&rec->data);
813
814
0
    can_data[0] = (logEntry.id >> 24);
815
0
    can_data[1] = (logEntry.id >> 16);
816
0
    can_data[2] = (logEntry.id >>  8);
817
0
    can_data[3] = (logEntry.id >>  0);
818
0
    can_data[4] = logEntry.length;
819
0
    can_data[5] = 0;
820
0
    can_data[6] = 0;
821
0
    can_data[7] = 0;
822
823
0
    if (logEntry.msgType == msg_tx_extended_e || logEntry.msgType == msg_rx_extended_e || (logEntry.id & CAN_EFF_MASK) > CAN_SFF_MASK)
824
0
        can_data[0] |= 0x80;
825
826
0
    memcpy(&can_data[8], logEntry.data, logEntry.length);
827
0
    return true;
828
0
}
829
830
static bool
831
cllog_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, int64_t *data_offset)
832
0
{
833
0
    *data_offset = file_tell(wth->fh);
834
835
0
    return cllog_read_common(wth, wth->fh, rec, err, err_info);
836
0
}
837
838
static bool
839
cllog_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, int *err, char **err_info)
840
0
{
841
0
    if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
842
0
        return false;
843
844
0
    return cllog_read_common(wth, wth->random_fh, rec, err, err_info);
845
0
}
846
847
wtap_open_return_val
848
cllog_open(wtap *wth, int *err, char **err_info)
849
0
{
850
0
    cCLLog_logFileInfo_t *clLog;
851
0
    char line[ MAX_LOG_LINE_LENGTH ];
852
0
    char *linep;
853
854
0
    clLog = g_new0(cCLLog_logFileInfo_t, 1);
855
856
    /* Initialize the header information */
857
0
    clLog->loggerType = 0;
858
0
    clLog->hwrev[0] = '\0';
859
0
    clLog->fwrev[0] = '\0';
860
0
    clLog->id[0] = '\0';
861
0
    clLog->sessionNo = 0;
862
0
    clLog->splitNo = 0;
863
0
    clLog->logStartTime.epoch = 0;
864
0
    clLog->logStartTime.ms = 0;
865
0
    clLog->logStartTimeString[0] = '\0';
866
0
    clLog->separator = '\0';
867
0
    clLog->timeFormat = 0;
868
0
    clLog->timeSeparator = '\0';
869
0
    clLog->timeSeparatorMs = '\0';
870
0
    clLog->dateSeparator = '\0';
871
0
    clLog->dateAndTimeSeparator = '\0';
872
0
    clLog->bitRate = 0;
873
0
    clLog->silentMode = 0;
874
0
    clLog->cyclicMode = 0;
875
876
    /* Set parse function pointers */
877
0
    memset(clLog->parseFieldFunc, 0, sizeof( clLog->parseFieldFunc));
878
879
    /*
880
     * We're at the beginning of the file.  The header is a set
881
     * of comment lines, each beginning with a '#'. Read each line,
882
     * stopping if we see a non-comment line, and parse each
883
     * comment line; if any aren't valid, quit and indicate that
884
     * this isn't a CLX log file.
885
     */
886
0
    while ((linep = file_gets(line, sizeof(line), wth->fh)) != NULL &&
887
0
           linep[0] == '#')
888
0
    {
889
        /*
890
         * Skip the comment character and white space following it.
891
         */
892
0
        linep++;
893
0
        while (*linep == ' ' || *linep == '\t')
894
0
            linep++;
895
896
0
        if (*linep == '\0')
897
0
        {
898
            /*
899
             * Skip over empty comment lines.
900
             * XXX - should we treat that as an indication of an
901
             * invalid file?
902
             */
903
0
            continue;
904
0
        }
905
906
        /*
907
         * Look for the handler for this particular header line.
908
         */
909
0
        for (unsigned int i = 0U; i < HEADER_LINE_PARSE_MAPPING_LENGTH; i++)
910
0
        {
911
0
            const headerLineParseMapping_t *pHeaderMapping = &headerLineParseMapping[i];
912
0
            size_t matchStringLen = strlen(pHeaderMapping->pMatchString);
913
914
0
            if (strncmp(linep, pHeaderMapping->pMatchString, matchStringLen) == 0 &&
915
0
                 pHeaderMapping->parseFunc != NULL)
916
0
            {
917
                /*
918
                 * This matches this header value.
919
                 * Skip past the tag.
920
                 */
921
0
                linep += matchStringLen;
922
923
                /* Replace any newline chars with end of line */
924
0
                for (char *pChar = linep; ; pChar++)
925
0
                {
926
0
                    if (*pChar == '\n' || *pChar == '\r' || *pChar == '\0')
927
0
                    {
928
0
                        *pChar = '\0';
929
0
                        break;
930
0
                    }
931
0
                }
932
933
                /*
934
                 * Call the handler.
935
                 */
936
0
                if (!pHeaderMapping->parseFunc(clLog, linep, err, err_info))
937
0
                {
938
                    /*
939
                     * XXX - should this file be rejected as not
940
                     * one of ours?  Given the line looks like
941
                     * a comment that begins with a valid header
942
                     * field tag, it may be likely to be one of
943
                     * ours.
944
                     */
945
0
                    g_free(clLog);
946
0
                    if (*err == WTAP_ERR_BAD_FILE)
947
0
                    {
948
0
                        wmem_free(NULL, *err_info);
949
0
                        *err_info = NULL;
950
0
                    }
951
0
                    return WTAP_OPEN_NOT_MINE;
952
0
                }
953
0
            }
954
0
        }
955
0
    }
956
957
    /*
958
     * Did file_gets() fail?
959
     */
960
0
    if (linep == NULL)
961
0
    {
962
        /*
963
         * Yes - file_gets() didn't return a line.
964
         * Did it get an error?
965
         */
966
0
        *err = file_error(wth->fh, err_info);
967
0
        if (*err != 0)
968
0
        {
969
            /* Yes.  What was it? */
970
0
            if (*err == WTAP_ERR_SHORT_READ)
971
0
            {
972
                /* Incomplete header, so not ours. */
973
0
                g_free(clLog);
974
0
                return WTAP_OPEN_NOT_MINE;
975
0
            }
976
0
            else
977
0
            {
978
                /* I/O error. */
979
0
                g_free(clLog);
980
0
                return WTAP_OPEN_ERROR;
981
0
            }
982
0
        }
983
984
        /*
985
         * No, it just got an EOF; treat it as our file, as
986
         * older versions did so.
987
         *
988
         * XXX - should we treat it as not our file, as it lacks
989
         * the column header line?
990
         */
991
0
    }
992
0
    else
993
0
    {
994
        /*
995
         * We've read the first line after the header, so it's the column
996
         * header line. Parse it.
997
         */
998
0
        if (!parseColumnHeaderFields(clLog, linep))
999
0
        {
1000
0
            g_free(clLog);
1001
0
            return WTAP_OPEN_NOT_MINE;
1002
0
        }
1003
0
    }
1004
1005
0
    wth->priv = clLog;
1006
1007
0
    wth->file_type_subtype = cllog_file_type_subtype;
1008
0
    wth->file_encap = WTAP_ENCAP_SOCKETCAN;
1009
0
    wth->snapshot_length = 0;
1010
1011
0
    wth->subtype_read = cllog_read;
1012
0
    wth->subtype_seek_read = cllog_seek_read;
1013
0
    wth->file_tsprec = WTAP_TSPREC_MSEC;
1014
1015
0
    return WTAP_OPEN_MINE;
1016
0
}
1017
1018
/* Options for packet blocks. */
1019
static const struct supported_option_type packet_block_options_supported[] = {
1020
    { OPT_PKT_FLAGS, ONE_OPTION_SUPPORTED },
1021
};
1022
1023
static const struct supported_block_type cllog_blocks_supported[] = {
1024
    /*
1025
     * We support packet blocks, with only the flags option supported.
1026
     */
1027
    { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(packet_block_options_supported) }
1028
};
1029
1030
static const struct file_type_subtype_info cllog_info = {
1031
    "CSS Electronics CLX000 CAN log", "cllog", "txt", NULL,
1032
    false, BLOCKS_SUPPORTED(cllog_blocks_supported),
1033
    NULL, NULL, NULL
1034
};
1035
1036
void
1037
register_canlogger(void)
1038
14
{
1039
14
    cllog_file_type_subtype = wtap_register_file_type_subtype(&cllog_info);
1040
14
}