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