Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / wiretap / cllog.c
blob5d49d7acdddcb11e9639f8fb1e8dc51614e29226
1 /* cllog.c
3 * Wiretap Library
4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
9 /*
10 * Reads log files from CLX000 CAN loggers from CSS Electronics:
12 * https://canlogger.csselectronics.com/clx000-docs/cl1000/log/index.html
13 * https://canlogger.csselectronics.com/clx000-docs/cl2000/log/index.html
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
18 * https://canlogger.csselectronics.com/downloads.php?q=wireshark
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.
25 * It could probably use some further cleanup.
28 #include "config.h"
30 #include <stdio.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <time.h>
37 #include <wsutil/str_util.h>
38 #include <wsutil/strtoi.h>
40 #include "wtap-int.h"
41 #include "file_wrappers.h"
43 /***********************************************************************************************************************
44 * Public definitions
45 **********************************************************************************************************************/
46 #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;
53 /* Message type */
54 typedef enum
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;
62 /* Typedef CAN-bus message type */
63 typedef struct
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;
73 /* Silent-mode*/
74 typedef enum { silent_disabled_e = 0, silent_enabled_e } cCLLog_silentMode_t;
76 /* Cyclic-mode*/
77 typedef enum { cyclic_disabled_e = 0, cyclic_enabled_e } cCLLog_cyclicMode_t;
79 /* Logger type */
80 typedef enum { type_CL1000_e = 0, type_CL2000_e, type_CL3000_e } cCLLog_loggerType_t;
82 typedef char * (*CLLog_gets_t)(char *s, int size, void *stream);
83 typedef int (*CLLog_rewind_t)(void *stream);
85 typedef struct cLLog_private cCLLog_logFileInfo_t;
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);
90 /* Log file information */
91 struct cLLog_private
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;
112 parseFieldFunc_t parseFieldFunc[ MAX_LOG_LINE_FIELDS ];
114 /* First log time stamp as relative offset */
115 cCLLog_timeStamp_t firstTimeStampAbs;
118 /***********************************************************************************************************************
119 * Private definitions
120 **********************************************************************************************************************/
121 #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 #define TIME_STAMP_STRING_STRIPPED_MAX_LENGTH ( sizeof( "YYYYMMDDhhmmsskkk" ) )
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);
132 /* Structure of the header parse mapping. A match string is paired with a parse function */
133 typedef struct
135 const char *pMatchString;
136 parseFunc_t parseFunc;
137 } headerLineParseMapping_t;
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 );
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 **********************************************************************************************************************/
166 /* Array of header line match strings and associated parse functions */
167 static const headerLineParseMapping_t headerLineParseMapping[] =
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},
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.
192 static bool
193 checked_strcpy(char *dest, size_t destlen, const char *src)
195 size_t srclen;
197 srclen = strlen(src) + 1; // count the trailing '\0'
198 if (srclen > destlen)
199 return false;
200 memcpy(dest, src, srclen);
201 return true;
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)
209 struct tm tm;
210 int ms;
212 /* Copy the string to not modify the original */
213 char timeStampCopy[TIME_STAMP_STRING_MAX_LENGTH];
214 if (!checked_strcpy(timeStampCopy, sizeof timeStampCopy, pField))
216 *err = WTAP_ERR_BAD_FILE;
217 *err_info = g_strdup("cllog: time stamp is too long");
218 return false;
221 /* Copy the header time stamp string to not modify the original */
222 char timeStampHeaderCopy[TIME_STAMP_STRING_MAX_LENGTH];
223 if (!checked_strcpy(timeStampHeaderCopy, sizeof timeStampHeaderCopy, pInfo->logStartTimeString))
225 *err = WTAP_ERR_BAD_FILE;
226 *err_info = g_strdup("cllog: header time stamp too long");
227 return false;
230 /* Strip the delimiters from the time strings */
231 uint8_t msgTimeStrippedLen = stripTimeStamp(pInfo, timeStampCopy);
232 if (msgTimeStrippedLen > TIME_STAMP_STRING_STRIPPED_MAX_LENGTH - 1) {
233 *err = WTAP_ERR_BAD_FILE;
234 *err_info = g_strdup("cllog: time stamp incorrectly formatted");
235 return false;
238 uint8_t headerTimeStrippedLen = stripTimeStamp(pInfo, timeStampHeaderCopy);
239 if (headerTimeStrippedLen > TIME_STAMP_STRING_STRIPPED_MAX_LENGTH - 1) {
240 *err = WTAP_ERR_BAD_FILE;
241 *err_info = g_strdup("cllog: header time stamp incorrectly formatted");
242 return false;
245 /* Set time string (YYYYMMDDhhmmsskkk) to the epoch */
246 char timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH] = "19700101000000000";
248 /* Copy the header time to the template */
249 memcpy(timeStampStringFull, timeStampHeaderCopy, headerTimeStrippedLen);
251 /* Copy the stripped timestamp into the full template */
252 memcpy(&timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH - 1 - msgTimeStrippedLen], timeStampCopy, msgTimeStrippedLen);
253 timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH - 1] = '\0';
255 memset(&tm, 0, sizeof tm);
257 /* YYYYMMDDThhmmss */
258 sscanf(timeStampStringFull, "%4u%2u%2u%2u%2u%2u%3d",
259 &tm.tm_year,
260 &tm.tm_mon,
261 &tm.tm_mday,
262 &tm.tm_hour,
263 &tm.tm_min,
264 &tm.tm_sec,
267 tm.tm_mon -= 1;
268 tm.tm_year -= 1900;
270 /* To Epoch (mktime converts to epoch from local (!!!) timezone) */
271 pLogEntry->timestamp.epoch = mktime(&tm);
272 pLogEntry->timestamp.ms = ms;
274 /* Is first time stamp ? */
275 if (pInfo->firstTimeStampAbs.epoch == 0 && pInfo->firstTimeStampAbs.ms == 0)
277 pInfo->firstTimeStampAbs.epoch = pLogEntry->timestamp.epoch;
278 pInfo->firstTimeStampAbs.ms = pLogEntry->timestamp.ms;
281 return true;
284 static bool parseFieldLost(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
286 uint32_t lost;
288 if (!ws_strtou32(pField, NULL, &lost)) {
289 *err = WTAP_ERR_BAD_FILE;
290 *err_info = g_strdup_printf("cllog: lost packet count value is not valid");
291 return false;
293 pLogEntry->lost = lost;
294 return true;
297 static bool parseFieldMsgType(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
299 switch (pField[0])
301 case '0':
302 pLogEntry->msgType = msg_rx_standard_e;
303 return true;
304 case '1':
305 pLogEntry->msgType = msg_rx_extended_e;
306 return true;
307 case '8':
308 pLogEntry->msgType = msg_tx_standard_e;
309 return true;
310 case '9':
311 pLogEntry->msgType = msg_tx_extended_e;
312 return true;
313 default:
314 *err = WTAP_ERR_BAD_FILE;
315 *err_info = g_strdup("cllog: unknown message type");
316 return false;
320 static bool parseFieldID(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
322 uint32_t id;
324 if (!ws_hexstrtou32(pField, NULL, &id)) {
325 *err = WTAP_ERR_BAD_FILE;
326 *err_info = g_strdup_printf("cllog: ID value is not valid");
327 return false;
329 pLogEntry->id = id;
330 return true;
333 static bool parseFieldLength(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
335 uint32_t length;
337 if (!ws_strtou32(pField, NULL, &length)) {
338 *err = WTAP_ERR_BAD_FILE;
339 *err_info = g_strdup_printf("cllog: length value is not valid");
340 return false;
342 if (length > array_length(pLogEntry->data)) {
343 *err = WTAP_ERR_BAD_FILE;
344 *err_info = g_strdup_printf("cllog: length value %u > maximum length %zu",
345 length, array_length(pLogEntry->data));
346 return false;
348 pLogEntry->length = length;
349 return true;
352 static bool parseFieldData(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
354 char *pFieldStart = pField;
356 /* Set data length in case length field is not set explicitly in the log file */
357 pLogEntry->length = 0;
359 /* Loop all data bytes */
360 while (pLogEntry->length < array_length(pLogEntry->data))
362 int hexdigit;
363 uint8_t data;
365 if (*pFieldStart == '\n' || *pFieldStart == '\r' || *pFieldStart == '\0')
367 break;
370 hexdigit = ws_xton(*pFieldStart);
371 if (hexdigit < 0) {
372 *err = WTAP_ERR_BAD_FILE;
373 *err_info = g_strdup_printf("cllog: packet byte value 0x%02x is not valid", *pFieldStart);
374 return false;
376 data = (uint8_t)hexdigit << 4U;
377 pFieldStart++;
378 hexdigit = ws_xton(*pFieldStart);
379 if (hexdigit < 0) {
380 *err = WTAP_ERR_BAD_FILE;
381 *err_info = g_strdup_printf("cllog: packet byte value 0x%02x is not valid", *pFieldStart);
382 return false;
384 data = data | (uint8_t)hexdigit;
385 pFieldStart++;
386 pLogEntry->data[pLogEntry->length++] = data;
388 return true;
391 static bool parseLogLine(cCLLog_logFileInfo_t *pInfo, char *pLine, cCLLog_message_t *pLogEntry, int *err, char **err_info)
393 char *pFieldStart = pLine;
395 /* Loop all fields in log line */
396 for (unsigned int fieldNo = 0, finalField = 0; fieldNo < MAX_LOG_LINE_FIELDS && finalField == 0; fieldNo++)
398 /* Find field end by separator */
399 char *pFieldEnd = strchr(pFieldStart, pInfo->separator);
401 /* If final field, then EOL marks the end of the field */
402 if (pFieldEnd == NULL)
404 pFieldEnd = strchr(pFieldStart, '\n');
405 finalField = 1;
408 /* Replace separator or terminator with string termination */
409 if (pFieldEnd != NULL)
411 *pFieldEnd = '\0';
414 /* Is parse function assigned to field? */
415 if (pInfo->parseFieldFunc[fieldNo] != NULL)
417 /* Parse field */
418 if (!pInfo->parseFieldFunc[fieldNo](pInfo, pFieldStart, pLogEntry, err, err_info))
420 return false;
424 /* Skip over the separator */
425 pFieldStart = pFieldEnd + 1;
427 return true;
430 /***********************************************************************************************************************
431 * parseColumnHeaderFields
433 * Parse the column fields and determine which fields are present and the position of the fields
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 )
440 bool resultFlag = false;
442 /* Initialise field start */
443 char *pFieldStart = pColLine;
445 /* Loop all fields in line */
446 for ( uint8_t fieldNo = 0, finalField = 0 ; fieldNo < MAX_LOG_LINE_FIELDS && finalField == 0 ; fieldNo++ )
448 /* Find field end */
449 char *pFieldEnd = strchr( pFieldStart, pInfo->separator );
451 /* If final field, then EOL marks the end of the field */
452 if( pFieldEnd == NULL )
454 pFieldEnd = strchr( pFieldStart, '\n' );
455 finalField = 1;
458 /* Replace separator or terminator with string termination */
459 if (pFieldEnd != NULL)
461 *pFieldEnd = '\0';
464 /* Set field number */
465 if( strcmp( pFieldStart, "Timestamp" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldTS; resultFlag = true; }
466 if( strcmp( pFieldStart, "Lost" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLost; resultFlag = true; }
467 if( strcmp( pFieldStart, "Type" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldMsgType; resultFlag = true; }
468 if( strcmp( pFieldStart, "ID" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldID; resultFlag = true; }
469 if( strcmp( pFieldStart, "Length" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLength; resultFlag = true; }
470 if( strcmp( pFieldStart, "Data" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldData; resultFlag = true; }
472 /* Set start of next field to end of previous + 1 */
473 pFieldStart = pFieldEnd + 1;
476 return resultFlag;
479 /***********************************************************************************************************************
480 * stripTimeStamp
482 * Strips a time stamp string for any delimiters
483 **********************************************************************************************************************/
484 static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t *pInfo, char *pTimeStampString )
486 uint8_t strippedLength = 0U;
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++ )
492 /* Get char */
493 char charTmp = pTimeStampString[i];
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; }
502 /* Not a delimiter, keep char */
503 pTimeStampString[ strippedLength++ ] = charTmp;
505 pTimeStampString[ strippedLength ] = '\0';
507 return strippedLength;
510 static bool parseString(const char *pFieldValue, char *valuep, size_t valueSize, char *fieldName, int *err, char **err_info)
512 if (!checked_strcpy(valuep, valueSize, pFieldValue))
514 *err = WTAP_ERR_BAD_FILE;
515 *err_info = ws_strdup_printf("cllog: %s is too long",
516 fieldName);
517 return false;
519 return true;
522 static bool parseUnsigned(const char *pFieldValue, uint32_t *valuep, char *fieldName, int *err, char **err_info)
524 uint32_t value;
526 if (!ws_strtou32(pFieldValue, NULL, &value)) {
527 *err = WTAP_ERR_BAD_FILE;
528 *err_info = ws_strdup_printf("cllog: %s value is not valid",
529 fieldName);
530 return false;
532 *valuep = value;
533 return true;
536 static bool parseSeparator(const char *pFieldValue, char *separatorp, char *fieldName, int *err, char **err_info)
538 char separator = '\0';
540 /* Separator field is if set e.g. ";" - that is 3 chars. Else it is "" */
541 if (strlen( pFieldValue) == 3)
543 if (pFieldValue[0] != '"' || !g_ascii_isprint(pFieldValue[1]) ||
544 pFieldValue[2] != '"')
546 *err = WTAP_ERR_BAD_FILE;
547 *err_info = ws_strdup_printf("cllog: %s separator is not valid",
548 fieldName);
549 return false;
551 separator = pFieldValue[1];
553 *separatorp = separator;
554 return true;
557 static bool parseBoolean(const char *pFieldValue, bool *value, char *fieldName, int *err, char **err_info)
559 if (strcmp(pFieldValue, "true") == 0)
561 *value = true;
563 else if (strcmp(pFieldValue, "false") == 0)
565 *value = false;
567 else
569 *err = WTAP_ERR_BAD_FILE;
570 *err_info = ws_strdup_printf("cllog: %s value is not valid",
571 fieldName);
572 return false;
574 return true;
577 static bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
579 if (strcmp(pFieldValue, "CANLogger1000") == 0 || strcmp(pFieldValue, "CL1000") == 0)
581 pInfo->loggerType = type_CL1000_e;
583 else if (strcmp(pFieldValue, "CANLogger2000") == 0 || strcmp(pFieldValue, "CL2000") == 0)
585 pInfo->loggerType = type_CL2000_e;
587 else if (strcmp(pFieldValue, "CANLogger3000") == 0 || strcmp(pFieldValue, "CL3000") == 0)
589 pInfo->loggerType = type_CL3000_e;
591 else
593 *err = WTAP_ERR_BAD_FILE;
594 *err_info = g_strdup("cllog: logger type value is not valid");
595 return false;
597 return true;
600 static bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
602 return parseString(pFieldValue, pInfo->hwrev, sizeof pInfo->hwrev, "hardware revision", err, err_info);
605 static bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
607 return parseString(pFieldValue, pInfo->fwrev, sizeof pInfo->fwrev, "firmware revision", err, err_info);
610 static bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
612 return parseString(pFieldValue, pInfo->id, sizeof pInfo->id, "ID", err, err_info);
615 static bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
617 return parseUnsigned(pFieldValue, &pInfo->sessionNo, "session number", err, err_info);
620 static bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
622 return parseUnsigned(pFieldValue, &pInfo->splitNo, "split number", err, err_info);
625 static bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
627 struct tm tm;
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;
642 /* To Epoch ( mktime converts to epoch from local (!!!) timezone )*/
643 pInfo->logStartTime.epoch = mktime(&tm);
644 pInfo->logStartTime.ms = 0;
646 if (!checked_strcpy(pInfo->logStartTimeString, sizeof pInfo->logStartTimeString, pFieldValue))
648 *err = WTAP_ERR_BAD_FILE;
649 *err_info = g_strdup("cllog: time is too long");
650 return false;
652 return true;
655 static bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
657 return parseSeparator(pFieldValue, &pInfo->separator, "value", err, err_info);
660 static bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
662 uint32_t format;
664 if (!ws_strtou32(pFieldValue, NULL, &format))
666 *err = WTAP_ERR_BAD_FILE;
667 *err_info = g_strdup("cllog: time format value is not valid");
668 return false;
670 if (format > 6)
672 *err = WTAP_ERR_BAD_FILE;
673 *err_info = g_strdup("cllog: time format value is not valid");
674 return false;
676 pInfo->timeFormat = (uint8_t)format;
677 return true;
680 static bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
682 return parseSeparator(pFieldValue, &pInfo->timeSeparator, "time", err, err_info);
685 static bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
687 return parseSeparator(pFieldValue, &pInfo->timeSeparatorMs, "time millisecond", err, err_info);
690 static bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
692 return parseSeparator(pFieldValue, &pInfo->dateSeparator, "date", err, err_info);
695 static bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
697 return parseSeparator(pFieldValue, &pInfo->dateAndTimeSeparator, "date and time", err, err_info);
700 static bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
702 return parseUnsigned(pFieldValue, &pInfo->bitRate, "bit rate", err, err_info);
705 static bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
707 bool silentMode;
709 if (!parseBoolean(pFieldValue, &silentMode, "silent mode", err, err_info))
711 return false;
714 if (silentMode)
716 pInfo->silentMode = silent_enabled_e;
718 else
720 pInfo->silentMode = silent_disabled_e;
722 return true;
725 static bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
727 bool cyclicMode;
729 if (!parseBoolean(pFieldValue, &cyclicMode, "silent mode", err, err_info))
731 return false;
734 if (cyclicMode)
736 pInfo->cyclicMode = cyclic_enabled_e;
738 else
740 pInfo->cyclicMode = cyclic_disabled_e;
742 return true;
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 *
760 #include "cllog.h"
762 static int cllog_file_type_subtype = -1;
764 #define CAN_EFF_MASK 0x1FFFFFFF /* extended frame format (EFF) */
765 #define CAN_SFF_MASK 0x000007FF /* standard frame format (SFF) */
767 static bool
768 cllog_read_common(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, char **err_info)
770 cCLLog_logFileInfo_t *clLog = (cCLLog_logFileInfo_t *) wth->priv;
771 char line[MAX_LOG_LINE_LENGTH];
772 cCLLog_message_t logEntry;
773 uint8_t *can_data;
775 /* Read a line */
776 if (file_gets(line, sizeof(line), fh) == NULL)
778 /* EOF or error. */
779 *err = file_error(wth->fh, err_info);
780 return false;
783 /* Default the log entry structure */
784 memset(&logEntry, 0, sizeof(logEntry));
786 /* Parse the line */
787 if (!parseLogLine(clLog, line, &logEntry, err, err_info))
789 return false;
792 rec->rec_type = REC_TYPE_PACKET;
793 rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
794 rec->presence_flags = WTAP_HAS_TS;
796 rec->ts.secs = logEntry.timestamp.epoch;
797 rec->ts.nsecs = logEntry.timestamp.ms * 1000U * 1000U;
799 rec->rec_header.packet_header.caplen = 8 + logEntry.length;
800 rec->rec_header.packet_header.len = 8 + logEntry.length;
802 if (logEntry.msgType == msg_tx_standard_e || logEntry.msgType == msg_tx_extended_e)
804 wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, PACK_FLAGS_DIRECTION_OUTBOUND);
806 else if (logEntry.msgType == msg_rx_standard_e || logEntry.msgType == msg_rx_extended_e)
808 wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, PACK_FLAGS_DIRECTION_INBOUND);
811 ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
812 can_data = ws_buffer_start_ptr(buf);
814 can_data[0] = (logEntry.id >> 24);
815 can_data[1] = (logEntry.id >> 16);
816 can_data[2] = (logEntry.id >> 8);
817 can_data[3] = (logEntry.id >> 0);
818 can_data[4] = logEntry.length;
819 can_data[5] = 0;
820 can_data[6] = 0;
821 can_data[7] = 0;
823 if (logEntry.msgType == msg_tx_extended_e || logEntry.msgType == msg_rx_extended_e || (logEntry.id & CAN_EFF_MASK) > CAN_SFF_MASK)
824 can_data[0] |= 0x80;
826 memcpy(&can_data[8], logEntry.data, logEntry.length);
827 return true;
830 static bool
831 cllog_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, char **err_info, int64_t *data_offset)
833 *data_offset = file_tell(wth->fh);
835 return cllog_read_common(wth, wth->fh, rec, buf, err, err_info);
838 static bool
839 cllog_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, Buffer *buf, int *err, char **err_info)
841 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
842 return false;
844 return cllog_read_common(wth, wth->random_fh, rec, buf, err, err_info);
847 wtap_open_return_val
848 cllog_open(wtap *wth, int *err, char **err_info)
850 cCLLog_logFileInfo_t *clLog;
851 char line[ MAX_LOG_LINE_LENGTH ];
852 char *linep;
854 clLog = g_new0(cCLLog_logFileInfo_t, 1);
856 /* Initialize the header information */
857 clLog->loggerType = 0;
858 clLog->hwrev[0] = '\0';
859 clLog->fwrev[0] = '\0';
860 clLog->id[0] = '\0';
861 clLog->sessionNo = 0;
862 clLog->splitNo = 0;
863 clLog->logStartTime.epoch = 0;
864 clLog->logStartTime.ms = 0;
865 clLog->logStartTimeString[0] = '\0';
866 clLog->separator = '\0';
867 clLog->timeFormat = 0;
868 clLog->timeSeparator = '\0';
869 clLog->timeSeparatorMs = '\0';
870 clLog->dateSeparator = '\0';
871 clLog->dateAndTimeSeparator = '\0';
872 clLog->bitRate = 0;
873 clLog->silentMode = 0;
874 clLog->cyclicMode = 0;
876 /* Set parse function pointers */
877 memset(clLog->parseFieldFunc, 0, sizeof( clLog->parseFieldFunc));
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.
886 while ((linep = file_gets(line, sizeof(line), wth->fh)) != NULL &&
887 linep[0] == '#')
890 * Skip the comment character and white space following it.
892 linep++;
893 while (*linep == ' ' || *linep == '\t')
894 linep++;
896 if (*linep == '\0')
899 * Skip over empty comment lines.
900 * XXX - should we treat that as an indication of an
901 * invalid file?
903 continue;
907 * Look for the handler for this particular header line.
909 for (unsigned int i = 0U; i < HEADER_LINE_PARSE_MAPPING_LENGTH; i++)
911 const headerLineParseMapping_t *pHeaderMapping = &headerLineParseMapping[i];
912 size_t matchStringLen = strlen(pHeaderMapping->pMatchString);
914 if (strncmp(linep, pHeaderMapping->pMatchString, matchStringLen) == 0 &&
915 pHeaderMapping->parseFunc != NULL)
918 * This matches this header value.
919 * Skip past the tag.
921 linep += matchStringLen;
923 /* Replace any newline chars with end of line */
924 for (char *pChar = linep; ; pChar++)
926 if (*pChar == '\n' || *pChar == '\r' || *pChar == '\0')
928 *pChar = '\0';
929 break;
934 * Call the handler.
936 if (!pHeaderMapping->parseFunc(clLog, linep, err, err_info))
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.
945 g_free(clLog);
946 if (*err == WTAP_ERR_BAD_FILE)
948 wmem_free(NULL, *err_info);
949 *err_info = NULL;
951 return WTAP_OPEN_NOT_MINE;
958 * Did file_gets() fail?
960 if (linep == NULL)
963 * Yes - file_gets() didn't return a line.
964 * Did it get an error?
966 *err = file_error(wth->fh, err_info);
967 if (*err != 0)
969 /* Yes. What was it? */
970 if (*err == WTAP_ERR_SHORT_READ)
972 /* Incomplete header, so not ours. */
973 g_free(clLog);
974 return WTAP_OPEN_NOT_MINE;
976 else
978 /* I/O error. */
979 g_free(clLog);
980 return WTAP_OPEN_ERROR;
985 * No, it just got an EOF; treat it as our file, as
986 * older versions did so.
988 * XXX - should we treat it as not our file, as it lacks
989 * the column header line?
992 else
995 * We've read the first line after the header, so it's the column
996 * header line. Parse it.
998 if (!parseColumnHeaderFields(clLog, linep))
1000 g_free(clLog);
1001 return WTAP_OPEN_NOT_MINE;
1005 wth->priv = clLog;
1007 wth->file_type_subtype = cllog_file_type_subtype;
1008 wth->file_encap = WTAP_ENCAP_SOCKETCAN;
1009 wth->snapshot_length = 0;
1011 wth->subtype_read = cllog_read;
1012 wth->subtype_seek_read = cllog_seek_read;
1013 wth->file_tsprec = WTAP_TSPREC_MSEC;
1015 return WTAP_OPEN_MINE;
1018 /* Options for packet blocks. */
1019 static const struct supported_option_type packet_block_options_supported[] = {
1020 { OPT_PKT_FLAGS, ONE_OPTION_SUPPORTED },
1023 static const struct supported_block_type cllog_blocks_supported[] = {
1025 * We support packet blocks, with only the flags option supported.
1027 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(packet_block_options_supported) }
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
1036 void
1037 register_canlogger(void)
1039 cllog_file_type_subtype = wtap_register_file_type_subtype(&cllog_info);