Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/busmaster.c
Line
Count
Source
1
/* busmaster.c
2
 *
3
 * Wiretap Library
4
 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
5
 *
6
 * Support for Busmaster log file format
7
 * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
8
 *
9
 * See https://rbei-etas.github.io/busmaster/ for the BUSMASTER software.
10
 *
11
 * SPDX-License-Identifier: GPL-2.0-or-later
12
 */
13
14
0
#define WS_LOG_DOMAIN "busmaster"
15
#include "config.h"
16
#include <wireshark.h>
17
#include "busmaster.h"
18
19
#include <socketcan.h>
20
#include <file_wrappers.h>
21
#include <epan/dissectors/packet-socketcan.h>
22
#include <wsutil/exported_pdu_tlvs.h>
23
#include "busmaster_priv.h"
24
#include <inttypes.h>
25
#include <string.h>
26
#include <errno.h>
27
28
//Private data for the wiretap
29
typedef struct {
30
    GSList* list;    //List of private entries
31
} busmaster_data_t;
32
33
static void
34
busmaster_close(void* tap_data);
35
36
static bool
37
busmaster_read(wtap   *wth, wtap_rec *rec,
38
               int    *err, char **err_info,
39
               int64_t *data_offset);
40
41
static bool
42
busmaster_seek_read(wtap     *wth, int64_t seek_off,
43
                    wtap_rec *rec,
44
                    int      *err, char **err_info);
45
46
static int busmaster_file_type_subtype = -1;
47
48
void register_busmaster(void);
49
50
51
static bool
52
busmaster_gen_packet(wtap* wth,
53
    wtap_rec* rec,
54
    const busmaster_priv_t* priv_entry, const msg_t* msg,
55
    int* err, char** err_info)
56
0
{
57
0
    wtap_can_msg_t     can_msg;
58
0
    bool has_ts = false;
59
60
0
    can_msg.id = msg->id;
61
0
    can_msg.type = msg->type;
62
0
    can_msg.flags = 0;     //Not used by BUSMASTER
63
0
    can_msg.interface_id = WTAP_SOCKETCAN_INVALID_INTERFACE_ID;     //Not (yet) used by BUSMASTER
64
0
    can_msg.data = msg->data;
65
66
    //Need to convert the timestamp
67
0
    if (priv_entry->time_mode == TIME_MODE_SYSTEM)
68
0
    {
69
0
        struct tm tm;
70
71
0
        tm.tm_year = priv_entry->start.d.year - 1900;
72
0
        tm.tm_mon = priv_entry->start.d.month - 1;
73
0
        tm.tm_mday = priv_entry->start.d.day;
74
0
        tm.tm_hour = msg->timestamp.hours;
75
0
        tm.tm_min = msg->timestamp.minutes;
76
0
        tm.tm_sec = msg->timestamp.seconds;
77
0
        tm.tm_isdst = -1;
78
79
0
        can_msg.ts.secs = mktime(&tm);
80
0
        can_msg.ts.nsecs = msg->timestamp.micros * 1000u;
81
0
        has_ts = true;
82
0
    }
83
0
    else if (priv_entry->time_mode == TIME_MODE_ABSOLUTE)
84
0
    {
85
0
        struct tm tm;
86
0
        uint32_t  micros;
87
88
0
        tm.tm_year = priv_entry->start.d.year - 1900;
89
0
        tm.tm_mon = priv_entry->start.d.month - 1;
90
0
        tm.tm_mday = priv_entry->start.d.day;
91
0
        tm.tm_hour = priv_entry->start.t.hours;
92
0
        tm.tm_min = priv_entry->start.t.minutes;
93
0
        tm.tm_sec = priv_entry->start.t.seconds;
94
0
        tm.tm_isdst = -1;
95
96
0
        can_msg.ts.secs = mktime(&tm);
97
98
0
        can_msg.ts.secs += msg->timestamp.hours * 3600;
99
0
        can_msg.ts.secs += msg->timestamp.minutes * 60;
100
0
        can_msg.ts.secs += msg->timestamp.seconds;
101
102
0
        micros = priv_entry->start.t.micros + msg->timestamp.micros;
103
0
        if (micros >= 1000000u)
104
0
        {
105
0
            micros -= 1000000u;
106
0
            can_msg.ts.secs += 1;
107
0
        }
108
109
0
        can_msg.ts.nsecs = micros * 1000u;
110
0
        has_ts = true;
111
0
    }
112
0
    else
113
0
    {
114
0
        can_msg.ts.secs = 0;
115
0
        can_msg.ts.nsecs = 0;
116
0
    }
117
118
0
    if (!wtap_socketcan_gen_packet(wth, rec, &can_msg, "busmaster", err, err_info))
119
0
        return false;
120
121
    //Packet may not have timestamp
122
0
    if (!has_ts)
123
0
        rec->presence_flags &= (~WTAP_HAS_TS);
124
0
    return true;
125
0
}
126
127
static log_entry_type_t
128
busmaster_parse(FILE_T fh, busmaster_state_t *state, int *err, char **err_info)
129
0
{
130
0
    bool ok;
131
0
    int64_t  seek_off;
132
133
0
    ws_debug("%s: Running busmaster file decoder\n", G_STRFUNC);
134
135
0
    state->fh = fh;
136
137
0
    do
138
0
    {
139
0
        if (file_eof(fh))
140
0
            return LOG_ENTRY_EOF;
141
142
0
        seek_off               = file_tell(fh);
143
0
        ws_debug("%s: Starting parser at offset %" PRIi64 "\n",
144
0
                               G_STRFUNC, seek_off);
145
0
        state->file_bytes_read = 0;
146
0
        ok                     = run_busmaster_parser(state, err, err_info);
147
148
        /* Rewind the file to the offset we have finished parsing */
149
0
        ws_debug("%s: Rewinding to offset %" PRIi64 "\n",
150
0
                               G_STRFUNC, seek_off + state->file_bytes_read);
151
0
        if (file_seek(fh, seek_off + state->file_bytes_read, SEEK_SET, err) == -1)
152
0
        {
153
0
            g_free(*err_info);
154
0
            *err      = errno;
155
0
            *err_info = g_strdup(g_strerror(errno));
156
0
            return LOG_ENTRY_ERROR;
157
0
        }
158
0
    }
159
0
    while (ok && state->entry_type == LOG_ENTRY_NONE);
160
161
0
    if (!ok)
162
0
        return LOG_ENTRY_ERROR;
163
164
0
    ws_debug("%s: Success\n", G_STRFUNC);
165
166
0
    return state->entry_type;
167
0
}
168
169
wtap_open_return_val
170
busmaster_open(wtap *wth, int *err, char **err_info)
171
0
{
172
0
    busmaster_state_t state = {0};
173
0
    log_entry_type_t  entry;
174
175
0
    ws_debug("%s: Trying to open with busmaster log reader\n",
176
0
                           G_STRFUNC);
177
178
    /* Rewind to the beginning */
179
0
    if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
180
0
        return WTAP_OPEN_ERROR;
181
182
0
    entry = busmaster_parse(wth->fh, &state, err, err_info);
183
184
0
    g_free(*err_info);
185
0
    *err_info = NULL;
186
0
    *err      = 0;
187
188
0
    if (entry != LOG_ENTRY_HEADER)
189
0
        return WTAP_OPEN_NOT_MINE;
190
191
    /* Rewind to the beginning, so busmaster_read may read from the very beginning */
192
0
    if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
193
0
        return WTAP_OPEN_ERROR;
194
195
0
    ws_debug("%s: That's a busmaster log\n", G_STRFUNC);
196
197
0
    wth->subtype_read      = busmaster_read;
198
0
    wth->subtype_seek_read = busmaster_seek_read;
199
0
    wtap_set_as_socketcan(wth, busmaster_file_type_subtype, WTAP_TSPREC_USEC, g_new0(busmaster_data_t, 1), busmaster_close);
200
201
0
    return WTAP_OPEN_MINE;
202
0
}
203
204
static void
205
busmaster_close(void* tap_data)
206
0
{
207
0
    busmaster_data_t* data = (busmaster_data_t*)tap_data;
208
209
0
    ws_debug("%s\n", G_STRFUNC);
210
211
0
    g_slist_free_full(data->list, g_free);
212
0
    g_free(data);
213
0
}
214
215
static busmaster_priv_t *
216
busmaster_find_priv_entry(GSList* priv_list, int64_t offset)
217
0
{
218
0
    GSList *list;
219
220
0
    for (list = priv_list; list; list = g_slist_next(list))
221
0
    {
222
0
        busmaster_priv_t *entry = (busmaster_priv_t *)list->data;
223
224
0
        if (((entry->file_end_offset == -1)
225
0
             && (g_slist_next(list) == NULL))
226
0
            || ((offset >= entry->file_start_offset)
227
0
                && (offset <= entry->file_end_offset)))
228
0
        {
229
0
            return entry;
230
0
        }
231
0
    }
232
233
0
    return NULL;
234
0
}
235
236
static bool
237
busmaster_read(wtap   *wth, wtap_rec *rec, int *err, char **err_info,
238
               int64_t *data_offset)
239
0
{
240
0
    log_entry_type_t   entry;
241
0
    busmaster_state_t  state;
242
0
    busmaster_priv_t  *priv_entry;
243
0
    bool               is_msg = false;
244
0
    bool               is_ok = true;
245
0
    busmaster_data_t*  priv_data = (busmaster_data_t*)wtap_socketcan_get_private_data(wth);
246
247
0
    while (!is_msg && is_ok)
248
0
    {
249
0
        ws_debug("%s: offset = %" PRIi64 "\n",
250
0
                               G_STRFUNC, file_tell(wth->fh));
251
252
0
        if (file_eof(wth->fh))
253
0
        {
254
0
            ws_debug("%s: End of file detected, nothing to do here\n",
255
0
                                   G_STRFUNC);
256
0
            *err      = 0;
257
0
            *err_info = NULL;
258
0
            return false;
259
0
        }
260
261
0
        *data_offset = file_tell(wth->fh);
262
0
        priv_entry   = busmaster_find_priv_entry(priv_data->list, *data_offset);
263
264
0
        memset(&state, 0, sizeof(state));
265
0
        if (priv_entry)
266
0
            state.header = *priv_entry;
267
0
        entry = busmaster_parse(wth->fh, &state, err, err_info);
268
269
0
        ws_debug("%s: analyzing output\n", G_STRFUNC);
270
0
        switch (entry)
271
0
        {
272
0
        case LOG_ENTRY_EMPTY:
273
0
            break;
274
0
        case LOG_ENTRY_FOOTER_AND_HEADER:
275
0
        case LOG_ENTRY_FOOTER:
276
0
            priv_entry = (busmaster_priv_t *)g_slist_last(priv_data->list)->data;
277
0
            if (!priv_entry)
278
0
            {
279
0
                *err      = WTAP_ERR_BAD_FILE;
280
0
                *err_info = g_strdup("Header is missing");
281
0
                return false;
282
0
            }
283
0
            priv_entry->file_end_offset  = *data_offset;
284
0
            if (entry == LOG_ENTRY_FOOTER)
285
0
                break;
286
            /* fall-through */
287
0
        case LOG_ENTRY_HEADER:
288
0
            if (state.header.protocol != PROTOCOL_CAN &&
289
0
                state.header.protocol != PROTOCOL_J1939)
290
0
            {
291
0
                *err      = WTAP_ERR_UNSUPPORTED;
292
0
                *err_info = g_strdup("Unsupported protocol type");
293
0
                return false;
294
0
            }
295
296
0
            if (priv_data->list)
297
0
            {
298
                /* Check that the previous section has a footer */
299
0
                priv_entry = (busmaster_priv_t *)g_slist_last(priv_data->list)->data;
300
301
0
                if (priv_entry && priv_entry->file_end_offset == -1)
302
0
                {
303
0
                    *err      = WTAP_ERR_BAD_FILE;
304
0
                    *err_info = g_strdup("Footer is missing");
305
0
                    return false;
306
0
                }
307
0
            }
308
309
            /* Start a new section */
310
0
            priv_entry = g_new(busmaster_priv_t, 1);
311
312
0
            priv_entry[0]                 = state.header;
313
0
            priv_entry->file_start_offset = file_tell(wth->fh);
314
0
            priv_entry->file_end_offset   = -1;
315
316
0
            priv_data->list = g_slist_append(priv_data->list, priv_entry);
317
0
            break;
318
0
        case LOG_ENTRY_MSG:
319
0
            is_msg     = true;
320
0
            priv_entry = busmaster_find_priv_entry(priv_data->list, *data_offset);
321
0
            is_ok  = busmaster_gen_packet(wth, rec, priv_entry, &state.msg, err, err_info);
322
0
            break;
323
0
        case LOG_ENTRY_EOF:
324
0
        case LOG_ENTRY_ERROR:
325
0
        case LOG_ENTRY_NONE:
326
0
        default:
327
0
            is_ok = false;
328
0
            break;
329
0
        }
330
0
    }
331
332
0
    ws_debug("%s: stopped at offset %" PRIi64 " with entry %d\n",
333
0
                           G_STRFUNC, file_tell(wth->fh), entry);
334
335
0
    return is_ok;
336
0
}
337
338
static bool
339
busmaster_seek_read(wtap   *wth, int64_t seek_off, wtap_rec *rec,
340
                    int *err, char **err_info)
341
0
{
342
0
    busmaster_priv_t  *priv_entry;
343
0
    busmaster_state_t  state = {0};
344
0
    log_entry_type_t   entry;
345
0
    busmaster_data_t* priv_data = (busmaster_data_t*)wtap_socketcan_get_private_data(wth);
346
347
0
    ws_debug("%s: offset = %" PRIi64 "\n", G_STRFUNC, seek_off);
348
349
0
    priv_entry = busmaster_find_priv_entry(priv_data->list, seek_off);
350
0
    if (!priv_entry)
351
0
    {
352
0
        ws_debug("%s: analyzing output\n", G_STRFUNC);
353
0
        *err = WTAP_ERR_BAD_FILE;
354
0
        *err_info = g_strdup("Malformed header");
355
0
        return false;
356
0
    }
357
358
0
    if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
359
0
        return false;
360
361
0
    state.header = *priv_entry;
362
0
    entry = busmaster_parse(wth->random_fh, &state, err, err_info);
363
364
0
    ws_debug("%s: analyzing output\n", G_STRFUNC);
365
366
0
    if (entry == LOG_ENTRY_ERROR || entry == LOG_ENTRY_NONE)
367
0
        return false;
368
369
0
    if (entry != LOG_ENTRY_MSG)
370
0
    {
371
0
        *err = WTAP_ERR_BAD_FILE;
372
0
        *err_info = g_strdup("Failed to read a frame");
373
0
        return false;
374
0
    }
375
376
0
    return busmaster_gen_packet(wth, rec, priv_entry, &state.msg, err, err_info);
377
0
}
378
379
static const struct supported_block_type busmaster_blocks_supported[] = {
380
    /*
381
     * We support packet blocks, with no comments or other options.
382
     */
383
    { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
384
};
385
386
static const struct file_type_subtype_info busmaster_info = {
387
    "BUSMASTER log file", "busmaster", "log", NULL,
388
    false, BLOCKS_SUPPORTED(busmaster_blocks_supported),
389
    NULL, NULL, NULL
390
};
391
392
void register_busmaster(void)
393
14
{
394
14
    busmaster_file_type_subtype = wtap_register_file_type_subtype(&busmaster_info);
395
14
}
396
397
/*
398
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
399
 *
400
 * Local variables:
401
 * c-basic-offset: 4
402
 * tab-width: 8
403
 * indent-tabs-mode: nil
404
 * End:
405
 *
406
 * vi: set shiftwidth=4 tabstop=8 expandtab:
407
 * :indentSize=4:tabSize=8:noTabs=true:
408
 */