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