dcerpc-netlogon: improve NetrLogonGetCapabilities dissection
[wireshark-sm.git] / wiretap / cllog.c
blobe52614307d367f8f9d3d6b0d4b6f7bdc16b42403
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 pLogEntry->length = length;
343 return true;
346 static bool parseFieldData(cCLLog_logFileInfo_t *pInfo _U_, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
348 char *pFieldStart = pField;
350 /* Set data length in case length field is not set explicitly in the log file */
351 pLogEntry->length = 0;
353 /* Loop all data bytes */
354 for (unsigned int dataByte = 0; dataByte < 8; dataByte++)
356 int hexdigit;
357 uint8_t data;
359 if (*pFieldStart == '\n' || *pFieldStart == '\r')
361 break;
364 hexdigit = ws_xton(*pFieldStart);
365 if (hexdigit < 0) {
366 *err = WTAP_ERR_BAD_FILE;
367 *err_info = g_strdup_printf("cllog: packet byte value is not valid");
368 return false;
370 data = (uint8_t)hexdigit << 4U;
371 pFieldStart++;
372 hexdigit = ws_xton(*pFieldStart);
373 if (hexdigit < 0) {
374 *err = WTAP_ERR_BAD_FILE;
375 *err_info = g_strdup("cllog: packet byte value is not valid");
376 return false;
378 data = data | (uint8_t)hexdigit;
379 pFieldStart++;
380 pLogEntry->data[dataByte] = data;
382 pLogEntry->length++;
384 return true;
387 static bool parseLogLine(cCLLog_logFileInfo_t *pInfo, char *pLine, cCLLog_message_t *pLogEntry, int *err, char **err_info)
389 char *pFieldStart = pLine;
391 /* Loop all fields in log line */
392 for (unsigned int fieldNo = 0, finalField = 0; fieldNo < MAX_LOG_LINE_FIELDS && finalField == 0; fieldNo++)
394 /* Find field end by separator */
395 char *pFieldEnd = strchr(pFieldStart, pInfo->separator);
397 /* If final field, then EOL marks the end of the field */
398 if (pFieldEnd == NULL)
400 pFieldEnd = strchr(pFieldStart, '\n');
401 finalField = 1;
404 /* Replace separator or terminator with string termination */
405 if (pFieldEnd != NULL)
407 *pFieldEnd = '\0';
410 /* Is parse function assigned to field? */
411 if (pInfo->parseFieldFunc[fieldNo] != NULL)
413 /* Parse field */
414 if (!pInfo->parseFieldFunc[fieldNo](pInfo, pFieldStart, pLogEntry, err, err_info))
416 return false;
420 /* Skip over the separator */
421 pFieldStart = pFieldEnd + 1;
423 return true;
426 /***********************************************************************************************************************
427 * parseColumnHeaderFields
429 * Parse the column fields and determine which fields are present and the position of the fields
431 * @param[ in ] pInfo Pointer to the CLLog object
432 * @param[ in ] pColLine The column line
433 **********************************************************************************************************************/
434 static bool parseColumnHeaderFields( cCLLog_logFileInfo_t *pInfo, char *pColLine )
436 bool resultFlag = false;
438 /* Initialise field start */
439 char *pFieldStart = pColLine;
441 /* Loop all fields in line */
442 for ( uint8_t fieldNo = 0, finalField = 0 ; fieldNo < MAX_LOG_LINE_FIELDS && finalField == 0 ; fieldNo++ )
444 /* Find field end */
445 char *pFieldEnd = strchr( pFieldStart, pInfo->separator );
447 /* If final field, then EOL marks the end of the field */
448 if( pFieldEnd == NULL )
450 pFieldEnd = strchr( pFieldStart, '\n' );
451 finalField = 1;
454 /* Replace separator or terminator with string termination */
455 if (pFieldEnd != NULL)
457 *pFieldEnd = '\0';
460 /* Set field number */
461 if( strcmp( pFieldStart, "Timestamp" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldTS; resultFlag = true; }
462 if( strcmp( pFieldStart, "Lost" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLost; resultFlag = true; }
463 if( strcmp( pFieldStart, "Type" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldMsgType; resultFlag = true; }
464 if( strcmp( pFieldStart, "ID" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldID; resultFlag = true; }
465 if( strcmp( pFieldStart, "Length" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLength; resultFlag = true; }
466 if( strcmp( pFieldStart, "Data" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldData; resultFlag = true; }
468 /* Set start of next field to end of previous + 1 */
469 pFieldStart = pFieldEnd + 1;
472 return resultFlag;
475 /***********************************************************************************************************************
476 * stripTimeStamp
478 * Strips a time stamp string for any delimiters
479 **********************************************************************************************************************/
480 static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t *pInfo, char *pTimeStampString )
482 uint8_t strippedLength = 0U;
484 /* Char by char, strip the delimiters from the time stamp string */
485 size_t timeStampStringLen = strlen( pTimeStampString );
486 for (size_t i = 0U; i < timeStampStringLen; i++ )
488 /* Get char */
489 char charTmp = pTimeStampString[i];
491 /* If delimiter, skip */
492 if( charTmp == pInfo->separator ){ continue; }
493 if( charTmp == pInfo->timeSeparator ){ continue; }
494 if( charTmp == pInfo->timeSeparatorMs ){ continue; }
495 if( charTmp == pInfo->dateSeparator ){ continue; }
496 if( charTmp == pInfo->dateAndTimeSeparator ){ continue; }
498 /* Not a delimiter, keep char */
499 pTimeStampString[ strippedLength++ ] = charTmp;
501 pTimeStampString[ strippedLength ] = '\0';
503 return strippedLength;
506 static bool parseString(const char *pFieldValue, char *valuep, size_t valueSize, char *fieldName, int *err, char **err_info)
508 if (!checked_strcpy(valuep, valueSize, pFieldValue))
510 *err = WTAP_ERR_BAD_FILE;
511 *err_info = ws_strdup_printf("cllog: %s is too long",
512 fieldName);
513 return false;
515 return true;
518 static bool parseUnsigned(const char *pFieldValue, uint32_t *valuep, char *fieldName, int *err, char **err_info)
520 uint32_t value;
522 if (!ws_strtou32(pFieldValue, NULL, &value)) {
523 *err = WTAP_ERR_BAD_FILE;
524 *err_info = ws_strdup_printf("cllog: %s value is not valid",
525 fieldName);
526 return false;
528 *valuep = value;
529 return true;
532 static bool parseSeparator(const char *pFieldValue, char *separatorp, char *fieldName, int *err, char **err_info)
534 char separator = '\0';
536 /* Separator field is if set e.g. ";" - that is 3 chars. Else it is "" */
537 if (strlen( pFieldValue) == 3)
539 if (pFieldValue[0] != '"' || !g_ascii_isprint(pFieldValue[1]) ||
540 pFieldValue[2] != '"')
542 *err = WTAP_ERR_BAD_FILE;
543 *err_info = ws_strdup_printf("cllog: %s separator is not valid",
544 fieldName);
545 return false;
547 separator = pFieldValue[1];
549 *separatorp = separator;
550 return true;
553 static bool parseBoolean(const char *pFieldValue, bool *value, char *fieldName, int *err, char **err_info)
555 if (strcmp(pFieldValue, "true") == 0)
557 *value = true;
559 else if (strcmp(pFieldValue, "false") == 0)
561 *value = false;
563 else
565 *err = WTAP_ERR_BAD_FILE;
566 *err_info = ws_strdup_printf("cllog: %s value is not valid",
567 fieldName);
568 return false;
570 return true;
573 static bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
575 if (strcmp(pFieldValue, "CANLogger1000") == 0 )
577 pInfo->loggerType = type_CL1000_e;
579 else if (strcmp(pFieldValue, "CANLogger2000") == 0)
581 pInfo->loggerType = type_CL2000_e;
583 else if (strcmp(pFieldValue, "CANLogger3000") == 0 )
585 pInfo->loggerType = type_CL3000_e;
587 else
589 *err = WTAP_ERR_BAD_FILE;
590 *err_info = g_strdup("cllog: logger type value is not valid");
591 return false;
593 return true;
596 static bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
598 return parseString(pFieldValue, pInfo->hwrev, sizeof pInfo->hwrev, "hardware revision", err, err_info);
601 static bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
603 return parseString(pFieldValue, pInfo->fwrev, sizeof pInfo->fwrev, "firmware revision", err, err_info);
606 static bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
608 return parseString(pFieldValue, pInfo->id, sizeof pInfo->id, "ID", err, err_info);
611 static bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
613 return parseUnsigned(pFieldValue, &pInfo->sessionNo, "session number", err, err_info);
616 static bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
618 return parseUnsigned(pFieldValue, &pInfo->splitNo, "split number", err, err_info);
621 static bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
623 struct tm tm;
625 memset(&tm, 0, sizeof tm);
626 /* YYYYMMDDThhmmss */
627 sscanf(pFieldValue,
628 "%4u%2u%2uT%2u%2u%2u",
629 &tm.tm_year,
630 &tm.tm_mon,
631 &tm.tm_mday,
632 &tm.tm_hour,
633 &tm.tm_min,
634 &tm.tm_sec);
635 tm.tm_mon -= 1;
636 tm.tm_year -= 1900;
638 /* To Epoch ( mktime converts to epoch from local (!!!) timezone )*/
639 pInfo->logStartTime.epoch = mktime(&tm);
640 pInfo->logStartTime.ms = 0;
642 if (!checked_strcpy(pInfo->logStartTimeString, sizeof pInfo->logStartTimeString, pFieldValue))
644 *err = WTAP_ERR_BAD_FILE;
645 *err_info = g_strdup("cllog: time is too long");
646 return false;
648 return true;
651 static bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
653 return parseSeparator(pFieldValue, &pInfo->separator, "value", err, err_info);
656 static bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
658 uint32_t format;
660 if (!ws_strtou32(pFieldValue, NULL, &format))
662 *err = WTAP_ERR_BAD_FILE;
663 *err_info = g_strdup("cllog: time format value is not valid");
664 return false;
666 if (format > 6)
668 *err = WTAP_ERR_BAD_FILE;
669 *err_info = g_strdup("cllog: time format value is not valid");
670 return false;
672 pInfo->timeFormat = (uint8_t)format;
673 return true;
676 static bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
678 return parseSeparator(pFieldValue, &pInfo->timeSeparator, "time", err, err_info);
681 static bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
683 return parseSeparator(pFieldValue, &pInfo->timeSeparatorMs, "time millisecond", err, err_info);
686 static bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
688 return parseSeparator(pFieldValue, &pInfo->dateSeparator, "date", err, err_info);
691 static bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
693 return parseSeparator(pFieldValue, &pInfo->dateAndTimeSeparator, "date and time", err, err_info);
696 static bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
698 return parseUnsigned(pFieldValue, &pInfo->bitRate, "bit rate", err, err_info);
701 static bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
703 bool silentMode;
705 if (!parseBoolean(pFieldValue, &silentMode, "silent mode", err, err_info))
707 return false;
710 if (silentMode)
712 pInfo->silentMode = silent_enabled_e;
714 else
716 pInfo->silentMode = silent_disabled_e;
718 return true;
721 static bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
723 bool cyclicMode;
725 if (!parseBoolean(pFieldValue, &cyclicMode, "silent mode", err, err_info))
727 return false;
730 if (cyclicMode)
732 pInfo->cyclicMode = cyclic_enabled_e;
734 else
736 pInfo->cyclicMode = cyclic_disabled_e;
738 return true;
743 c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(248): warning C4
744 477: 'sscanf' : format string '%i' requires an argument of type 'int *',
745 but variadic argument 1 has type 'uint8_t *'
746 c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(274): warning C4
747 477: 'sscanf' : format string '%i' requires an argument of type 'int *',
748 but variadic argument 1 has type 'uint8_t *'
749 c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(288): warning C4
750 477: 'sscanf' : format string '%2x' requires an argument of type 'unsign
751 ed int *', but variadic argument 1 has type 'uint8_t *
756 #include "cllog.h"
758 static int cllog_file_type_subtype = -1;
760 #define CAN_EFF_MASK 0x1FFFFFFF /* extended frame format (EFF) */
761 #define CAN_SFF_MASK 0x000007FF /* standard frame format (SFF) */
763 static bool
764 cllog_read_common(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, char **err_info)
766 cCLLog_logFileInfo_t *clLog = (cCLLog_logFileInfo_t *) wth->priv;
767 char line[MAX_LOG_LINE_LENGTH];
768 cCLLog_message_t logEntry;
769 uint8_t *can_data;
771 /* Read a line */
772 if (file_gets(line, sizeof(line), fh) == NULL)
774 /* EOF or error. */
775 *err = file_error(wth->fh, err_info);
776 return false;
779 /* Default the log entry structure */
780 memset(&logEntry, 0, sizeof(logEntry));
782 /* Parse the line */
783 if (!parseLogLine(clLog, line, &logEntry, err, err_info))
785 return false;
788 rec->rec_type = REC_TYPE_PACKET;
789 rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
790 rec->presence_flags = WTAP_HAS_TS;
792 rec->ts.secs = logEntry.timestamp.epoch;
793 rec->ts.nsecs = logEntry.timestamp.ms * 1000U * 1000U;
795 rec->rec_header.packet_header.caplen = 8 + logEntry.length;
796 rec->rec_header.packet_header.len = 8 + logEntry.length;
798 if (logEntry.msgType == msg_tx_standard_e || logEntry.msgType == msg_tx_extended_e)
800 wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, PACK_FLAGS_DIRECTION_OUTBOUND);
802 else if (logEntry.msgType == msg_rx_standard_e || logEntry.msgType == msg_rx_extended_e)
804 wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, PACK_FLAGS_DIRECTION_INBOUND);
807 ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
808 can_data = ws_buffer_start_ptr(buf);
810 can_data[0] = (logEntry.id >> 24);
811 can_data[1] = (logEntry.id >> 16);
812 can_data[2] = (logEntry.id >> 8);
813 can_data[3] = (logEntry.id >> 0);
814 can_data[4] = logEntry.length;
815 can_data[5] = 0;
816 can_data[6] = 0;
817 can_data[7] = 0;
819 if (logEntry.msgType == msg_tx_extended_e || logEntry.msgType == msg_rx_extended_e || (logEntry.id & CAN_EFF_MASK) > CAN_SFF_MASK)
820 can_data[0] |= 0x80;
822 memcpy(&can_data[8], logEntry.data, logEntry.length);
823 return true;
826 static bool
827 cllog_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, char **err_info, int64_t *data_offset)
829 *data_offset = file_tell(wth->fh);
831 return cllog_read_common(wth, wth->fh, rec, buf, err, err_info);
834 static bool
835 cllog_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, Buffer *buf, int *err, char **err_info)
837 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
838 return false;
840 return cllog_read_common(wth, wth->random_fh, rec, buf, err, err_info);
843 wtap_open_return_val
844 cllog_open(wtap *wth, int *err, char **err_info)
846 cCLLog_logFileInfo_t *clLog;
847 char line[ MAX_LOG_LINE_LENGTH ];
848 char *linep;
850 clLog = g_new0(cCLLog_logFileInfo_t, 1);
852 /* Initialize the header information */
853 clLog->loggerType = 0;
854 clLog->hwrev[0] = '\0';
855 clLog->fwrev[0] = '\0';
856 clLog->id[0] = '\0';
857 clLog->sessionNo = 0;
858 clLog->splitNo = 0;
859 clLog->logStartTime.epoch = 0;
860 clLog->logStartTime.ms = 0;
861 clLog->logStartTimeString[0] = '\0';
862 clLog->separator = '\0';
863 clLog->timeFormat = 0;
864 clLog->timeSeparator = '\0';
865 clLog->timeSeparatorMs = '\0';
866 clLog->dateSeparator = '\0';
867 clLog->dateAndTimeSeparator = '\0';
868 clLog->bitRate = 0;
869 clLog->silentMode = 0;
870 clLog->cyclicMode = 0;
872 /* Set parse function pointers */
873 memset(clLog->parseFieldFunc, 0, sizeof( clLog->parseFieldFunc));
876 * We're at the beginning of the file. The header is a set
877 * of comment lines, each beginning with a '#'. Read each line,
878 * stopping if we see a non-comment line, and parse each
879 * comment line; if any aren't valid, quit and indicate that
880 * this isn't a CLX log file.
882 while ((linep = file_gets(line, sizeof(line), wth->fh)) != NULL &&
883 linep[0] == '#')
886 * Skip the comment character and white space following it.
888 linep++;
889 while (*linep == ' ' || *linep == '\t')
890 linep++;
892 if (*linep == '\0')
895 * Skip over empty comment lines.
896 * XXX - should we treat that as an indication of an
897 * invalid file?
899 continue;
903 * Look for the handler for this particular header line.
905 for (unsigned int i = 0U; i < HEADER_LINE_PARSE_MAPPING_LENGTH; i++)
907 const headerLineParseMapping_t *pHeaderMapping = &headerLineParseMapping[i];
908 size_t matchStringLen = strlen(pHeaderMapping->pMatchString);
910 if (strncmp(linep, pHeaderMapping->pMatchString, matchStringLen) == 0 &&
911 pHeaderMapping->parseFunc != NULL)
914 * This matches this header value.
915 * Skip past the tag.
917 linep += matchStringLen;
919 /* Replace any newline chars with end of line */
920 for (char *pChar = linep; ; pChar++)
922 if (*pChar == '\n' || *pChar == '\r' || *pChar == '\0')
924 *pChar = '\0';
925 break;
930 * Call the handler.
932 if (!pHeaderMapping->parseFunc(clLog, linep, err, err_info))
935 * XXX - should this file be rejected as not
936 * one of ours? Given the line looks like
937 * a comment that begins with a valid header
938 * field tag, it may be likely to be one of
939 * ours.
941 g_free(clLog);
942 if (*err == WTAP_ERR_BAD_FILE)
944 wmem_free(NULL, *err_info);
945 *err_info = NULL;
947 return WTAP_OPEN_NOT_MINE;
954 * Did file_gets() fail?
956 if (linep == NULL)
959 * Yes - file_gets() didn't return a line.
960 * Did it get an error?
962 *err = file_error(wth->fh, err_info);
963 if (*err != 0)
965 /* Yes. What was it? */
966 if (*err == WTAP_ERR_SHORT_READ)
968 /* Incomplete header, so not ours. */
969 g_free(clLog);
970 return WTAP_OPEN_NOT_MINE;
972 else
974 /* I/O error. */
975 g_free(clLog);
976 return WTAP_OPEN_ERROR;
981 * No, it just got an EOF; treat it as our file, as
982 * older versions did so.
984 * XXX - should we treat it as not our file, as it lacks
985 * the column header line?
988 else
991 * We've read the first line after the header, so it's the column
992 * header line. Parse it.
994 if (!parseColumnHeaderFields(clLog, linep))
996 g_free(clLog);
997 return WTAP_OPEN_NOT_MINE;
1001 wth->priv = clLog;
1003 wth->file_type_subtype = cllog_file_type_subtype;
1004 wth->file_encap = WTAP_ENCAP_SOCKETCAN;
1005 wth->snapshot_length = 0;
1007 wth->subtype_read = cllog_read;
1008 wth->subtype_seek_read = cllog_seek_read;
1009 wth->file_tsprec = WTAP_TSPREC_MSEC;
1011 return WTAP_OPEN_MINE;
1014 /* Options for packet blocks. */
1015 static const struct supported_option_type packet_block_options_supported[] = {
1016 { OPT_PKT_FLAGS, ONE_OPTION_SUPPORTED },
1019 static const struct supported_block_type cllog_blocks_supported[] = {
1021 * We support packet blocks, with only the flags option supported.
1023 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(packet_block_options_supported) }
1026 static const struct file_type_subtype_info cllog_info = {
1027 "CSS Electronics CLX000 CAN log", "cllog", "txt", NULL,
1028 false, BLOCKS_SUPPORTED(cllog_blocks_supported),
1029 NULL, NULL, NULL
1032 void
1033 register_canlogger(void)
1035 cllog_file_type_subtype = wtap_register_file_type_subtype(&cllog_info);