Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/logcat.c
Line
Count
Source
1
/* logcat.c
2
 *
3
 * Copyright 2014, Michal Labedzki for Tieto Corporation
4
 *
5
 * SPDX-License-Identifier: GPL-2.0-or-later
6
 */
7
8
#include "config.h"
9
#include "logcat.h"
10
11
#include <string.h>
12
13
#include <wsutil/pint.h>
14
15
#include "wtap_module.h"
16
#include "file_wrappers.h"
17
18
static int logcat_file_type_subtype = -1;
19
20
void register_logcat(void);
21
22
/* Returns '?' for invalid priorities */
23
0
static char get_priority(const uint8_t priority) {
24
0
    static const char priorities[] = "??VDIWEFS";
25
26
0
    if (priority >= (uint8_t) sizeof(priorities))
27
0
        return '?';
28
29
0
    return priorities[priority];
30
0
}
31
32
/*
33
 * Returns:
34
 *
35
 *  -2 if we get an EOF at the beginning;
36
 *  -1 on an I/O error;
37
 *  0 if the record doesn't appear to be valid;
38
 *  1-{max int} as a version number if we got a valid record.
39
 */
40
static int detect_version(FILE_T fh, int *err, char **err_info)
41
0
{
42
0
    uint16_t                 payload_length;
43
0
    uint16_t                 hdr_size;
44
0
    uint16_t                 read_sofar;
45
0
    uint16_t                 entry_len;
46
0
    int                      version;
47
0
    struct logger_entry     *log_entry;
48
0
    struct logger_entry_v2  *log_entry_v2;
49
0
    uint8_t                 *buffer;
50
0
    uint16_t                 tmp;
51
0
    uint8_t                 *msg_payload;
52
0
    uint8_t                 *msg_part;
53
0
    uint8_t                 *msg_end;
54
0
    uint16_t                 msg_len;
55
56
    /* 16-bit payload length */
57
0
    if (!wtap_read_bytes_or_eof(fh, &tmp, 2, err, err_info)) {
58
0
        if (*err == 0) {
59
            /*
60
             * Got an EOF at the beginning.
61
             */
62
0
            return -2;
63
0
        }
64
0
        if (*err != WTAP_ERR_SHORT_READ)
65
0
            return -1;
66
0
        return 0;
67
0
    }
68
0
    payload_length = pletohu16(&tmp);
69
70
    /* must contain at least priority and two nulls as separator */
71
0
    if (payload_length < 3)
72
0
        return 0;
73
    /* payload length may not exceed the maximum payload size */
74
0
    if (payload_length > LOGGER_ENTRY_MAX_PAYLOAD)
75
0
        return 0;
76
77
    /* 16-bit header length (or padding, equal to 0x0000) */
78
0
    if (!wtap_read_bytes(fh, &tmp, 2, err, err_info)) {
79
0
        if (*err != WTAP_ERR_SHORT_READ)
80
0
            return -1;
81
0
        return 0;
82
0
    }
83
0
    hdr_size = pletohu16(&tmp);
84
0
    read_sofar = 4;
85
86
    /* ensure buffer is large enough for all versions */
87
0
    buffer = (uint8_t *) g_malloc(sizeof(*log_entry_v2) + payload_length);
88
0
    log_entry_v2 = (struct logger_entry_v2 *)(void *) buffer;
89
0
    log_entry = (struct logger_entry *)(void *) buffer;
90
91
    /* cannot rely on __pad being 0 for v1, use heuristics to find out what
92
     * version is in use. First assume the smallest msg. */
93
0
    for (version = 1; version <= 2; ++version) {
94
0
        if (version == 1) {
95
0
            msg_payload = (uint8_t *) (log_entry + 1);
96
0
            entry_len = sizeof(*log_entry) + payload_length;
97
0
        } else if (version == 2) {
98
            /* v2 is 4 bytes longer */
99
0
            msg_payload = (uint8_t *) (log_entry_v2 + 1);
100
0
            entry_len = sizeof(*log_entry_v2) + payload_length;
101
0
            if (hdr_size != sizeof(*log_entry_v2))
102
0
                continue;
103
0
        } else {
104
0
            continue;
105
0
        }
106
107
0
        if (!wtap_read_bytes(fh, buffer + read_sofar, entry_len - read_sofar, err, err_info)) {
108
0
            g_free(buffer);
109
0
            if (*err != WTAP_ERR_SHORT_READ)
110
0
                return -1;
111
0
            return 0;
112
0
        }
113
0
        read_sofar += entry_len - read_sofar;
114
115
        /* A v2 msg has a 32-bit userid instead of v1 priority */
116
0
        if (get_priority(msg_payload[0]) == '?')
117
0
            continue;
118
119
        /* Is there a terminating '\0' for the tag? */
120
0
        msg_part = (uint8_t *) memchr(msg_payload, '\0', payload_length - 1);
121
0
        if (msg_part == NULL)
122
0
            continue;
123
124
        /* if msg is '\0'-terminated, is it equal to the payload len? */
125
0
        ++msg_part;
126
0
        msg_len = (uint16_t)(payload_length - (msg_part - msg_payload));
127
0
        msg_end = (uint8_t *) memchr(msg_part, '\0', msg_len);
128
        /* is the end of the buffer (-1) equal to the end of msg? */
129
0
        if (msg_end && (msg_payload + payload_length - 1 != msg_end))
130
0
            continue;
131
132
0
        g_free(buffer);
133
0
        return version;
134
0
    }
135
136
    /* No version number is valid */
137
0
    g_free(buffer);
138
0
    return 0;
139
0
}
140
141
0
int logcat_exported_pdu_length(const uint8_t *pd) {
142
0
    const uint16_t *tag;
143
0
    const uint16_t *tag_length;
144
0
    int             length = 0;
145
146
0
    tag = (const uint16_t *)(const void *) pd;
147
148
0
    while(GINT16_FROM_BE(*tag)) {
149
0
        tag_length = (const uint16_t *)(const void *) (pd + 2);
150
0
        length += 2 + 2 + GINT16_FROM_BE(*tag_length);
151
152
0
        pd += 2 + 2 + GINT16_FROM_BE(*tag_length);
153
0
        tag = (const uint16_t *)(const void *) pd;
154
0
    }
155
156
0
    length += 2 + 2;
157
158
0
    return length;
159
0
}
160
161
static bool logcat_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
162
    int *err, char **err_info)
163
0
{
164
0
    struct logcat_phdr  *logcat = (struct logcat_phdr *) wth->priv;
165
0
    int                  packet_size;
166
0
    uint16_t             payload_length;
167
0
    unsigned             tmp[2];
168
0
    uint8_t             *pd;
169
0
    struct logger_entry *log_entry;
170
171
0
    if (!wtap_read_bytes_or_eof(fh, &tmp, 2, err, err_info)) {
172
0
        return false;
173
0
    }
174
0
    payload_length = pletohu16(tmp);
175
176
0
    if (logcat->version == 1) {
177
0
        packet_size = (int)sizeof(struct logger_entry) + payload_length;
178
0
    } else if (logcat->version == 2) {
179
0
        packet_size = (int)sizeof(struct logger_entry_v2) + payload_length;
180
0
    } else {
181
0
        return false;
182
0
    }
183
    /*
184
     * The maximum value of payload_length is 65535, which, even after
185
     * the size of the logger entry structure is added to it, is less
186
     * than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
187
     * it.
188
     */
189
190
0
    ws_buffer_assure_space(&rec->data, packet_size);
191
0
    pd = ws_buffer_start_ptr(&rec->data);
192
0
    log_entry = (struct logger_entry *)(void *) pd;
193
194
    /* Copy the first two bytes of the packet. */
195
0
    memcpy(pd, tmp, 2);
196
197
    /* Read the rest of the packet. */
198
0
    if (!wtap_read_bytes(fh, pd + 2, packet_size - 2, err, err_info)) {
199
0
        return false;
200
0
    }
201
202
0
    wtap_setup_packet_rec(rec, wth->file_encap);
203
0
    rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
204
0
    rec->presence_flags = WTAP_HAS_TS;
205
0
    rec->ts.secs = (time_t) GINT32_FROM_LE(log_entry->sec);
206
0
    rec->ts.nsecs = GINT32_FROM_LE(log_entry->nsec);
207
0
    rec->rec_header.packet_header.caplen = packet_size;
208
0
    rec->rec_header.packet_header.len = packet_size;
209
210
0
    rec->rec_header.packet_header.pseudo_header.logcat.version = logcat->version;
211
212
0
    return true;
213
0
}
214
215
static bool logcat_read(wtap *wth, wtap_rec *rec,
216
    int *err, char **err_info, int64_t *data_offset)
217
0
{
218
0
    *data_offset = file_tell(wth->fh);
219
220
0
    return logcat_read_packet(wth, wth->fh, rec, err, err_info);
221
0
}
222
223
static bool logcat_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
224
    int *err, char **err_info)
225
0
{
226
0
    if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
227
0
        return false;
228
229
0
    if (!logcat_read_packet(wth, wth->random_fh, rec, err, err_info)) {
230
0
        if (*err == 0)
231
0
            *err = WTAP_ERR_SHORT_READ;
232
0
        return false;
233
0
    }
234
0
    return true;
235
0
}
236
237
wtap_open_return_val logcat_open(wtap *wth, int *err, char **err_info)
238
0
{
239
0
    int                 version;
240
0
    int                 tmp_version;
241
0
    struct logcat_phdr *logcat;
242
243
    /* check first 3 packets (or 2 or 1 if EOF) versions to check file format is correct */
244
0
    version = detect_version(wth->fh, err, err_info); /* first packet */
245
0
    if (version == -1)
246
0
        return WTAP_OPEN_ERROR; /* I/O error */
247
0
    if (version == 0)
248
0
        return WTAP_OPEN_NOT_MINE;  /* not a logcat file */
249
0
    if (version == -2)
250
0
        return WTAP_OPEN_NOT_MINE;  /* empty file, so not any type of file */
251
252
0
    tmp_version = detect_version(wth->fh, err, err_info); /* second packet */
253
0
    if (tmp_version == -1)
254
0
        return WTAP_OPEN_ERROR; /* I/O error */
255
0
    if (tmp_version == 0)
256
0
        return WTAP_OPEN_NOT_MINE;  /* not a logcat file */
257
0
    if (tmp_version != -2) {
258
        /* we've read two packets; do they have the same version? */
259
0
        if (tmp_version != version) {
260
            /* no, so this is presumably not a logcat file */
261
0
            return WTAP_OPEN_NOT_MINE;
262
0
        }
263
264
0
        tmp_version = detect_version(wth->fh, err, err_info); /* third packet */
265
0
        if (tmp_version < 0)
266
0
            return WTAP_OPEN_ERROR; /* I/O error */
267
0
        if (tmp_version == 0)
268
0
            return WTAP_OPEN_NOT_MINE;  /* not a logcat file */
269
270
        /*
271
         * we've read three packets and the first two have the same
272
         * version; does the third have the same version?
273
         */
274
0
        if (tmp_version != version) {
275
            /* no, so this is presumably not a logcat file */
276
0
            return WTAP_OPEN_NOT_MINE;
277
0
        }
278
0
    }
279
280
0
    if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
281
0
        return WTAP_OPEN_ERROR;
282
283
0
    logcat = g_new(struct logcat_phdr, 1);
284
0
    logcat->version = version;
285
286
0
    wth->priv = logcat;
287
288
0
    wth->file_type_subtype = logcat_file_type_subtype;
289
0
    wth->file_encap = WTAP_ENCAP_LOGCAT;
290
0
    wth->snapshot_length = 0;
291
292
0
    wth->subtype_read = logcat_read;
293
0
    wth->subtype_seek_read = logcat_seek_read;
294
0
    wth->file_tsprec = WTAP_TSPREC_USEC;
295
296
    /*
297
     * Add an IDB; we don't know how many interfaces were
298
     * involved, so we just say one interface, about which
299
     * we only know the link-layer type, snapshot length,
300
     * and time stamp resolution.
301
     */
302
0
    wtap_add_generated_idb(wth);
303
304
0
    return WTAP_OPEN_MINE;
305
0
}
306
307
static int logcat_dump_can_write_encap(int encap)
308
0
{
309
0
    if (encap == WTAP_ENCAP_PER_PACKET)
310
0
        return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
311
312
0
    if (encap != WTAP_ENCAP_LOGCAT && encap != WTAP_ENCAP_WIRESHARK_UPPER_PDU)
313
0
        return WTAP_ERR_UNWRITABLE_ENCAP;
314
315
0
    return 0;
316
0
}
317
318
static bool logcat_binary_dump(wtap_dumper *wdh, const wtap_rec *rec,
319
    int *err, char **err_info _U_)
320
0
{
321
0
    int caplen;
322
323
    /* We can only write packet records. */
324
0
    if (rec->rec_type != REC_TYPE_PACKET) {
325
0
        *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
326
0
        *err_info = wtap_unwritable_rec_type_err_string(rec);
327
0
        return false;
328
0
    }
329
330
    /*
331
     * Make sure this packet doesn't have a link-layer type that
332
     * differs from the one for the file.
333
     */
334
0
    if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
335
0
        *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
336
0
        return false;
337
0
    }
338
339
0
    caplen = rec->rec_header.packet_header.caplen;
340
341
0
    const uint8_t *pd = ws_buffer_start_ptr(&rec->data);
342
343
    /* Skip EXPORTED_PDU*/
344
0
    if (wdh->file_encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
345
0
        int skipped_length;
346
347
0
        skipped_length = logcat_exported_pdu_length(pd);
348
0
        pd += skipped_length;
349
0
        caplen -= skipped_length;
350
0
    }
351
352
0
    if (!wtap_dump_file_write(wdh, pd, caplen, err))
353
0
        return false;
354
355
0
    return true;
356
0
}
357
358
static bool logcat_binary_dump_open(wtap_dumper *wdh, int *err _U_,
359
    char **err_info _U_)
360
0
{
361
0
    wdh->subtype_write = logcat_binary_dump;
362
363
0
    return true;
364
0
}
365
366
static const struct supported_block_type logcat_blocks_supported[] = {
367
    /*
368
     * We support packet blocks, with no comments or other options.
369
     */
370
    { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
371
};
372
373
static const struct file_type_subtype_info logcat_info = {
374
    "Android Logcat Binary format", "logcat", "logcat", NULL,
375
    false, BLOCKS_SUPPORTED(logcat_blocks_supported),
376
    logcat_dump_can_write_encap, logcat_binary_dump_open, NULL
377
};
378
379
void register_logcat(void)
380
14
{
381
14
    logcat_file_type_subtype = wtap_register_file_type_subtype(&logcat_info);
382
383
    /*
384
     * Register name for backwards compatibility with the
385
     * wtap_filetypes table in Lua.
386
     */
387
14
    wtap_register_backwards_compatibility_lua_name("LOGCAT",
388
14
                                                   logcat_file_type_subtype);
389
14
}
390
391
/*
392
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
393
 *
394
 * Local variables:
395
 * c-basic-offset: 4
396
 * tab-width: 8
397
 * indent-tabs-mode: nil
398
 * End:
399
 *
400
 * vi: set shiftwidth=4 tabstop=8 expandtab:
401
 * :indentSize=4:tabSize=8:noTabs=true:
402
 */