Bug Summary

File:builds/wireshark/wireshark/wiretap/cllog.c
Warning:line 473, column 33
Addition of a null pointer (from variable 'pFieldEnd') and a nonzero integer value results in undefined behavior

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name cllog.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /usr/include/libxml2 -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D wiretap_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/wiretap -I /builds/wireshark/wireshark/build/wiretap -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu17 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-06-05-100346-3529-1 -x c /builds/wireshark/wireshark/wiretap/cllog.c
1/* cllog.c
2 *
3 * Wiretap Library
4 * Copyright (c) 1998 by Gilbert Ramirez <[email protected]>
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/array.h>
38#include <wsutil/str_util.h>
39#include <wsutil/strtoi.h>
40
41#include "socketcan.h"
42#include "file_wrappers.h"
43#include "cllog.h"
44
45static int cllog_file_type_subtype = -1;
46
47/***********************************************************************************************************************
48 * Public definitions
49 **********************************************************************************************************************/
50#define MAX_LOG_LINE_FIELDS7 7 /*( seqNo, timestamp, lost, SE, ID, length, data) */
51/***********************************************************************************************************************
52 * Public type declarations
53 **********************************************************************************************************************/
54
55 /* Message type */
56typedef enum
57{
58 msg_rx_standard_e = 0,
59 msg_rx_extended_e = 1,
60 msg_tx_standard_e = 7,
61 msg_tx_extended_e = 8,
62} cCLLog_messageType_t;
63
64/* Typedef CAN-bus message type */
65typedef struct
66{
67 uint32_t lost;
68 cCLLog_messageType_t msgType;
69 wtap_can_msg_t msg;
70} cCLLog_message_t;
71
72/* Silent-mode*/
73typedef enum { silent_disabled_e = 0, silent_enabled_e } cCLLog_silentMode_t;
74
75/* Cyclic-mode*/
76typedef enum { cyclic_disabled_e = 0, cyclic_enabled_e } cCLLog_cyclicMode_t;
77
78/* Logger type */
79typedef enum { type_CL1000_e = 0, type_CL2000_e, type_CL3000_e } cCLLog_loggerType_t;
80
81typedef char * (*CLLog_gets_t)(char *s, int size, void *stream);
82typedef int (*CLLog_rewind_t)(void *stream);
83
84typedef struct cLLog_private cCLLog_logFileInfo_t;
85
86/* Type used to parse a field in a log line */
87typedef bool_Bool (*parseFieldFunc_t)(cCLLog_logFileInfo_t *pInfo, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info);
88
89/* Log file information */
90struct cLLog_private
91{
92 uint32_t firstLogRow;
93 cCLLog_loggerType_t loggerType;
94 char hwrev[5];
95 char fwrev[5];
96 char id[20];
97 uint32_t sessionNo;
98 uint32_t splitNo;
99 nstime_t logStartTime;
100 char logStartTimeString[ 20 ];
101 char separator;
102 uint8_t timeFormat;
103 char timeSeparator;
104 char timeSeparatorMs;
105 char dateSeparator;
106 char dateAndTimeSeparator;
107 uint32_t bitRate;
108 cCLLog_silentMode_t silentMode;
109 cCLLog_cyclicMode_t cyclicMode;
110
111 parseFieldFunc_t parseFieldFunc[ MAX_LOG_LINE_FIELDS7 ];
112
113 /* First log time stamp as relative offset */
114 nstime_t firstTimeStampAbs;
115};
116
117/***********************************************************************************************************************
118 * Private definitions
119 **********************************************************************************************************************/
120#define HEADER_LINE_PARSE_MAPPING_LENGTH(sizeof (headerLineParseMapping) / sizeof (headerLineParseMapping
)[0])
array_length(headerLineParseMapping)(sizeof (headerLineParseMapping) / sizeof (headerLineParseMapping
)[0])
121#define MAX_LOG_LINE_LENGTH200 200
122#define TIME_STAMP_STRING_MAX_LENGTH( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) ) ( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) )
123#define TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) ( sizeof( "YYYYMMDDhhmmsskkk" ) )
124
125/***********************************************************************************************************************
126 * Private type definitions
127 **********************************************************************************************************************/
128/* Function type to parse a single log file line */
129typedef bool_Bool (*parseFunc_t)(cCLLog_logFileInfo_t *pInfo, char *pLine, int *err, char **err_info);
130
131/* Structure of the header parse mapping. A match string is paired with a parse function */
132typedef struct
133{
134 const char *pMatchString;
135 parseFunc_t parseFunc;
136} headerLineParseMapping_t;
137
138/***********************************************************************************************************************
139 * Private function declarations
140 **********************************************************************************************************************/
141static bool_Bool parseColumnHeaderFields( cCLLog_logFileInfo_t *pInfo, char *pColLine );
142static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t *pInfo, char *pTimeStampString );
143
144/* Parse header lines functions */
145static bool_Bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
146static bool_Bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
147static bool_Bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
148static bool_Bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
149static bool_Bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
150static bool_Bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
151static bool_Bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
152static bool_Bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
153static bool_Bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
154static bool_Bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
155static bool_Bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
156static bool_Bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
157static bool_Bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
158static bool_Bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
159static bool_Bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
160static bool_Bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
161/***********************************************************************************************************************
162 * Private variable definitions
163 **********************************************************************************************************************/
164
165/* Array of header line match strings and associated parse functions */
166static const headerLineParseMapping_t headerLineParseMapping[] =
167{
168 { .pMatchString = "Logger type: ", .parseFunc = parseLogFileHeaderLine_type},
169 { .pMatchString = "HW rev: ", .parseFunc = parseLogFileHeaderLine_hwrev },
170 { .pMatchString = "FW rev: ", .parseFunc = parseLogFileHeaderLine_fwrev },
171 { .pMatchString = "Logger ID: ", .parseFunc = parseLogFileHeaderLine_id},
172 { .pMatchString = "Session No.: ", .parseFunc = parseLogFileHeaderLine_sessionNo},
173 { .pMatchString = "Split No.: ", .parseFunc = parseLogFileHeaderLine_splitNo},
174 { .pMatchString = "Time: ", .parseFunc = parseLogFileHeaderLine_time},
175 { .pMatchString = "Value separator: ", .parseFunc = parseLogFileHeaderLine_valueSeparator},
176 { .pMatchString = "Time format: ", .parseFunc = parseLogFileHeaderLine_timeFormat},
177 { .pMatchString = "Time separator: ", .parseFunc = parseLogFileHeaderLine_timeSeparator},
178 { .pMatchString = "Time separator ms: ", .parseFunc = parseLogFileHeaderLine_timeSeparatorMs},
179 { .pMatchString = "Date separator: ", .parseFunc = parseLogFileHeaderLine_dateSeparator},
180 { .pMatchString = "Time and date separator: ", .parseFunc = parseLogFileHeaderLine_timeAndDateSeparator},
181 { .pMatchString = "Bit-rate: ", .parseFunc = parseLogFileHeaderLine_bitRate},
182 { .pMatchString = "Silent mode: ", .parseFunc = parseLogFileHeaderLine_silentMode},
183 { .pMatchString = "Cyclic mode: ", .parseFunc = parseLogFileHeaderLine_cyclicMode},
184};
185
186/*
187 * Do a string copy to a buffer of a specified length.
188 * If the string will fit, return true.
189 * If the string won't fit, return false.
190 */
191static bool_Bool
192checked_strcpy(char *dest, size_t destlen, const char *src)
193{
194 size_t srclen;
195
196 srclen = strlen(src) + 1; // count the trailing '\0'
197 if (srclen > destlen)
198 return false0;
199 memcpy(dest, src, srclen);
200 return true1;
201}
202
203/* TODO: Does not support separators set to numbers (will remove part of the time stamp also */
204/* TODO: Does not support time stamps without ms, as given in the header */
205/* TODO: Alot of copying slows down the parsing */
206static bool_Bool parseFieldTS(cCLLog_logFileInfo_t *pInfo, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
207{
208 struct tm tm;
209 int ms;
210
211 /* Copy the string to not modify the original */
212 char timeStampCopy[TIME_STAMP_STRING_MAX_LENGTH( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) )];
213 if (!checked_strcpy(timeStampCopy, sizeof timeStampCopy, pField))
214 {
215 *err = WTAP_ERR_BAD_FILE-13;
216 *err_info = g_strdup("cllog: time stamp is too long")g_strdup_inline ("cllog: time stamp is too long");
217 return false0;
218 }
219
220 /* Copy the header time stamp string to not modify the original */
221 char timeStampHeaderCopy[TIME_STAMP_STRING_MAX_LENGTH( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) )];
222 if (!checked_strcpy(timeStampHeaderCopy, sizeof timeStampHeaderCopy, pInfo->logStartTimeString))
223 {
224 *err = WTAP_ERR_BAD_FILE-13;
225 *err_info = g_strdup("cllog: header time stamp too long")g_strdup_inline ("cllog: header time stamp too long");
226 return false0;
227 }
228
229 /* Strip the delimiters from the time strings */
230 uint8_t msgTimeStrippedLen = stripTimeStamp(pInfo, timeStampCopy);
231 if (msgTimeStrippedLen > TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) - 1) {
232 *err = WTAP_ERR_BAD_FILE-13;
233 *err_info = g_strdup("cllog: time stamp incorrectly formatted")g_strdup_inline ("cllog: time stamp incorrectly formatted");
234 return false0;
235 }
236
237 uint8_t headerTimeStrippedLen = stripTimeStamp(pInfo, timeStampHeaderCopy);
238 if (headerTimeStrippedLen > TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) - 1) {
239 *err = WTAP_ERR_BAD_FILE-13;
240 *err_info = g_strdup("cllog: header time stamp incorrectly formatted")g_strdup_inline ("cllog: header time stamp incorrectly formatted"
)
;
241 return false0;
242 }
243
244 /* Set time string (YYYYMMDDhhmmsskkk) to the epoch */
245 char timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) )] = "19700101000000000";
246
247 /* Copy the header time to the template */
248 memcpy(timeStampStringFull, timeStampHeaderCopy, headerTimeStrippedLen);
249
250 /* Copy the stripped timestamp into the full template */
251 memcpy(&timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) - 1 - msgTimeStrippedLen], timeStampCopy, msgTimeStrippedLen);
252 timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) - 1] = '\0';
253
254 memset(&tm, 0, sizeof tm);
255
256 /* YYYYMMDDThhmmss */
257 sscanf(timeStampStringFull, "%4u%2u%2u%2u%2u%2u%3d",
258 &tm.tm_year,
259 &tm.tm_mon,
260 &tm.tm_mday,
261 &tm.tm_hour,
262 &tm.tm_min,
263 &tm.tm_sec,
264 &ms
265 );
266 tm.tm_mon -= 1;
267 tm.tm_year -= 1900;
268
269 /* To Epoch (mktime converts to epoch from local (!!!) timezone) */
270 pLogEntry->msg.ts.secs = mktime(&tm);
271 pLogEntry->msg.ts.nsecs = ms*1000 * 1000;
272
273 /* Is first time stamp ? */
274 if (pInfo->firstTimeStampAbs.secs == 0 && pInfo->firstTimeStampAbs.nsecs == 0)
275 pInfo->firstTimeStampAbs = pLogEntry->msg.ts;
276
277 return true1;
278}
279
280static bool_Bool parseFieldLost(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
281{
282 uint32_t lost;
283
284 if (!ws_strtou32(pField, NULL((void*)0), &lost)) {
285 *err = WTAP_ERR_BAD_FILE-13;
286 *err_info = g_strdup_printf("cllog: lost packet count value is not valid");
287 return false0;
288 }
289 pLogEntry->lost = lost;
290 return true1;
291}
292
293static bool_Bool parseFieldMsgType(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
294{
295 switch (pField[0])
296 {
297 case '0':
298 pLogEntry->msgType = msg_rx_standard_e;
299 pLogEntry->msg.type = MSG_TYPE_STD;
300 return true1;
301 case '1':
302 pLogEntry->msgType = msg_rx_extended_e;
303 pLogEntry->msg.type = MSG_TYPE_EXT;
304 return true1;
305 case '8':
306 pLogEntry->msgType = msg_tx_standard_e;
307 pLogEntry->msg.type = MSG_TYPE_STD;
308 return true1;
309 case '9':
310 pLogEntry->msgType = msg_tx_extended_e;
311 pLogEntry->msg.type = MSG_TYPE_EXT;
312 return true1;
313 default:
314 *err = WTAP_ERR_BAD_FILE-13;
315 *err_info = g_strdup("cllog: unknown message type")g_strdup_inline ("cllog: unknown message type");
316 return false0;
317 }
318}
319
320static bool_Bool parseFieldID(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
321{
322 uint32_t id;
323
324 if (!ws_hexstrtou32(pField, NULL((void*)0), &id)) {
325 *err = WTAP_ERR_BAD_FILE-13;
326 *err_info = g_strdup_printf("cllog: ID value is not valid");
327 return false0;
328 }
329 pLogEntry->msg.id = id;
330 return true1;
331}
332
333static bool_Bool parseFieldLength(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
334{
335 uint32_t length;
336
337 if (!ws_strtou32(pField, NULL((void*)0), &length)) {
338 *err = WTAP_ERR_BAD_FILE-13;
339 *err_info = g_strdup_printf("cllog: length value is not valid");
340 return false0;
341 }
342 if (length > array_length(pLogEntry->msg.data.data)(sizeof (pLogEntry->msg.data.data) / sizeof (pLogEntry->
msg.data.data)[0])
) {
343 *err = WTAP_ERR_BAD_FILE-13;
344 *err_info = g_strdup_printf("cllog: length value %u > maximum length %zu",
345 length, array_length(pLogEntry->msg.data.data)(sizeof (pLogEntry->msg.data.data) / sizeof (pLogEntry->
msg.data.data)[0])
);
346 return false0;
347 }
348 pLogEntry->msg.data.length = length;
349 return true1;
350}
351
352static bool_Bool parseFieldData(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
353{
354 char *pFieldStart = pField;
355
356 /* Set data length in case length field is not set explicitly in the log file */
357 pLogEntry->msg.data.length = 0;
358
359 /* Loop all data bytes */
360 while (pLogEntry->msg.data.length < array_length(pLogEntry->msg.data.data)(sizeof (pLogEntry->msg.data.data) / sizeof (pLogEntry->
msg.data.data)[0])
)
361 {
362 int hexdigit;
363 uint8_t data;
364
365 if (*pFieldStart == '\n' || *pFieldStart == '\r' || *pFieldStart == '\0')
366 {
367 break;
368 }
369
370 hexdigit = ws_xton(*pFieldStart);
371 if (hexdigit < 0) {
372 *err = WTAP_ERR_BAD_FILE-13;
373 *err_info = g_strdup_printf("cllog: packet byte value 0x%02x is not valid", *pFieldStart);
374 return false0;
375 }
376 data = (uint8_t)hexdigit << 4U;
377 pFieldStart++;
378 hexdigit = ws_xton(*pFieldStart);
379 if (hexdigit < 0) {
380 *err = WTAP_ERR_BAD_FILE-13;
381 *err_info = g_strdup_printf("cllog: packet byte value 0x%02x is not valid", *pFieldStart);
382 return false0;
383 }
384 data = data | (uint8_t)hexdigit;
385 pFieldStart++;
386 pLogEntry->msg.data.data[pLogEntry->msg.data.length++] = data;
387 }
388 return true1;
389}
390
391static bool_Bool parseLogLine(cCLLog_logFileInfo_t *pInfo, char *pLine, cCLLog_message_t *pLogEntry, int *err, char **err_info)
392{
393 char *pFieldStart = pLine;
394
395 /* Loop all fields in log line */
396 for (unsigned int fieldNo = 0, finalField = 0; fieldNo < MAX_LOG_LINE_FIELDS7 && finalField == 0; fieldNo++)
397 {
398 /* Find field end by separator */
399 char *pFieldEnd = strchr(pFieldStart, pInfo->separator);
400
401 /* If final field, then EOL marks the end of the field */
402 if (pFieldEnd == NULL((void*)0))
403 {
404 pFieldEnd = strchr(pFieldStart, '\n');
405 finalField = 1;
406 }
407
408 /* Replace separator or terminator with string termination */
409 if (pFieldEnd != NULL((void*)0))
410 {
411 *pFieldEnd = '\0';
412 }
413
414 /* Is parse function assigned to field? */
415 if (pInfo->parseFieldFunc[fieldNo] != NULL((void*)0))
416 {
417 /* Parse field */
418 if (!pInfo->parseFieldFunc[fieldNo](pInfo, pFieldStart, pLogEntry, err, err_info))
419 {
420 return false0;
421 }
422 }
423
424 /* Skip over the separator */
425 pFieldStart = pFieldEnd + 1;
426 }
427 return true1;
428}
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 **********************************************************************************************************************/
438static bool_Bool parseColumnHeaderFields( cCLLog_logFileInfo_t *pInfo, char *pColLine )
439{
440 bool_Bool resultFlag = false0;
441
442 /* Initialise field start */
443 char *pFieldStart = pColLine;
444
445 /* Loop all fields in line */
446 for ( uint8_t fieldNo = 0, finalField = 0 ; fieldNo
0.1
'fieldNo' is < MAX_LOG_LINE_FIELDS
< MAX_LOG_LINE_FIELDS7 && finalField == 0 ; fieldNo++ )
1
Loop condition is true. Entering loop body
447 {
448 /* Find field end */
449 char *pFieldEnd = strchr( pFieldStart, pInfo->separator );
450
451 /* If final field, then EOL marks the end of the field */
452 if( pFieldEnd == NULL((void*)0) )
2
Assuming 'pFieldEnd' is equal to NULL
3
Taking true branch
453 {
454 pFieldEnd = strchr( pFieldStart, '\n' );
4
Value assigned to 'pFieldEnd'
455 finalField = 1;
456 }
457
458 /* Replace separator or terminator with string termination */
459 if (pFieldEnd != NULL((void*)0))
5
Assuming 'pFieldEnd' is equal to NULL
6
Taking false branch
460 {
461 *pFieldEnd = '\0';
462 }
463
464 /* Set field number */
465 if( strcmp( pFieldStart, "Timestamp" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldTS; resultFlag = true1; }
7
Assuming the condition is false
8
Taking false branch
466 if( strcmp( pFieldStart, "Lost" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLost; resultFlag = true1; }
9
Assuming the condition is false
10
Taking false branch
467 if( strcmp( pFieldStart, "Type" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldMsgType; resultFlag = true1; }
11
Assuming the condition is false
12
Taking false branch
468 if( strcmp( pFieldStart, "ID" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldID; resultFlag = true1; }
13
Assuming the condition is false
14
Taking false branch
469 if( strcmp( pFieldStart, "Length" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLength; resultFlag = true1; }
15
Assuming the condition is false
16
Taking false branch
470 if( strcmp( pFieldStart, "Data" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldData; resultFlag = true1; }
17
Assuming the condition is false
18
Taking false branch
471
472 /* Set start of next field to end of previous + 1 */
473 pFieldStart = pFieldEnd + 1;
19
Addition of a null pointer (from variable 'pFieldEnd') and a nonzero integer value results in undefined behavior
474 }
475
476 return resultFlag;
477}
478
479/***********************************************************************************************************************
480 * stripTimeStamp
481 *
482 * Strips a time stamp string for any delimiters
483 **********************************************************************************************************************/
484static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t *pInfo, char *pTimeStampString )
485{
486 uint8_t strippedLength = 0U;
487
488 /* Char by char, strip the delimiters from the time stamp string */
489 size_t timeStampStringLen = strlen( pTimeStampString );
490 for (size_t i = 0U; i < timeStampStringLen; i++ )
491 {
492 /* Get char */
493 char charTmp = pTimeStampString[i];
494
495 /* If delimiter, skip */
496 if( charTmp == pInfo->separator ){ continue; }
497 if( charTmp == pInfo->timeSeparator ){ continue; }
498 if( charTmp == pInfo->timeSeparatorMs ){ continue; }
499 if( charTmp == pInfo->dateSeparator ){ continue; }
500 if( charTmp == pInfo->dateAndTimeSeparator ){ continue; }
501
502 /* Not a delimiter, keep char */
503 pTimeStampString[ strippedLength++ ] = charTmp;
504 }
505 pTimeStampString[ strippedLength ] = '\0';
506
507 return strippedLength;
508}
509
510static bool_Bool parseString(const char *pFieldValue, char *valuep, size_t valueSize, char *fieldName, int *err, char **err_info)
511{
512 if (!checked_strcpy(valuep, valueSize, pFieldValue))
513 {
514 *err = WTAP_ERR_BAD_FILE-13;
515 *err_info = ws_strdup_printf("cllog: %s is too long",wmem_strdup_printf(((void*)0), "cllog: %s is too long", fieldName
)
516 fieldName)wmem_strdup_printf(((void*)0), "cllog: %s is too long", fieldName
)
;
517 return false0;
518 }
519 return true1;
520}
521
522static bool_Bool parseUnsigned(const char *pFieldValue, uint32_t *valuep, char *fieldName, int *err, char **err_info)
523{
524 uint32_t value;
525
526 if (!ws_strtou32(pFieldValue, NULL((void*)0), &value)) {
527 *err = WTAP_ERR_BAD_FILE-13;
528 *err_info = ws_strdup_printf("cllog: %s value is not valid",wmem_strdup_printf(((void*)0), "cllog: %s value is not valid"
, fieldName)
529 fieldName)wmem_strdup_printf(((void*)0), "cllog: %s value is not valid"
, fieldName)
;
530 return false0;
531 }
532 *valuep = value;
533 return true1;
534}
535
536static bool_Bool parseSeparator(const char *pFieldValue, char *separatorp, char *fieldName, int *err, char **err_info)
537{
538 char separator = '\0';
539
540 /* Separator field is if set e.g. ";" - that is 3 chars. Else it is "" */
541 if (strlen( pFieldValue) == 3)
542 {
543 if (pFieldValue[0] != '"' || !g_ascii_isprint(pFieldValue[1])((g_ascii_table[(guchar) (pFieldValue[1])] & G_ASCII_PRINT
) != 0)
||
544 pFieldValue[2] != '"')
545 {
546 *err = WTAP_ERR_BAD_FILE-13;
547 *err_info = ws_strdup_printf("cllog: %s separator is not valid",wmem_strdup_printf(((void*)0), "cllog: %s separator is not valid"
, fieldName)
548 fieldName)wmem_strdup_printf(((void*)0), "cllog: %s separator is not valid"
, fieldName)
;
549 return false0;
550 }
551 separator = pFieldValue[1];
552 }
553 *separatorp = separator;
554 return true1;
555}
556
557static bool_Bool parseBoolean(const char *pFieldValue, bool_Bool *value, char *fieldName, int *err, char **err_info)
558{
559 if (strcmp(pFieldValue, "true") == 0)
560 {
561 *value = true1;
562 }
563 else if (strcmp(pFieldValue, "false") == 0)
564 {
565 *value = false0;
566 }
567 else
568 {
569 *err = WTAP_ERR_BAD_FILE-13;
570 *err_info = ws_strdup_printf("cllog: %s value is not valid",wmem_strdup_printf(((void*)0), "cllog: %s value is not valid"
, fieldName)
571 fieldName)wmem_strdup_printf(((void*)0), "cllog: %s value is not valid"
, fieldName)
;
572 return false0;
573 }
574 return true1;
575}
576
577static bool_Bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
578{
579 if (strcmp(pFieldValue, "CANLogger1000") == 0 || strcmp(pFieldValue, "CL1000") == 0)
580 {
581 pInfo->loggerType = type_CL1000_e;
582 }
583 else if (strcmp(pFieldValue, "CANLogger2000") == 0 || strcmp(pFieldValue, "CL2000") == 0)
584 {
585 pInfo->loggerType = type_CL2000_e;
586 }
587 else if (strcmp(pFieldValue, "CANLogger3000") == 0 || strcmp(pFieldValue, "CL3000") == 0)
588 {
589 pInfo->loggerType = type_CL3000_e;
590 }
591 else
592 {
593 *err = WTAP_ERR_BAD_FILE-13;
594 *err_info = g_strdup("cllog: logger type value is not valid")g_strdup_inline ("cllog: logger type value is not valid");
595 return false0;
596 }
597 return true1;
598}
599
600static bool_Bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
601{
602 return parseString(pFieldValue, pInfo->hwrev, sizeof pInfo->hwrev, "hardware revision", err, err_info);
603}
604
605static bool_Bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
606{
607 return parseString(pFieldValue, pInfo->fwrev, sizeof pInfo->fwrev, "firmware revision", err, err_info);
608}
609
610static bool_Bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
611{
612 return parseString(pFieldValue, pInfo->id, sizeof pInfo->id, "ID", err, err_info);
613}
614
615static bool_Bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
616{
617 return parseUnsigned(pFieldValue, &pInfo->sessionNo, "session number", err, err_info);
618}
619
620static bool_Bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
621{
622 return parseUnsigned(pFieldValue, &pInfo->splitNo, "split number", err, err_info);
623}
624
625static bool_Bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
626{
627 struct tm tm;
628
629 memset(&tm, 0, sizeof tm);
630 /* YYYYMMDDThhmmss */
631 sscanf(pFieldValue,
632 "%4u%2u%2uT%2u%2u%2u",
633 &tm.tm_year,
634 &tm.tm_mon,
635 &tm.tm_mday,
636 &tm.tm_hour,
637 &tm.tm_min,
638 &tm.tm_sec);
639 tm.tm_mon -= 1;
640 tm.tm_year -= 1900;
641
642 /* To Epoch ( mktime converts to epoch from local (!!!) timezone )*/
643 pInfo->logStartTime.secs = mktime(&tm);
644 pInfo->logStartTime.nsecs = 0;
645
646 if (!checked_strcpy(pInfo->logStartTimeString, sizeof pInfo->logStartTimeString, pFieldValue))
647 {
648 *err = WTAP_ERR_BAD_FILE-13;
649 *err_info = g_strdup("cllog: time is too long")g_strdup_inline ("cllog: time is too long");
650 return false0;
651 }
652 return true1;
653}
654
655static bool_Bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
656{
657 return parseSeparator(pFieldValue, &pInfo->separator, "value", err, err_info);
658}
659
660static bool_Bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
661{
662 uint32_t format;
663
664 if (!ws_strtou32(pFieldValue, NULL((void*)0), &format))
665 {
666 *err = WTAP_ERR_BAD_FILE-13;
667 *err_info = g_strdup("cllog: time format value is not valid")g_strdup_inline ("cllog: time format value is not valid");
668 return false0;
669 }
670 if (format > 6)
671 {
672 *err = WTAP_ERR_BAD_FILE-13;
673 *err_info = g_strdup("cllog: time format value is not valid")g_strdup_inline ("cllog: time format value is not valid");
674 return false0;
675 }
676 pInfo->timeFormat = (uint8_t)format;
677 return true1;
678}
679
680static bool_Bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
681{
682 return parseSeparator(pFieldValue, &pInfo->timeSeparator, "time", err, err_info);
683}
684
685static bool_Bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
686{
687 return parseSeparator(pFieldValue, &pInfo->timeSeparatorMs, "time millisecond", err, err_info);
688}
689
690static bool_Bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
691{
692 return parseSeparator(pFieldValue, &pInfo->dateSeparator, "date", err, err_info);
693}
694
695static bool_Bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
696{
697 return parseSeparator(pFieldValue, &pInfo->dateAndTimeSeparator, "date and time", err, err_info);
698}
699
700static bool_Bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
701{
702 return parseUnsigned(pFieldValue, &pInfo->bitRate, "bit rate", err, err_info);
703}
704
705static bool_Bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
706{
707 bool_Bool silentMode;
708
709 if (!parseBoolean(pFieldValue, &silentMode, "silent mode", err, err_info))
710 {
711 return false0;
712 }
713
714 if (silentMode)
715 {
716 pInfo->silentMode = silent_enabled_e;
717 }
718 else
719 {
720 pInfo->silentMode = silent_disabled_e;
721 }
722 return true1;
723}
724
725static bool_Bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
726{
727 bool_Bool cyclicMode;
728
729 if (!parseBoolean(pFieldValue, &cyclicMode, "silent mode", err, err_info))
730 {
731 return false0;
732 }
733
734 if (cyclicMode)
735 {
736 pInfo->cyclicMode = cyclic_enabled_e;
737 }
738 else
739 {
740 pInfo->cyclicMode = cyclic_disabled_e;
741 }
742 return true1;
743}
744
745static bool_Bool
746cllog_read_common(wtap *wth, FILE_T fh, wtap_rec *rec, int *err, char **err_info)
747{
748 cCLLog_logFileInfo_t* clLog = (cCLLog_logFileInfo_t*)wtap_socketcan_get_private_data(wth);
749 char line[MAX_LOG_LINE_LENGTH200];
750 cCLLog_message_t logEntry;
751
752 /* Read a line */
753 if (file_gets(line, sizeof(line), fh) == NULL((void*)0))
754 {
755 /* EOF or error. */
756 *err = file_error(fh, err_info);
757 return false0;
758 }
759
760 /* Default the log entry structure */
761 memset(&logEntry, 0, sizeof(logEntry));
762
763 /* Parse the line */
764 if (!parseLogLine(clLog, line, &logEntry, err, err_info))
765 {
766 return false0;
767 }
768
769 logEntry.msg.interface_id = wtap_socketcan_find_or_create_new_interface(wth, clLog->id);
770 if (!wtap_socketcan_gen_packet(wth, rec, &logEntry.msg, "cllog", err, err_info))
771 return false0;
772
773
774 if (logEntry.msgType == msg_tx_standard_e || logEntry.msgType == msg_tx_extended_e)
775 {
776 wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS2, PACK_FLAGS_DIRECTION_OUTBOUND2);
777 }
778 else if (logEntry.msgType == msg_rx_standard_e || logEntry.msgType == msg_rx_extended_e)
779 {
780 wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS2, PACK_FLAGS_DIRECTION_INBOUND1);
781 }
782
783 return true1;
784}
785
786static bool_Bool
787cllog_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, int64_t *data_offset)
788{
789 *data_offset = file_tell(wth->fh);
790
791 return cllog_read_common(wth, wth->fh, rec, err, err_info);
792}
793
794static bool_Bool
795cllog_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, int *err, char **err_info)
796{
797 if (file_seek(wth->random_fh, seek_off, SEEK_SET0, err) == -1)
798 return false0;
799
800 return cllog_read_common(wth, wth->random_fh, rec, err, err_info);
801}
802
803static void
804cllog_close(void* tap_data)
805{
806 /* Clean up the file information */
807 g_free((cCLLog_logFileInfo_t*)tap_data)(__builtin_object_size (((cCLLog_logFileInfo_t*)tap_data), 0)
!= ((size_t) - 1)) ? g_free_sized ((cCLLog_logFileInfo_t*)tap_data
, __builtin_object_size (((cCLLog_logFileInfo_t*)tap_data), 0
)) : (g_free) ((cCLLog_logFileInfo_t*)tap_data)
;
808}
809
810wtap_open_return_val
811cllog_open(wtap *wth, int *err, char **err_info)
812{
813 cCLLog_logFileInfo_t *clLog;
814 char line[ MAX_LOG_LINE_LENGTH200 ];
815 char *linep;
816
817 clLog = g_new0(cCLLog_logFileInfo_t, 1)((cCLLog_logFileInfo_t *) g_malloc0_n ((1), sizeof (cCLLog_logFileInfo_t
)))
;
818
819 /*
820 * We're at the beginning of the file. The header is a set
821 * of comment lines, each beginning with a '#'. Read each line,
822 * stopping if we see a non-comment line, and parse each
823 * comment line; if any aren't valid, quit and indicate that
824 * this isn't a CLX log file.
825 */
826 while ((linep = file_gets(line, sizeof(line), wth->fh)) != NULL((void*)0) &&
827 linep[0] == '#')
828 {
829 /*
830 * Skip the comment character and white space following it.
831 */
832 linep++;
833 while (*linep == ' ' || *linep == '\t')
834 linep++;
835
836 if (*linep == '\0')
837 {
838 /*
839 * Skip over empty comment lines.
840 * XXX - should we treat that as an indication of an
841 * invalid file?
842 */
843 continue;
844 }
845
846 /*
847 * Look for the handler for this particular header line.
848 */
849 for (unsigned int i = 0U; i < HEADER_LINE_PARSE_MAPPING_LENGTH(sizeof (headerLineParseMapping) / sizeof (headerLineParseMapping
)[0])
; i++)
850 {
851 const headerLineParseMapping_t *pHeaderMapping = &headerLineParseMapping[i];
852 size_t matchStringLen = strlen(pHeaderMapping->pMatchString);
853
854 if (strncmp(linep, pHeaderMapping->pMatchString, matchStringLen) == 0 &&
855 pHeaderMapping->parseFunc != NULL((void*)0))
856 {
857 /*
858 * This matches this header value.
859 * Skip past the tag.
860 */
861 linep += matchStringLen;
862
863 /* Replace any newline chars with end of line */
864 for (char *pChar = linep; ; pChar++)
865 {
866 if (*pChar == '\n' || *pChar == '\r' || *pChar == '\0')
867 {
868 *pChar = '\0';
869 break;
870 }
871 }
872
873 /*
874 * Call the handler.
875 */
876 if (!pHeaderMapping->parseFunc(clLog, linep, err, err_info))
877 {
878 /*
879 * XXX - should this file be rejected as not
880 * one of ours? Given the line looks like
881 * a comment that begins with a valid header
882 * field tag, it may be likely to be one of
883 * ours.
884 */
885 g_free(clLog)(__builtin_object_size ((clLog), 0) != ((size_t) - 1)) ? g_free_sized
(clLog, __builtin_object_size ((clLog), 0)) : (g_free) (clLog
)
;
886 if (*err == WTAP_ERR_BAD_FILE-13)
887 {
888 wmem_free(NULL((void*)0), *err_info);
889 *err_info = NULL((void*)0);
890 }
891 return WTAP_OPEN_NOT_MINE;
892 }
893 }
894 }
895 }
896
897 /*
898 * Did file_gets() fail?
899 */
900 if (linep == NULL((void*)0))
901 {
902 /*
903 * Yes - file_gets() didn't return a line.
904 * Did it get an error?
905 */
906 *err = file_error(wth->fh, err_info);
907 if (*err != 0)
908 {
909 /* Yes. What was it? */
910 if (*err == WTAP_ERR_SHORT_READ-12)
911 {
912 /* Incomplete header, so not ours. */
913 g_free(clLog)(__builtin_object_size ((clLog), 0) != ((size_t) - 1)) ? g_free_sized
(clLog, __builtin_object_size ((clLog), 0)) : (g_free) (clLog
)
;
914 return WTAP_OPEN_NOT_MINE;
915 }
916 else
917 {
918 /* I/O error. */
919 g_free(clLog)(__builtin_object_size ((clLog), 0) != ((size_t) - 1)) ? g_free_sized
(clLog, __builtin_object_size ((clLog), 0)) : (g_free) (clLog
)
;
920 return WTAP_OPEN_ERROR;
921 }
922 }
923
924 /*
925 * No, it just got an EOF; treat it as our file, as
926 * older versions did so.
927 *
928 * XXX - should we treat it as not our file, as it lacks
929 * the column header line?
930 */
931 }
932 else
933 {
934 /*
935 * We've read the first line after the header, so it's the column
936 * header line. Parse it. This requires that we have been provided with a
937 * valid separator.
938 */
939 if (clLog->separator == '\0' || !parseColumnHeaderFields(clLog, linep))
940 {
941 g_free(clLog)(__builtin_object_size ((clLog), 0) != ((size_t) - 1)) ? g_free_sized
(clLog, __builtin_object_size ((clLog), 0)) : (g_free) (clLog
)
;
942 return WTAP_OPEN_NOT_MINE;
943 }
944 }
945
946 wtap_set_as_socketcan(wth, cllog_file_type_subtype, WTAP_TSPREC_MSEC3, clLog, cllog_close);
947 wth->snapshot_length = 0;
948
949 wth->subtype_read = cllog_read;
950 wth->subtype_seek_read = cllog_seek_read;
951
952 return WTAP_OPEN_MINE;
953}
954
955/* Options for packet blocks. */
956static const struct supported_option_type packet_block_options_supported[] = {
957 { OPT_PKT_FLAGS2, ONE_OPTION_SUPPORTED },
958};
959
960static const struct supported_block_type cllog_blocks_supported[] = {
961 /*
962 * We support packet blocks, with only the flags option supported.
963 */
964 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(packet_block_options_supported)(sizeof (packet_block_options_supported) / sizeof (packet_block_options_supported
)[0]), packet_block_options_supported
}
965};
966
967static const struct file_type_subtype_info cllog_info = {
968 "CSS Electronics CLX000 CAN log", "cllog", "txt", NULL((void*)0),
969 false0, BLOCKS_SUPPORTED(cllog_blocks_supported)(sizeof (cllog_blocks_supported) / sizeof (cllog_blocks_supported
)[0]), cllog_blocks_supported
,
970 NULL((void*)0), NULL((void*)0), NULL((void*)0)
971};
972
973void
974register_canlogger(void)
975{
976 cllog_file_type_subtype = wtap_register_file_type_subtype(&cllog_info);
977}