Bug Summary

File:wiretap/peak-trc.c
Warning:line 800, column 9
Attempt to free released memory

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 peak-trc.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-19/lib/clang/19 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /usr/include/libxml2 -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-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../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-truncation -Wno-format-nonliteral -Wno-pointer-sign -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2025-07-10-100258-3847-1 -x c /builds/wireshark/wireshark/wiretap/peak-trc.c
1/* peak_trc.c
2 *
3 * Wiretap Library
4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
5 *
6 * Support for TRC log file format generated by several CAN tools from PEAK Gmbh
7 * Copyright (c) 2024 by Miklos Marton <martonmiklosqdev@gmail.com>
8 *
9 * File format description:
10 * https://www.peak-system.com/produktcd/Pdf/English/PEAK_CAN_TRC_File_Format.pdf
11 *
12 * SPDX-License-Identifier: GPL-2.0-or-later
13 */
14
15#define WS_LOG_DOMAIN"peak-trc" "peak-trc"
16#include <config.h>
17#include <wireshark.h>
18#include <file_wrappers.h>
19#include <epan/dissectors/packet-socketcan.h>
20#include <wctype.h>
21#include <string.h>
22#include <stdlib.h>
23#include <errno(*__errno_location ()).h>
24#include <time.h>
25#include "peak-trc.h"
26#include "socketcan.h"
27
28#define PEAK_TRC_MAX_LINE_SIZE4096 4096 // J1939 logs could contain long lines
29
30typedef enum {
31 FileVersion,
32 StartTime,
33 ColumnHeader,
34 Comment,
35 Data
36} peak_trc_parse_state_t;
37
38typedef enum {
39 Col_BusNumber, // 0
40 Col_BusNumber_v1, // 1
41 Col_Direction, // 2
42 Col_CanId, // 3
43 Col_DLC, // 4
44 Col_MessageNumber, // 5
45 Col_TimeOffset_v1_0, // 6
46 Col_TimeOffset, // 7
47 Col_Reserved, // 8
48 Col_MessageType, // 9
49 Col_ErrorOrRtr_v1p0, // 10
50 // keep it to the last one to get the
51 Col_Data_v1_0, // 11
52 Col_Data, // 12
53 Col_Invalid // 13
54} peak_trc_column_type_t;
55
56typedef struct {
57 peak_trc_column_type_t type;
58 char code;
59 char* regex;
60} peak_trc_column_map_t;
61
62typedef struct {
63 time_t trace_start;
64 time_t trace_start_nsec;
65
66 uint8_t column_positions[Col_Invalid];
67
68 uint8_t file_version_major, file_version_minor;
69 GRegex* data_matcher;
70} peak_trc_state_t;
71
72static int peak_trc_file_type_subtype = -1;
73
74static peak_trc_column_map_t colmap[] = {
75 {Col_BusNumber, 'B', "\\s*(([0-9]*)|-)"},
76 /* This isn't a real column type, so give it a "type" that shouldn't be used */
77 {Col_BusNumber_v1, 1, "\\s*([0-9]*)"},
78 {Col_Direction, 'd', "\\s*(Rx|Tx|Warng|Error)"},
79 /* This isn't a real column type, so give it a "type" that shouldn't be used */
80 {Col_Data_v1_0, 2, "\\s?(((--|[0-9A-F]{2})\\s?)*)"},
81 {Col_Data, 'D', "\\s?((((--|[0-9A-F]{2})\\s?))*)"},
82 {Col_CanId, 'I', "\\s*([0-9A-F]*)"},
83 {Col_DLC, 'l', "\\s*([0-9]*)"},
84 {Col_DLC, 'L', "\\s*([0-9]*)"},
85 {Col_MessageNumber, 'N', "\\s*([0-9]*)\\)"},
86 {Col_TimeOffset, 'O', "\\s*([0-9]*\\.[0-9]*)"},
87 /* This isn't a real column type, so give it a "type" that shouldn't be used */
88 {Col_TimeOffset_v1_0, 3, "\\s*([0-9]*)"},
89 {Col_Reserved, 'R', "\\s*(-)"},
90 {Col_MessageType, 'T', "\\s*([A-Z]{2})"},
91 /* This isn't a real column, so give it a "type" that shouldn't be used */
92 {Col_ErrorOrRtr_v1p0, 4, "\\s*(ERROR|RTR)?"},
93 {Col_Invalid, 0, ""},
94 };
95
96void register_peak_trc(void);
97
98static wtap_open_return_val
99peak_trc_parse(wtap* wth, peak_trc_state_t* state, gint64* offset, int* err, char** err_info)
100{
101 gint64 seek_off = 0;
102 bool_Bool found_timestamp = false0;
103 char line_buffer[PEAK_TRC_MAX_LINE_SIZE4096];
104
105 /* 1.0 file format does not have version info or column line,
106 * so assume that until told otherwise
107 */
108 int major = 1;
109 int minor = 0;
110
111 ws_debug("%s: Trying peak_trc file decoder\n", G_STRFUNC)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c"
, 111, __func__, "%s: Trying peak_trc file decoder\n", ((const
char*) (__func__))); } } while (0)
;
112
113 /* Initial start time until we find it in the header */
114 state->trace_start = time(NULL((void*)0));
115 state->trace_start_nsec = 0;
116
117 state->file_version_major = 1;
118 state->file_version_minor = 0;
119
120 /* Initialize columns to "invalid" */
121 for (int i = 0; i < Col_Invalid; i++)
122 state->column_positions[i] = Col_Invalid;
123
124 /* Bail if the end of the file is here and it hasn't been determined it's a PEAK file */
125 while (!file_eof(wth->fh))
126 {
127 seek_off = file_tell(wth->fh);
128
129 ws_debug("%s: Starting parser at offset %" PRIi64 "\n", G_STRFUNC, seek_off)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c"
, 129, __func__, "%s: Starting parser at offset %" "l" "i" "\n"
, ((const char*) (__func__)), seek_off); } } while (0)
;
130
131 if (file_gets(line_buffer, PEAK_TRC_MAX_LINE_SIZE4096, wth->fh) == NULL((void*)0))
132 {
133 /* Error reading file, bail out */
134 *err = file_error(wth->fh, err_info);
135 if (*err != 0 && *err != WTAP_ERR_SHORT_READ-12)
136 return WTAP_OPEN_ERROR;
137
138 return WTAP_OPEN_NOT_MINE;
139 }
140
141 g_strstrip(line_buffer)g_strchomp (g_strchug (line_buffer));
142 size_t line_len = strlen(line_buffer);
143
144 /* Ignore empty lines */
145 if (line_len == 0)
146 continue;
147
148 /* The intention here is to get through all of the "header" which consists of
149 * "commented" lines (notated with a ;)
150 * Once the header is complete, parsing can be confident it's a PEAK file
151 */
152 if (line_buffer[0] != ';')
153 {
154 /* Two possibilities:
155 * 1. This is the start of the actual packet data and header parsing is complete
156 * 2. This isn't a PEAK file
157 *
158 * Determine this by if the timestamp has been found
159 */
160 if (!found_timestamp)
161 return WTAP_OPEN_NOT_MINE;
162
163 /* Valid PEAK file */
164 GString* regex_str = g_string_new(NULL((void*)0));
165
166 *offset = seek_off;
167
168 /* Save off the data found in the header */
169 state->file_version_major = (uint8_t)major;
170 state->file_version_minor = (uint8_t)minor;
171
172 /* File version 1.x doesn't explicitly provide column order information */
173 if (state->file_version_major == 1)
174 {
175 state->column_positions[Col_MessageNumber] = 1;
176
177 switch(state->file_version_minor)
178 {
179 case 0:
180 state->column_positions[Col_TimeOffset_v1_0] = 2;
181 state->column_positions[Col_CanId] = 3;
182 state->column_positions[Col_DLC] = 4;
183 state->column_positions[Col_ErrorOrRtr_v1p0] = 5;
184 state->column_positions[Col_Data_v1_0] = 6;
185 break;
186 case 1:
187 state->column_positions[Col_TimeOffset] = 2;
188 state->column_positions[Col_Direction] = 3;
189 state->column_positions[Col_CanId] = 4;
190 state->column_positions[Col_DLC] = 5;
191 state->column_positions[Col_ErrorOrRtr_v1p0] = 6;
192 state->column_positions[Col_Data] = 7;
193 break;
194 case 2:
195 state->column_positions[Col_TimeOffset] = 2;
196 state->column_positions[Col_BusNumber_v1] = 3;
197 state->column_positions[Col_Direction] = 4;
198 state->column_positions[Col_CanId] = 5;
199 state->column_positions[Col_DLC] = 6;
200 state->column_positions[Col_ErrorOrRtr_v1p0] = 7;
201 state->column_positions[Col_Data] = 8;
202 break;
203 default:
204 state->column_positions[Col_TimeOffset] = 2;
205 state->column_positions[Col_BusNumber_v1] = 3;
206 state->column_positions[Col_Direction] = 4;
207 state->column_positions[Col_CanId] = 5;
208 state->column_positions[Col_Reserved] = 6;
209 state->column_positions[Col_DLC] = 7;
210 state->column_positions[Col_ErrorOrRtr_v1p0] = 8;
211 state->column_positions[Col_Data] = 9;
212 break;
213 }
214
215 for (int column_index = 0; column_index < Col_Invalid; column_index++)
216 {
217 for (int col_type = Col_BusNumber; col_type < Col_Invalid; col_type++)
218 {
219 if (state->column_positions[col_type] == column_index)
220 {
221 for (peak_trc_column_map_t* item = colmap; item->type != Col_Invalid; item++)
222 {
223 if ((int)item->type == col_type)
224 {
225 g_string_append(regex_str, item->regex)(__builtin_constant_p (item->regex) ? __extension__ ({ const
char * const __val = (item->regex); g_string_append_len_inline
(regex_str, __val, (__val != ((void*)0)) ? (gssize) strlen (
((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(regex_str, item->regex, (gssize) -1))
;
226 break;
227 }
228 }
229 }
230 }
231 }
232
233 state->data_matcher = g_regex_new(regex_str->str, (GRegexCompileFlags)(G_REGEX_OPTIMIZE | G_REGEX_RAW), (GRegexMatchFlags)0, NULL((void*)0));
234 g_string_free(regex_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) (
(regex_str), ((!(0)))) : g_string_free_and_steal (regex_str))
: (g_string_free) ((regex_str), ((!(0)))))
;
235 }
236
237 return WTAP_OPEN_MINE;
238 }
239
240 if (g_str_has_prefix(line_buffer, ";$FILEVERSION=")(__builtin_constant_p (";$FILEVERSION=")? __extension__ ({ const
char * const __str = (line_buffer); const char * const __prefix
= (";$FILEVERSION="); gboolean __result = (0); if (__str == (
(void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix
) (__str, __prefix); else { const size_t __str_len = strlen (
((__str) + !(__str))); const size_t __prefix_len = strlen (((
__prefix) + !(__prefix))); if (__str_len >= __prefix_len) __result
= memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len
) == 0; } __result; }) : (g_str_has_prefix) (line_buffer, ";$FILEVERSION="
) )
)
241 {
242 if (sscanf(line_buffer, ";$FILEVERSION=%d.%d\r\n", &major, &minor) != 2)
243 return WTAP_OPEN_NOT_MINE;
244 }
245 else if (g_str_has_prefix(line_buffer, ";$STARTTIME=")(__builtin_constant_p (";$STARTTIME=")? __extension__ ({ const
char * const __str = (line_buffer); const char * const __prefix
= (";$STARTTIME="); gboolean __result = (0); if (__str == ((
void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix
) (__str, __prefix); else { const size_t __str_len = strlen (
((__str) + !(__str))); const size_t __prefix_len = strlen (((
__prefix) + !(__prefix))); if (__str_len >= __prefix_len) __result
= memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len
) == 0; } __result; }) : (g_str_has_prefix) (line_buffer, ";$STARTTIME="
) )
)
246 {
247 /* $STARTTIME keyword to store the absolute start time of the trace file:
248 Format: Floating point, decimal separator is a point.
249 Value: Integral part = Number of days that have passed since 30. December 1899.
250 Fractional Part = Fraction of a 24-hour day that has elapsed, resolution is 1 millisecond. */
251
252 gchar** parts = g_strsplit(line_buffer, "=", 2);
253 if (parts[1] == NULL((void*)0))
254 {
255 /* At this point, the file is probably a PEAK file that is poorly formatted, so bail with an error */
256 *err_info = ws_strdup("peak_trc: unable to parse start time")wmem_strdup(((void*)0), "peak_trc: unable to parse start time"
)
;
257 g_strfreev(parts);
258 return WTAP_OPEN_ERROR;
259 }
260
261 /* Convert to a more "standard" timestamp */
262 double days_since_epoch = g_strtod(parts[1], NULL((void*)0));
263 days_since_epoch -= 25569.0;
264 time_t ts = 0;
265 struct tm* t = gmtime(&ts);
266 t->tm_sec += (int)(days_since_epoch * 24 * 3600);
267 state->trace_start = mktime(t);
268 state->trace_start_nsec = 0;
269
270 found_timestamp = true1;
271
272 g_strfreev(parts);
273 }
274 else if (g_str_has_prefix(line_buffer, ";$COLUMNS=")(__builtin_constant_p (";$COLUMNS=")? __extension__ ({ const char
* const __str = (line_buffer); const char * const __prefix =
(";$COLUMNS="); gboolean __result = (0); if (__str == ((void
*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix)
(__str, __prefix); else { const size_t __str_len = strlen ((
(__str) + !(__str))); const size_t __prefix_len = strlen (((__prefix
) + !(__prefix))); if (__str_len >= __prefix_len) __result
= memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len
) == 0; } __result; }) : (g_str_has_prefix) (line_buffer, ";$COLUMNS="
) )
)
275 {
276 gchar** parts = g_strsplit(line_buffer, "=", 2);
277 if (parts[1] == NULL((void*)0))
278 {
279 /* At this point, the file is probably a PEAK file that is poorly formatted, so bail with an error */
280 *err_info = ws_strdup("peak_trc: unable to parse column definitions")wmem_strdup(((void*)0), "peak_trc: unable to parse column definitions"
)
;
281 g_strfreev(parts);
282 return WTAP_OPEN_ERROR;
283 }
284
285 gchar** columns = g_strsplit(parts[1], ",", 0);
286 int column_index = 0;
287 for (gchar** iter = columns; *iter != NULL((void*)0); iter++)
288 {
289 size_t col_length = strlen(*iter);
290 if (col_length > 1) {
291 *err_info = ws_strdup_printf("peak_trc: unknown column definition in the $COLUMNS line: '%s'", *iter)wmem_strdup_printf(((void*)0), "peak_trc: unknown column definition in the $COLUMNS line: '%s'"
, *iter)
;
292 g_strfreev(columns);
293 g_strfreev(parts);
294 return WTAP_OPEN_ERROR;
295 }
296 if (col_length == 0)
297 {
298 //Done ?
299 break;
300 }
301
302 /* Assign the column value if found */
303 for (peak_trc_column_map_t* item = colmap; item->type != Col_Invalid; item++)
304 {
305 if ((*iter)[0] == item->code)
306 {
307 // column type matched
308 state->column_positions[item->type] = column_index;
309 break;
310 }
311 }
312 column_index++;
313 }
314
315 g_strfreev(columns);
316 g_strfreev(parts);
317 }
318 else if (line_len >= 2)
319 {
320 /* The rest of the "keywords" are separated by whitespace after the initial ';' */
321 char* keyword = &line_buffer[1];
322 while (iswspace(*keyword))
323 keyword++;
324
325 if (g_str_has_prefix(keyword, "Start time:")(__builtin_constant_p ("Start time:")? __extension__ ({ const
char * const __str = (keyword); const char * const __prefix =
("Start time:"); gboolean __result = (0); if (__str == ((void
*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix)
(__str, __prefix); else { const size_t __str_len = strlen ((
(__str) + !(__str))); const size_t __prefix_len = strlen (((__prefix
) + !(__prefix))); if (__str_len >= __prefix_len) __result
= memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len
) == 0; } __result; }) : (g_str_has_prefix) (keyword, "Start time:"
) )
)
326 {
327 /* Use the $STARTTIME if it has already been found */
328 if (!found_timestamp)
329 {
330 int day, month, year, hour, minute, second, millisecond;
331 if (sscanf(keyword, "Start time: %d.%d.%d %d:%d:%d.%d", &month, &day, &year, &hour, &minute, &second, &millisecond) != 7)
332 {
333 *err_info = ws_strdup("peak_trc: unable to parse start time")wmem_strdup(((void*)0), "peak_trc: unable to parse start time"
)
;
334 return WTAP_OPEN_ERROR;
335 }
336
337 struct tm t;
338 t.tm_sec = second;
339 t.tm_min = minute;
340 t.tm_hour = hour;
341 t.tm_mday = day;
342 t.tm_mon = month-1;
343 t.tm_year = year-1900;
344 t.tm_wday = 0;
345 t.tm_yday = 0;
346 t.tm_isdst = -1;
347
348 state->trace_start = mktime(&t);
349 state->trace_start_nsec = millisecond*1000000;
350
351 found_timestamp = true1;
352 }
353 }
354 }
355 }
356
357 return WTAP_OPEN_NOT_MINE;
358}
359
360static bool_Bool peak_trc_read_packet_v1(wtap* wth, peak_trc_state_t* state, wtap_can_msg_t* peak_msg, char* line_buffer)
361{
362 // pre 2.0 packets use regexp with fixed column order with a regexp
363 GMatchInfo* match_info = NULL((void*)0);
364 bool_Bool found = g_regex_match(state->data_matcher, line_buffer, 0, &match_info);
365 int column_count = g_match_info_get_match_count(match_info);
366 if (!found || column_count < 5)
367 {
368 /* Not a valid v1 packet */
369 g_match_info_free(match_info);
370 return false0;
371 }
372
373 for (int col = Col_BusNumber; col < Col_Invalid; col++)
374 {
375 if (state->column_positions[col] >= column_count || state->column_positions[col] == Col_Invalid)
376 continue;
377
378 gchar* column_text = g_match_info_fetch(match_info, state->column_positions[col]);
379 if (column_text == NULL((void*)0))
380 {
381 g_match_info_free(match_info);
382 return false0;
383 }
384
385 switch (col)
386 {
387 case Col_Direction:
388 if (strcmp(column_text, "Warng") == 0)
389 {
390 // Warning lines cannot be interpreted in Wireshark needs to be skipped
391 g_free(column_text);
392 g_match_info_free(match_info);
393 return false0;
394 }
395 if (strcmp(column_text, "Error") == 0)
396 {
397 peak_msg->type = MSG_TYPE_ERR;
398 }
399 break;
400 case Col_CanId:
401 peak_msg->id = (guint32)g_ascii_strtoull(column_text, NULL((void*)0), 16);
402 break;
403 case Col_ErrorOrRtr_v1p0:
404 if (state->file_version_minor == 1 && strcmp(column_text, "RTR") == 0)
405 {
406 peak_msg->type = MSG_TYPE_STD_RTR;
407 }
408 break;
409 case Col_TimeOffset:
410 case Col_TimeOffset_v1_0:
411 {
412 double tsd = g_ascii_strtod(column_text, NULL((void*)0)) / 1000.0; // parse to seconds
413 tsd += state->trace_start;
414 peak_msg->ts.secs = (guint64)tsd;
415 peak_msg->ts.nsecs = (int)((tsd - (guint64)tsd) * 1000000000);
416 }
417 break;
418 case Col_DLC:
419 peak_msg->data.length = (uint8_t)g_ascii_strtoull(column_text, NULL((void*)0), 10);
420 break;
421 case Col_BusNumber_v1:
422 {
423 char* interface_name = wmem_strdup_printf(NULL((void*)0), "interface%s", column_text);
424 peak_msg->interface_id = wtap_socketcan_find_or_create_new_interface(wth, interface_name);
425 wmem_free(NULL((void*)0), interface_name);
426 break;
427 }
428
429 case Col_Data:
430 case Col_Data_v1_0:
431 {
432 gchar* err_or_rtr = g_match_info_fetch(match_info, state->column_positions[Col_ErrorOrRtr_v1p0]);
433 gchar** bytes = g_strsplit(g_strstrip(column_text)g_strchomp (g_strchug (column_text)), " ", CAN_MAX_DLEN8);
434 // 1.0 format has an ERROR prefix in the data bytes, 1.1 has a separate columns for message type
435 if ((g_str_has_prefix(err_or_rtr, "ERROR")(__builtin_constant_p ("ERROR")? __extension__ ({ const char *
const __str = (err_or_rtr); const char * const __prefix = ("ERROR"
); gboolean __result = (0); if (__str == ((void*)0) || __prefix
== ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix
); else { const size_t __str_len = strlen (((__str) + !(__str
))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix
))); if (__str_len >= __prefix_len) __result = memcmp (((__str
) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0
; } __result; }) : (g_str_has_prefix) (err_or_rtr, "ERROR") )
) ||
436 (state->file_version_minor == 1 && peak_msg->type == MSG_TYPE_ERR))
437 {
438 peak_msg->type = MSG_TYPE_ERR;
439
440 /* For LINUX_CAN_ERR the length has to be CAN_MAX_DLEN */
441 peak_msg->data.length = CAN_MAX_DLEN8;
442 /* Clear the data of the message to populate it with SocketCAN meta data */
443 memset(peak_msg->data.data, 0, CAN_MAX_DLEN8);
444
445 guint32 old_id = peak_msg->id;
446 peak_msg->id = 0;
447 // ID: Type of Error Frame
448 switch (old_id)
449 {
450 case 1: // 1 = Bit Error
451 peak_msg->id |= CAN_ERR_PROT_BIT0x01;
452 break;
453 case 2: // 2 = Form Error
454 peak_msg->id |= CAN_ERR_PROT_FORM0x02;
455 break;
456 case 4: // 4 = Stuff Error
457 peak_msg->id |= CAN_ERR_PROT_STUFF0x04;
458 break;
459 }
460
461 //Translate PEAK error data into SocketCAN meta data for supported error types
462 if ((peak_msg->id | (CAN_ERR_PROT_FORM0x02& CAN_ERR_PROT_STUFF0x04)) != 0)
463 {
464 // Data Byte 0: Direction
465 uint8_t byte0 = (uint8_t)g_ascii_strtoull(bytes[0], NULL((void*)0), 16);
466 if (byte0 < 2)
467 {
468 //0 = Error occurred while sending
469 //1 = Error occurred while receiving.
470 peak_msg->data.data[2] |= CAN_ERR_PROT_TX0x80;
471 }
472
473 // Data Byte 1: Current Position in Bit Stream
474 uint8_t bit_pos = (uint8_t)g_ascii_strtoull(bytes[1], NULL((void*)0), 16);
475 if (bit_pos == 28)
476 {
477 peak_msg->data.data[2] |= CAN_ERR_PROT_OVERLOAD0x20;
478 }
479 else
480 {
481 // magically SocketCAN and PEAK error bit positions are identical with some exceptions
482 peak_msg->data.data[3] = bit_pos;
483 if (peak_msg->data.data[3] == 23)
484 peak_msg->data.data[3] = 0; // Error position not present in SocketCAN
485 }
486 }
487 }
488 else if (g_str_has_prefix(err_or_rtr, "RTR")(__builtin_constant_p ("RTR")? __extension__ ({ const char * const
__str = (err_or_rtr); const char * const __prefix = ("RTR");
gboolean __result = (0); if (__str == ((void*)0) || __prefix
== ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix
); else { const size_t __str_len = strlen (((__str) + !(__str
))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix
))); if (__str_len >= __prefix_len) __result = memcmp (((__str
) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0
; } __result; }) : (g_str_has_prefix) (err_or_rtr, "RTR") )
|| (state->file_version_minor == 1 && peak_msg->type == MSG_TYPE_STD_RTR))
489 {
490 peak_msg->type = MSG_TYPE_STD_RTR;
491 peak_msg->data.length = 0;
492 }
493 else
494 {
495 peak_msg->type = MSG_TYPE_STD;
496 if (bytes)
497 {
498 for (int byte_i = 0; ((byte_i < CAN_MAX_DLEN8) && (bytes[byte_i] != 0)); byte_i++)
499 peak_msg->data.data[byte_i] = (uint8_t)g_ascii_strtoull(bytes[byte_i], NULL((void*)0), 16);
500 }
501 }
502 g_strfreev(bytes);
503 g_free(err_or_rtr);
504 break;
505 }
506 }
507 g_free(column_text);
508 }
509
510#ifdef WS_DEBUG1
511 for (int i = 0; i < column_count; i++)
512 ws_debug("%d: %s\n", i, g_match_info_fetch(match_info, i))do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c"
, 512, __func__, "%d: %s\n", i, g_match_info_fetch(match_info
, i)); } } while (0)
;
513#endif
514
515 g_match_info_free(match_info);
516 return true1;
517}
518
519static bool_Bool peak_trc_read_packet_v2(wtap* wth, peak_trc_state_t* state, wtap_can_msg_t* peak_msg, char* line_buffer)
520{
521 int i = 0;
522 int column_i = 0;
523 int column_start = 0;
524 bool_Bool last_char_is_ws = true1;
525 peak_trc_column_type_t current_column = Col_Invalid;
526 while (line_buffer[i] != 0)
527 {
528 bool_Bool is_whitespace = iswspace(line_buffer[i]);
529 if (current_column == Col_Data)
530 {
531 // data always the last and could contain spaces
532 is_whitespace = (line_buffer[i] == '\r' || line_buffer[i] == '\n');
533 }
534
535 if (is_whitespace)
536 {
537 if (!last_char_is_ws)
538 {
539 // column closed -> process data
540 gchar* column_text = g_utf8_substring(line_buffer, column_start, i);
541
542 ws_debug("Column %d: %s\n", current_column, column_text)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c"
, 542, __func__, "Column %d: %s\n", current_column, column_text
); } } while (0)
;
543
544 switch (current_column) {
545 case Col_BusNumber:
546 {
547 char* interface_name = wmem_strdup_printf(NULL((void*)0), "interface%s", column_text);
548 peak_msg->interface_id = wtap_socketcan_find_or_create_new_interface(wth, interface_name);
549 wmem_free(NULL((void*)0), interface_name);
550 break;
551 }
552 case Col_Direction:
553 break;
554 case Col_Data: {
555 if (peak_msg->type == MSG_TYPE_ERR)
556 {
557 // FIXME fill data and msg id
558 }
559 else
560 {
561 gchar** bytes = g_strsplit(g_strstrip(column_text)g_strchomp (g_strchug (column_text)), " ", CANFD_MAX_DLEN64);
562 int byte_i = 0;
563 while (byte_i < CANFD_MAX_DLEN64) {
564 if (bytes[byte_i] == 0)
565 break;
566 peak_msg->data.data[byte_i] = (uint8_t)g_ascii_strtoull(bytes[byte_i], NULL((void*)0), 16);
567 byte_i++;
568 }
569 }
570 break;
571 }
572 case Col_CanId:
573 peak_msg->id = (guint)g_ascii_strtoull(column_text, NULL((void*)0), 16);
574 break;
575 case Col_DLC:
576 peak_msg->data.length = (guint)g_ascii_strtoull(column_text, NULL((void*)0), 10);
577 break;
578 case Col_TimeOffset:
579 {
580 double tsd = g_ascii_strtod(column_text, NULL((void*)0)) / 1000.0; // parse to seconds
581 tsd += state->trace_start;
582 peak_msg->ts.secs = (guint64)tsd;
583 peak_msg->ts.nsecs = (int)((tsd - (guint64)tsd) * 1000000000);
584 break;
585 }
586 case Col_MessageType:
587 if (strcmp(column_text, "DT") == 0)
588 {
589 peak_msg->type = MSG_TYPE_STD;
590 }
591 else if (strcmp(column_text, "FD") == 0)
592 {
593 peak_msg->type = MSG_TYPE_STD_FD;
594 }
595 else if (strcmp(column_text, "FB") == 0)
596 {
597 // CAN FD data frame with BRS bit set (Bit Rate Switch).
598 peak_msg->type = MSG_TYPE_STD_FD; // TODO
599 }
600 else if (strcmp(column_text, "FE") == 0)
601 {
602 // CAN FD data frame with ESI bit set (Error State Indicator).
603 peak_msg->type = MSG_TYPE_ERR;
604 }
605 else if (strcmp(column_text, "BI") == 0)
606 {
607 // CAN FD data frame with both BRS and ESI bits set.
608 peak_msg->type = MSG_TYPE_ERR;
609 }
610 else if (strcmp(column_text, "RR") == 0)
611 {
612 peak_msg->type = MSG_TYPE_STD_RTR;
613 }
614 else if (strcmp(column_text, "ST") == 0)
615 {
616 // TODO: Hardware Status change.
617 // Currently not supported in Wireshark
618 return false0;
619 }
620 else if (strcmp(column_text, "ER") == 0)
621 {
622 peak_msg->type = MSG_TYPE_ERR;
623 }
624 else if (strcmp(column_text, "EC") == 0)
625 {
626 // TODO: Error Counter change
627 // Currently not supported in Wireshark
628 return false0;
629 }
630 else if (strcmp(column_text, "EV") == 0)
631 {
632 // Event. User-defined text, begins directly after bus specifier
633 // TODO add support for this event type
634 return false0;
635 }
636 break;
637 case Col_MessageNumber:
638 case Col_Reserved:
639 case Col_Invalid:
640 break;
641 /* Version 1 column types ignored */
642 case Col_ErrorOrRtr_v1p0:
643 case Col_BusNumber_v1:
644 case Col_TimeOffset_v1_0:
645 case Col_Data_v1_0:
646 break;
647 }
648 g_free(column_text);
649 }
650 }
651 else
652 {
653 // non whitespace read
654 if (last_char_is_ws)
655 {
656 for (int lut_i = 0; lut_i < Col_Invalid; lut_i++)
657 {
658 if (state->column_positions[lut_i] == column_i)
659 {
660 current_column = lut_i;
661 break;
662 }
663 }
664 column_i++;
665 column_start = i;
666 }
667 }
668 last_char_is_ws = is_whitespace;
669 i++;
670 }
671
672 return true1;
673}
674
675static bool_Bool
676peak_trc_read_packet(wtap *wth, FILE_T fh, peak_trc_state_t* state,
677 wtap_rec *rec, int *err, gchar **err_info,
678 gint64 *data_offset)
679{
680 wtap_can_msg_t peak_msg = {0};
681 char line_buffer[PEAK_TRC_MAX_LINE_SIZE4096];
682
683 while (!file_eof(fh))
684 {
685 if (data_offset)
686 *data_offset = file_tell(fh);
687
688 /* Read a line */
689 if (file_gets(line_buffer, PEAK_TRC_MAX_LINE_SIZE4096, fh) == NULL((void*)0))
690 {
691 *err = file_error(fh, err_info);
692 return false0;
693 }
694
695 /* Ignore empty or commented lines */
696 if ((strlen(line_buffer) == 0) ||
697 (line_buffer[0] == ';'))
698 continue;
699
700 //Start with unknown interface ID
701 peak_msg.interface_id = WTAP_SOCKETCAN_INVALID_INTERFACE_ID0xFFFFFFFF;
702
703 if (state->file_version_major >= 2)
704 {
705 if (!peak_trc_read_packet_v2(wth, state, &peak_msg, line_buffer))
706 continue;
707 }
708 else
709 {
710 if (!peak_trc_read_packet_v1(wth, state, &peak_msg, line_buffer))
711 continue;
712 }
713
714 if (peak_msg.id > 0x7FF)
715 {
716 //Translate from 11-bit to 32-bit IDs
717 switch (peak_msg.type)
718 {
719 case MSG_TYPE_STD_FD:
720 peak_msg.type = MSG_TYPE_EXT_FD;
721 break;
722 case MSG_TYPE_STD:
723 peak_msg.type = MSG_TYPE_EXT;
724 break;
725 case MSG_TYPE_STD_RTR:
726 peak_msg.type = MSG_TYPE_EXT_RTR;
727 break;
728 default:
729 //Ignore other types
730 break;
731 }
732 }
733
734 return wtap_socketcan_gen_packet(wth, rec, &peak_msg, "peak", err, err_info);
735 }
736
737 return false0;
738}
739
740static bool_Bool
741peak_trc_read(wtap *wth, wtap_rec *rec, int *err, char **err_info,
742 int64_t *data_offset)
743{
744 peak_trc_state_t* state = (peak_trc_state_t*)wtap_socketcan_get_private_data(wth);
745
746 ws_debug("%s: Try reading at offset %" PRIi64 "\n", G_STRFUNC, file_tell(wth->fh))do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c"
, 746, __func__, "%s: Try reading at offset %" "l" "i" "\n", (
(const char*) (__func__)), file_tell(wth->fh)); } } while (
0)
;
747
748 return peak_trc_read_packet(wth, wth->fh, state, rec, err, err_info, data_offset);
749}
750
751static bool_Bool peak_trc_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, int *err, char **err_info)
752{
753 ws_debug("%s: Read at offset %" PRIi64 "\n", G_STRFUNC, seek_off)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c"
, 753, __func__, "%s: Read at offset %" "l" "i" "\n", ((const
char*) (__func__)), seek_off); } } while (0)
;
754
755 peak_trc_state_t* state = (peak_trc_state_t*)wtap_socketcan_get_private_data(wth);
756
757 if (file_seek(wth->random_fh, seek_off, SEEK_SET0, err) == -1)
758 {
759 *err = errno(*__errno_location ());
760 *err_info = g_strdup(g_strerror(errno))g_strdup_inline (g_strerror((*__errno_location ())));
761
762 return false0;
763 }
764
765 return peak_trc_read_packet(wth, wth->random_fh, state, rec, err, err_info, NULL((void*)0));
766}
767
768static void
769clean_trc_state(peak_trc_state_t* state)
770{
771 if (state != NULL((void*)0))
5
Assuming 'state' is not equal to NULL
6
Taking true branch
772 {
773 if (state->data_matcher != NULL((void*)0))
7
Assuming field 'data_matcher' is equal to NULL
8
Taking false branch
774 g_regex_unref(state->data_matcher);
775
776 g_free(state);
9
Memory is released
777 }
778}
779
780static void
781peak_trc_close(void* tap_data)
782{
783 clean_trc_state((peak_trc_state_t*)tap_data);
784}
785
786wtap_open_return_val
787peak_trc_open(wtap* wth, int* err, char** err_info)
788{
789 gint64 data_offset = 0;
790 peak_trc_state_t* trc_state = g_new0(peak_trc_state_t, 1)((peak_trc_state_t *) g_malloc0_n ((1), sizeof (peak_trc_state_t
)))
;
1
Memory is allocated
791
792 /* wth->priv stores a pointer to the general file properties.
793 * It it updated when the header data is parsed */
794 wth->priv = trc_state;
795
796 wtap_open_return_val open_val = peak_trc_parse(wth, trc_state, &data_offset, err, err_info);
797 if (open_val != WTAP_OPEN_MINE)
2
Assuming 'open_val' is not equal to WTAP_OPEN_MINE
3
Taking true branch
798 {
799 clean_trc_state(trc_state);
4
Calling 'clean_trc_state'
10
Returning; memory was released via 1st parameter
800 g_free(trc_state);
11
Attempt to free released memory
801 wth->priv = NULL((void*)0);
802 return open_val;
803 }
804
805 ws_debug("%s: This is our file\n", G_STRFUNC)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c"
, 805, __func__, "%s: This is our file\n", ((const char*) (__func__
))); } } while (0)
;
806
807 /* Go to the start of the real packet data since header is now done */
808 if (file_seek(wth->fh, data_offset, SEEK_SET0, err) == -1)
809 {
810 *err = errno(*__errno_location ());
811 *err_info = g_strdup(g_strerror(errno))g_strdup_inline (g_strerror((*__errno_location ())));
812 return WTAP_OPEN_ERROR;
813 }
814
815 wtap_set_as_socketcan(wth, peak_trc_file_type_subtype, WTAP_TSPREC_USEC6, trc_state, peak_trc_close);
816 wth->subtype_read = peak_trc_read;
817 wth->subtype_seek_read = peak_trc_seek_read;
818
819 return WTAP_OPEN_MINE;
820}
821
822static const struct supported_block_type peak_trc_blocks_supported[] = {
823 /*
824 * We support packet blocks, with no comments or other options.
825 */
826 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED0, ((void*)0) }
827};
828
829static const struct file_type_subtype_info peak_trc_info = {
830 "PEAK CAN TRC file", "peak-trc", "trc", NULL((void*)0),
831 false0, BLOCKS_SUPPORTED(peak_trc_blocks_supported)(sizeof (peak_trc_blocks_supported) / sizeof (peak_trc_blocks_supported
)[0]), peak_trc_blocks_supported
,
832 NULL((void*)0), NULL((void*)0), NULL((void*)0)
833};
834
835void register_peak_trc(void)
836{
837 peak_trc_file_type_subtype = wtap_register_file_type_subtype(&peak_trc_info);
838}
839
840/*
841 * Editor modelines - https://www.wireshark.org/tools/modelines.html
842 *
843 * Local variables:
844 * c-basic-offset: 4
845 * tab-width: 8
846 * indent-tabs-mode: nil
847 * End:
848 *
849 * vi: set shiftwidth=4 tabstop=8 expandtab:
850 * :indentSize=4:tabSize=8:noTabs=true:
851 */