Coverage Report

Created: 2025-08-04 07:15

/src/wireshark/epan/dissectors/packet-logcat-text.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-logcat-text.c
2
 * Routines for Android Logcat text formats
3
 *
4
 * Copyright 2014, Michal Orynicz for Tieto Corporation
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998 Gerald Combs
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
#include "config.h"
14
15
#include <stdio.h>      /* for sscanf() */
16
17
#include "epan/packet.h"
18
#include "epan/expert.h"
19
#include "epan/exported_pdu.h"
20
#include "epan/tap.h"
21
#include "wiretap/logcat_text.h"
22
23
extern const value_string priority_vals[];
24
25
static int proto_logcat_text;
26
27
static int hf_logcat_text_pid;
28
static int hf_logcat_text_tid;
29
static int hf_logcat_text_timestamp;
30
static int hf_logcat_text_priority;
31
static int hf_logcat_text_tag;
32
static int hf_logcat_text_log;
33
34
static int ett_logcat;
35
36
static expert_field ei_malformed_time;
37
static expert_field ei_malformed_token;
38
39
static dissector_handle_t logcat_text_brief_handle;
40
static dissector_handle_t logcat_text_tag_handle;
41
static dissector_handle_t logcat_text_process_handle;
42
static dissector_handle_t logcat_text_time_handle;
43
static dissector_handle_t logcat_text_thread_handle;
44
static dissector_handle_t logcat_text_threadtime_handle;
45
static dissector_handle_t logcat_text_long_handle;
46
47
static int exported_pdu_tap = -1;
48
49
static GRegex *special_regex;
50
static GRegex *brief_regex;
51
static GRegex *tag_regex;
52
static GRegex *time_regex;
53
static GRegex *process_regex;
54
static GRegex *thread_regex;
55
static GRegex *threadtime_regex;
56
static GRegex *long_regex;
57
58
static const char dissector_name[] = "Logcat Text";
59
60
typedef int (*tGETTER) (const char *frame, const char *token, tvbuff_t *tvb,
61
        proto_tree *maintree, int start_offset, packet_info *pinfo);
62
63
typedef struct {
64
    GRegex **regex;
65
    const tGETTER *getters;
66
    unsigned no_of_getters;
67
} dissect_info_t;
68
69
void proto_register_logcat_text(void);
70
void proto_reg_handoff_logcat_text(void);
71
72
static int get_priority(const char *frame, const char *token, tvbuff_t *tvb,
73
0
        proto_tree *maintree, int start_offset, packet_info *pinfo) {
74
0
    int prio;
75
0
    char *p = g_strstr_len(frame + start_offset, -1, token);
76
0
    int offset = (int)(p - frame);
77
78
0
    if (!p) {
79
0
        proto_tree_add_expert(maintree, pinfo, &ei_malformed_token, tvb, start_offset, -1);
80
0
        return (start_offset + 1);
81
0
    }
82
83
0
    switch (*p) {
84
0
    case 'I':
85
0
        prio = 4;
86
0
        break;
87
0
    case 'V':
88
0
        prio = 2;
89
0
        break;
90
0
    case 'D':
91
0
        prio = 3;
92
0
        break;
93
0
    case 'W':
94
0
        prio = 5;
95
0
        break;
96
0
    case 'E':
97
0
        prio = 6;
98
0
        break;
99
0
    case 'F':
100
0
        prio = 7;
101
0
        break;
102
0
    default:
103
0
        prio = 0;
104
0
    }
105
106
0
    proto_tree_add_uint(maintree, hf_logcat_text_priority, tvb, offset, 1, prio);
107
0
    return offset + 1;
108
0
}
109
110
static int get_tag(const char *frame, const char *token, tvbuff_t *tvb,
111
0
        proto_tree *maintree, int start_offset, packet_info *pinfo) {
112
0
    char *p = g_strstr_len(frame + start_offset, -1, token);
113
0
    int offset = (int)(p - frame);
114
0
    uint8_t *src_addr = wmem_strdup(pinfo->pool, token);
115
0
    int tok_len = (int)strlen(token);
116
117
0
    proto_tree_add_string(maintree, hf_logcat_text_tag, tvb, offset, tok_len,
118
0
            token);
119
0
    set_address(&pinfo->src, AT_STRINGZ, tok_len + 1, src_addr);
120
0
    set_address(&pinfo->dst, AT_STRINGZ, sizeof(dissector_name), dissector_name);
121
0
    return offset + tok_len;
122
0
}
123
124
static int get_ptid(const char *frame, const char *token, tvbuff_t *tvb,
125
0
        proto_tree *maintree, int header_field, int start_offset) {
126
0
    char *p = g_strstr_len(frame + start_offset, -1, token);
127
0
    int offset = (int)(p - frame);
128
129
0
    proto_tree_add_uint(maintree, header_field, tvb, offset, (int)strlen(token),
130
0
            (uint32_t)g_ascii_strtoull(token, NULL, 10));
131
0
    return offset + (int)strlen(token);
132
0
}
133
134
static int get_pid(const char *frame, const char *token, tvbuff_t *tvb,
135
0
        proto_tree *maintree, int start_offset, packet_info *pinfo _U_) {
136
0
    return get_ptid(frame, token, tvb, maintree, hf_logcat_text_pid, start_offset);
137
0
}
138
139
static int get_tid(const char *frame, const char *token, tvbuff_t *tvb,
140
0
        proto_tree *maintree, int start_offset, packet_info *pinfo _U_) {
141
0
    return get_ptid(frame, token, tvb, maintree, hf_logcat_text_tid, start_offset);
142
0
}
143
144
static int get_log(const char *frame, const char *token, tvbuff_t *tvb,
145
0
        proto_tree *maintree, int start_offset, packet_info *pinfo) {
146
0
    char *p = g_strstr_len(frame + start_offset, -1, token);
147
0
    int offset = (int)(p - frame);
148
149
0
    proto_tree_add_string(maintree, hf_logcat_text_log, tvb, offset,
150
0
            (int)strlen(token), token);
151
0
    col_add_str(pinfo->cinfo, COL_INFO, token);
152
0
    return offset + (int)strlen(token);
153
0
}
154
155
static int get_time(const char *frame, const char *token, tvbuff_t *tvb,
156
0
        proto_tree *maintree, int start_offset, packet_info *pinfo) {
157
0
    int offset;
158
0
    char *p;
159
0
    int ms;
160
0
    struct tm date;
161
0
    time_t seconds;
162
0
    nstime_t ts;
163
164
0
    p = g_strstr_len(frame + start_offset, -1, token);
165
0
    offset = (int)(p - frame);
166
167
0
    if (6 == sscanf(token, "%d-%d %d:%d:%d.%d", &date.tm_mon, &date.tm_mday,
168
0
                    &date.tm_hour, &date.tm_min, &date.tm_sec, &ms)) {
169
0
        date.tm_year = 70;
170
0
        date.tm_mon -= 1;
171
0
        date.tm_isdst = -1;
172
0
        seconds = mktime(&date);
173
0
        ts.secs = seconds;
174
0
        ts.nsecs = (int) (ms * 1e6);
175
0
        proto_tree_add_time(maintree, hf_logcat_text_timestamp, tvb, offset,
176
0
                (int)strlen(token), &ts);
177
0
    } else {
178
0
        proto_tree_add_expert(maintree, pinfo, &ei_malformed_time, tvb, offset, -1);
179
0
    }
180
0
    return offset + (int)strlen(token);
181
0
}
182
183
static int dissect_logcat_text(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo,
184
0
        const dissect_info_t *dinfo) {
185
0
    char **tokens;
186
0
    unsigned i;
187
0
    char *frame = tvb_get_string_enc(pinfo->pool, tvb, 0, tvb_captured_length(tvb),
188
0
            ENC_UTF_8);
189
0
    proto_item *mainitem = proto_tree_add_item(tree, proto_logcat_text, tvb, 0, -1, ENC_NA);
190
0
    proto_tree *maintree = proto_item_add_subtree(mainitem, ett_logcat);
191
0
    int offset = 0;
192
193
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, dissector_name);
194
195
0
    if (!g_regex_match(special_regex, frame, G_REGEX_MATCH_NOTEMPTY, NULL)) {
196
197
0
        tokens = g_regex_split(*dinfo->regex, frame, G_REGEX_MATCH_NOTEMPTY);
198
0
        if (NULL == tokens) return 0;
199
0
        if (g_strv_length(tokens) != dinfo->no_of_getters + 2) {
200
0
            proto_tree_add_expert(maintree, pinfo, &ei_malformed_token, tvb, offset, -1);
201
0
            g_strfreev(tokens);
202
0
            return 0;
203
0
        }
204
205
0
        for (i = 0; i < dinfo->no_of_getters; ++i) {
206
0
            offset = ((*dinfo->getters[i])(frame, tokens[i + 1], tvb, maintree, offset, pinfo));
207
0
        }
208
0
    } else {
209
0
        tokens = g_regex_split(special_regex, frame, G_REGEX_MATCH_NOTEMPTY);
210
0
        if (NULL == tokens) return 0;
211
0
        offset = get_log(frame, tokens[1], tvb, maintree, 0, pinfo);
212
0
    }
213
0
    g_strfreev(tokens);
214
0
    return offset;
215
0
}
216
217
0
static void add_exported_pdu(tvbuff_t *tvb, packet_info *pinfo, const char * subdissector_name){
218
0
    if (have_tap_listener(exported_pdu_tap)) {
219
0
        exp_pdu_data_t *exp_pdu_data;
220
221
0
        exp_pdu_data = export_pdu_create_tags(pinfo, subdissector_name, EXP_PDU_TAG_DISSECTOR_NAME, NULL);
222
223
0
        exp_pdu_data->tvb_captured_length = tvb_captured_length(tvb);
224
0
        exp_pdu_data->tvb_reported_length = tvb_reported_length(tvb);
225
0
        exp_pdu_data->pdu_tvb = tvb;
226
0
        tap_queue_packet(exported_pdu_tap, pinfo, exp_pdu_data);
227
0
    }
228
0
}
229
230
static int dissect_logcat_text_brief(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
231
0
        void *data _U_) {
232
0
    static const tGETTER getters[] = { get_priority, get_tag, get_pid, get_log };
233
0
    dissect_info_t dinfo = { &brief_regex, getters, array_length(getters) };
234
235
0
    add_exported_pdu(tvb,pinfo,"logcat_text_brief");
236
0
    return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
237
0
}
238
239
static int dissect_logcat_text_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
240
0
        void *data _U_) {
241
0
    static const tGETTER getters[] = { get_priority, get_tag, get_log };
242
0
    dissect_info_t dinfo = { &tag_regex, getters, array_length(getters) };
243
244
0
    add_exported_pdu(tvb,pinfo,"logcat_text_tag");
245
0
    return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
246
0
}
247
248
static int dissect_logcat_text_process(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
249
0
        void *data _U_) {
250
0
    static const tGETTER getters[] = { get_priority, get_pid, get_log };
251
0
    dissect_info_t dinfo = { &process_regex, getters, array_length(getters) };
252
253
0
    add_exported_pdu(tvb,pinfo,"logcat_text_process");
254
0
    set_address(&pinfo->dst, AT_STRINGZ, 1, "");
255
0
    set_address(&pinfo->src, AT_STRINGZ, 1, "");
256
257
0
    return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
258
0
}
259
260
static int dissect_logcat_text_time(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
261
0
        void *data _U_) {
262
0
    static const tGETTER getters[] = { get_time, get_priority, get_tag, get_pid, get_log };
263
0
    dissect_info_t dinfo = { &time_regex, getters, array_length(getters) };
264
265
0
    add_exported_pdu(tvb,pinfo,"logcat_text_time");
266
0
    return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
267
0
}
268
269
static int dissect_logcat_text_thread(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
270
0
        void *data _U_) {
271
0
    static const tGETTER getters[] = { get_priority, get_pid, get_tid, get_log };
272
0
    dissect_info_t dinfo = { &thread_regex, getters, array_length(getters) };
273
274
0
    add_exported_pdu(tvb,pinfo,"logcat_text_brief");
275
0
    set_address(&pinfo->dst, AT_STRINGZ, 1, "");
276
0
    set_address(&pinfo->src, AT_STRINGZ, 1, "");
277
278
0
    return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
279
0
}
280
281
static int dissect_logcat_text_threadtime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
282
0
        void *data _U_) {
283
0
    static const tGETTER getters[] = { get_time, get_pid, get_tid, get_priority, get_tag, get_log };
284
0
    dissect_info_t dinfo = { &threadtime_regex, getters, array_length(getters) };
285
286
0
    add_exported_pdu(tvb,pinfo,"logcat_text_threadtime");
287
0
    return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
288
0
}
289
290
static int dissect_logcat_text_long(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
291
0
        void *data _U_) {
292
0
    static const tGETTER getters[] = { get_time, get_pid, get_tid, get_priority, get_tag, get_log };
293
0
    dissect_info_t dinfo = { &long_regex, getters, array_length(getters) };
294
295
0
    add_exported_pdu(tvb,pinfo,"logcat_text_long");
296
0
    return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
297
0
}
298
299
static void logcat_text_create_regex(void)
300
14
{
301
14
    special_regex =    g_regex_new(SPECIAL_STRING,    (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_OPTIMIZE | G_REGEX_RAW),  G_REGEX_MATCH_NOTEMPTY, NULL);
302
14
    brief_regex =      g_regex_new(BRIEF_STRING,      (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_OPTIMIZE | G_REGEX_RAW),  G_REGEX_MATCH_NOTEMPTY, NULL);
303
14
    tag_regex =        g_regex_new(TAG_STRING,        (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_OPTIMIZE | G_REGEX_RAW),  G_REGEX_MATCH_NOTEMPTY, NULL);
304
14
    time_regex =       g_regex_new(TIME_STRING,       (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_OPTIMIZE | G_REGEX_RAW),  G_REGEX_MATCH_NOTEMPTY, NULL);
305
14
    thread_regex =     g_regex_new(THREAD_STRING,     (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_OPTIMIZE | G_REGEX_RAW),  G_REGEX_MATCH_NOTEMPTY, NULL);
306
14
    threadtime_regex = g_regex_new(THREADTIME_STRING, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_OPTIMIZE | G_REGEX_RAW),  G_REGEX_MATCH_NOTEMPTY, NULL);
307
14
    process_regex =    g_regex_new(PROCESS_STRING,    (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_OPTIMIZE | G_REGEX_RAW),  G_REGEX_MATCH_NOTEMPTY, NULL);
308
14
    long_regex =       g_regex_new(LONG_STRING,       (GRegexCompileFlags)(G_REGEX_MULTILINE | G_REGEX_OPTIMIZE | G_REGEX_RAW), G_REGEX_MATCH_NOTEMPTY, NULL);
309
14
}
310
311
static void logcat_text_shutdown(void)
312
0
{
313
0
    g_regex_unref(special_regex);
314
0
    g_regex_unref(brief_regex);
315
0
    g_regex_unref(tag_regex);
316
0
    g_regex_unref(time_regex);
317
0
    g_regex_unref(thread_regex);
318
0
    g_regex_unref(threadtime_regex);
319
0
    g_regex_unref(process_regex);
320
0
    g_regex_unref(long_regex);
321
0
}
322
323
14
void proto_register_logcat_text(void) {
324
14
    expert_module_t  *expert_module;
325
14
    static hf_register_info hf[] = {
326
14
            { &hf_logcat_text_timestamp,
327
14
                { "Timestamp", "logcat_text.timestamp",
328
14
                FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x00, NULL, HFILL
329
14
                }
330
14
            },
331
14
            { &hf_logcat_text_tag,
332
14
                { "Tag",       "logcat_text.tag",
333
14
                FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL
334
14
                }
335
14
            },
336
14
            { &hf_logcat_text_log,
337
14
                { "Log",       "logcat_text.log",
338
14
                FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL
339
14
                }
340
14
            },
341
14
            { &hf_logcat_text_priority,
342
14
                { "Priority",  "logcat_text.priority",
343
14
                FT_UINT8, BASE_DEC, VALS(priority_vals), 0x00, NULL, HFILL
344
14
                }
345
14
            },
346
14
            { &hf_logcat_text_pid,
347
14
                { "PID",       "logcat_text.pid",
348
14
                FT_UINT32, BASE_DEC, NULL, 0x00, "Process ID", HFILL
349
14
                }
350
14
            },
351
14
            { &hf_logcat_text_tid,
352
14
                { "TID",       "logcat_text.tid",
353
14
                FT_UINT32, BASE_DEC, NULL, 0x00, "Thread ID", HFILL
354
14
                }
355
14
            }
356
14
    };
357
358
14
    static ei_register_info ei[] = {
359
14
            { &ei_malformed_time,  { "logcat_text.malformed_time", PI_PROTOCOL, PI_ERROR, "Malformed time data", EXPFILL }},
360
14
            { &ei_malformed_token, { "logcat_text.malformed_token", PI_PROTOCOL, PI_ERROR, "Failed to decode one or more tokens", EXPFILL }},
361
14
    };
362
363
14
    static int *ett[] = { &ett_logcat};
364
365
14
    proto_logcat_text = proto_register_protocol("Android Logcat Text", dissector_name,
366
14
            "logcat_text");
367
14
    proto_register_field_array(proto_logcat_text, hf, array_length(hf));
368
14
    proto_register_subtree_array(ett, array_length(ett));
369
370
14
    logcat_text_brief_handle =      register_dissector("logcat_text_brief",
371
14
            dissect_logcat_text_brief, proto_logcat_text);
372
14
    logcat_text_tag_handle =        register_dissector("logcat_text_tag",
373
14
            dissect_logcat_text_tag, proto_logcat_text);
374
14
    logcat_text_time_handle =       register_dissector("logcat_text_time",
375
14
            dissect_logcat_text_time, proto_logcat_text);
376
14
    logcat_text_process_handle =    register_dissector("logcat_text_process",
377
14
            dissect_logcat_text_process, proto_logcat_text);
378
14
    logcat_text_thread_handle =     register_dissector("logcat_text_thread",
379
14
            dissect_logcat_text_thread, proto_logcat_text);
380
14
    logcat_text_threadtime_handle = register_dissector("logcat_text_threadtime",
381
14
            dissect_logcat_text_threadtime, proto_logcat_text);
382
14
    logcat_text_long_handle =       register_dissector("logcat_text_long",
383
14
            dissect_logcat_text_long, proto_logcat_text);
384
385
14
    logcat_text_create_regex();
386
14
    register_shutdown_routine(logcat_text_shutdown);
387
388
14
    expert_module = expert_register_protocol(proto_logcat_text);
389
14
    expert_register_field_array(expert_module, ei, array_length(ei));
390
391
14
    exported_pdu_tap = register_export_pdu_tap("Logcat Text");
392
14
}
393
394
14
void proto_reg_handoff_logcat_text(void) {
395
14
    dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_BRIEF,
396
14
            logcat_text_brief_handle);
397
14
    dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_TAG,
398
14
            logcat_text_tag_handle);
399
14
    dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_TIME,
400
14
            logcat_text_time_handle);
401
14
    dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_THREAD,
402
14
            logcat_text_thread_handle);
403
14
    dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_THREADTIME,
404
14
            logcat_text_threadtime_handle);
405
14
    dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_PROCESS,
406
14
            logcat_text_process_handle);
407
14
    dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_LONG,
408
14
            logcat_text_long_handle);
409
14
}
410
411
/*
412
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
413
 *
414
 * Local variables:
415
 * c-basic-offset: 4
416
 * tab-width: 8
417
 * indent-tabs-mode: nil
418
 * End:
419
 *
420
 * vi: set shiftwidth=4 tabstop=8 expandtab:
421
 * :indentSize=4:tabSize=8:noTabs=true:
422
 */