3 * Copyright 2014, Michal Labedzki for Tieto Corporation
5 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include "file_wrappers.h"
16 static int logcat_file_type_subtype
= -1;
18 void register_logcat(void);
20 /* Returns '?' for invalid priorities */
21 static char get_priority(const uint8_t priority
) {
22 static char priorities
[] = "??VDIWEFS";
24 if (priority
>= (uint8_t) sizeof(priorities
))
27 return priorities
[priority
];
33 * -2 if we get an EOF at the beginning;
35 * 0 if the record doesn't appear to be valid;
36 * 1-{max int} as a version number if we got a valid record.
38 static int detect_version(FILE_T fh
, int *err
, char **err_info
)
40 uint16_t payload_length
;
45 struct logger_entry
*log_entry
;
46 struct logger_entry_v2
*log_entry_v2
;
54 /* 16-bit payload length */
55 if (!wtap_read_bytes_or_eof(fh
, &tmp
, 2, err
, err_info
)) {
58 * Got an EOF at the beginning.
62 if (*err
!= WTAP_ERR_SHORT_READ
)
66 payload_length
= pletoh16(&tmp
);
68 /* must contain at least priority and two nulls as separator */
69 if (payload_length
< 3)
71 /* payload length may not exceed the maximum payload size */
72 if (payload_length
> LOGGER_ENTRY_MAX_PAYLOAD
)
75 /* 16-bit header length (or padding, equal to 0x0000) */
76 if (!wtap_read_bytes(fh
, &tmp
, 2, err
, err_info
)) {
77 if (*err
!= WTAP_ERR_SHORT_READ
)
81 hdr_size
= pletoh16(&tmp
);
84 /* ensure buffer is large enough for all versions */
85 buffer
= (uint8_t *) g_malloc(sizeof(*log_entry_v2
) + payload_length
);
86 log_entry_v2
= (struct logger_entry_v2
*)(void *) buffer
;
87 log_entry
= (struct logger_entry
*)(void *) buffer
;
89 /* cannot rely on __pad being 0 for v1, use heuristics to find out what
90 * version is in use. First assume the smallest msg. */
91 for (version
= 1; version
<= 2; ++version
) {
93 msg_payload
= (uint8_t *) (log_entry
+ 1);
94 entry_len
= sizeof(*log_entry
) + payload_length
;
95 } else if (version
== 2) {
96 /* v2 is 4 bytes longer */
97 msg_payload
= (uint8_t *) (log_entry_v2
+ 1);
98 entry_len
= sizeof(*log_entry_v2
) + payload_length
;
99 if (hdr_size
!= sizeof(*log_entry_v2
))
105 if (!wtap_read_bytes(fh
, buffer
+ read_sofar
, entry_len
- read_sofar
, err
, err_info
)) {
107 if (*err
!= WTAP_ERR_SHORT_READ
)
111 read_sofar
+= entry_len
- read_sofar
;
113 /* A v2 msg has a 32-bit userid instead of v1 priority */
114 if (get_priority(msg_payload
[0]) == '?')
117 /* Is there a terminating '\0' for the tag? */
118 msg_part
= (uint8_t *) memchr(msg_payload
, '\0', payload_length
- 1);
119 if (msg_part
== NULL
)
122 /* if msg is '\0'-terminated, is it equal to the payload len? */
124 msg_len
= (uint16_t)(payload_length
- (msg_part
- msg_payload
));
125 msg_end
= (uint8_t *) memchr(msg_part
, '\0', msg_len
);
126 /* is the end of the buffer (-1) equal to the end of msg? */
127 if (msg_end
&& (msg_payload
+ payload_length
- 1 != msg_end
))
134 /* No version number is valid */
139 int logcat_exported_pdu_length(const uint8_t *pd
) {
141 const uint16_t *tag_length
;
144 tag
= (const uint16_t *)(const void *) pd
;
146 while(GINT16_FROM_BE(*tag
)) {
147 tag_length
= (const uint16_t *)(const void *) (pd
+ 2);
148 length
+= 2 + 2 + GINT16_FROM_BE(*tag_length
);
150 pd
+= 2 + 2 + GINT16_FROM_BE(*tag_length
);
151 tag
= (const uint16_t *)(const void *) pd
;
159 static bool logcat_read_packet(struct logcat_phdr
*logcat
, FILE_T fh
,
160 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
163 uint16_t payload_length
;
166 struct logger_entry
*log_entry
;
168 if (!wtap_read_bytes_or_eof(fh
, &tmp
, 2, err
, err_info
)) {
171 payload_length
= pletoh16(tmp
);
173 if (logcat
->version
== 1) {
174 packet_size
= (int)sizeof(struct logger_entry
) + payload_length
;
175 } else if (logcat
->version
== 2) {
176 packet_size
= (int)sizeof(struct logger_entry_v2
) + payload_length
;
181 * The maximum value of payload_length is 65535, which, even after
182 * the size of the logger entry structure is added to it, is less
183 * than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
187 ws_buffer_assure_space(buf
, packet_size
);
188 pd
= ws_buffer_start_ptr(buf
);
189 log_entry
= (struct logger_entry
*)(void *) pd
;
191 /* Copy the first two bytes of the packet. */
194 /* Read the rest of the packet. */
195 if (!wtap_read_bytes(fh
, pd
+ 2, packet_size
- 2, err
, err_info
)) {
199 rec
->rec_type
= REC_TYPE_PACKET
;
200 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
201 rec
->presence_flags
= WTAP_HAS_TS
;
202 rec
->ts
.secs
= (time_t) GINT32_FROM_LE(log_entry
->sec
);
203 rec
->ts
.nsecs
= GINT32_FROM_LE(log_entry
->nsec
);
204 rec
->rec_header
.packet_header
.caplen
= packet_size
;
205 rec
->rec_header
.packet_header
.len
= packet_size
;
207 rec
->rec_header
.packet_header
.pseudo_header
.logcat
.version
= logcat
->version
;
212 static bool logcat_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
213 int *err
, char **err_info
, int64_t *data_offset
)
215 *data_offset
= file_tell(wth
->fh
);
217 return logcat_read_packet((struct logcat_phdr
*) wth
->priv
, wth
->fh
,
218 rec
, buf
, err
, err_info
);
221 static bool logcat_seek_read(wtap
*wth
, int64_t seek_off
,
222 wtap_rec
*rec
, Buffer
*buf
,
223 int *err
, char **err_info
)
225 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
228 if (!logcat_read_packet((struct logcat_phdr
*) wth
->priv
, wth
->random_fh
,
229 rec
, buf
, err
, err_info
)) {
231 *err
= WTAP_ERR_SHORT_READ
;
237 wtap_open_return_val
logcat_open(wtap
*wth
, int *err
, char **err_info
)
241 struct logcat_phdr
*logcat
;
243 /* check first 3 packets (or 2 or 1 if EOF) versions to check file format is correct */
244 version
= detect_version(wth
->fh
, err
, err_info
); /* first packet */
246 return WTAP_OPEN_ERROR
; /* I/O error */
248 return WTAP_OPEN_NOT_MINE
; /* not a logcat file */
250 return WTAP_OPEN_NOT_MINE
; /* empty file, so not any type of file */
252 tmp_version
= detect_version(wth
->fh
, err
, err_info
); /* second packet */
253 if (tmp_version
== -1)
254 return WTAP_OPEN_ERROR
; /* I/O error */
255 if (tmp_version
== 0)
256 return WTAP_OPEN_NOT_MINE
; /* not a logcat file */
257 if (tmp_version
!= -2) {
258 /* we've read two packets; do they have the same version? */
259 if (tmp_version
!= version
) {
260 /* no, so this is presumably not a logcat file */
261 return WTAP_OPEN_NOT_MINE
;
264 tmp_version
= detect_version(wth
->fh
, err
, err_info
); /* third packet */
266 return WTAP_OPEN_ERROR
; /* I/O error */
267 if (tmp_version
== 0)
268 return WTAP_OPEN_NOT_MINE
; /* not a logcat file */
271 * we've read three packets and the first two have the same
272 * version; does the third have the same version?
274 if (tmp_version
!= version
) {
275 /* no, so this is presumably not a logcat file */
276 return WTAP_OPEN_NOT_MINE
;
280 if (file_seek(wth
->fh
, 0, SEEK_SET
, err
) == -1)
281 return WTAP_OPEN_ERROR
;
283 logcat
= g_new(struct logcat_phdr
, 1);
284 logcat
->version
= version
;
288 wth
->file_type_subtype
= logcat_file_type_subtype
;
289 wth
->file_encap
= WTAP_ENCAP_LOGCAT
;
290 wth
->snapshot_length
= 0;
292 wth
->subtype_read
= logcat_read
;
293 wth
->subtype_seek_read
= logcat_seek_read
;
294 wth
->file_tsprec
= WTAP_TSPREC_USEC
;
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.
302 wtap_add_generated_idb(wth
);
304 return WTAP_OPEN_MINE
;
307 static int logcat_dump_can_write_encap(int encap
)
309 if (encap
== WTAP_ENCAP_PER_PACKET
)
310 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
312 if (encap
!= WTAP_ENCAP_LOGCAT
&& encap
!= WTAP_ENCAP_WIRESHARK_UPPER_PDU
)
313 return WTAP_ERR_UNWRITABLE_ENCAP
;
318 static bool logcat_binary_dump(wtap_dumper
*wdh
,
320 const uint8_t *pd
, int *err
, char **err_info _U_
)
324 /* We can only write packet records. */
325 if (rec
->rec_type
!= REC_TYPE_PACKET
) {
326 *err
= WTAP_ERR_UNWRITABLE_REC_TYPE
;
331 * Make sure this packet doesn't have a link-layer type that
332 * differs from the one for the file.
334 if (wdh
->file_encap
!= rec
->rec_header
.packet_header
.pkt_encap
) {
335 *err
= WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
339 caplen
= rec
->rec_header
.packet_header
.caplen
;
341 /* Skip EXPORTED_PDU*/
342 if (wdh
->file_encap
== WTAP_ENCAP_WIRESHARK_UPPER_PDU
) {
345 skipped_length
= logcat_exported_pdu_length(pd
);
346 pd
+= skipped_length
;
347 caplen
-= skipped_length
;
350 if (!wtap_dump_file_write(wdh
, pd
, caplen
, err
))
356 static bool logcat_binary_dump_open(wtap_dumper
*wdh
, int *err _U_
,
359 wdh
->subtype_write
= logcat_binary_dump
;
364 static const struct supported_block_type logcat_blocks_supported
[] = {
366 * We support packet blocks, with no comments or other options.
368 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
371 static const struct file_type_subtype_info logcat_info
= {
372 "Android Logcat Binary format", "logcat", "logcat", NULL
,
373 false, BLOCKS_SUPPORTED(logcat_blocks_supported
),
374 logcat_dump_can_write_encap
, logcat_binary_dump_open
, NULL
377 void register_logcat(void)
379 logcat_file_type_subtype
= wtap_register_file_type_subtype(&logcat_info
);
382 * Register name for backwards compatibility with the
383 * wtap_filetypes table in Lua.
385 wtap_register_backwards_compatibility_lua_name("LOGCAT",
386 logcat_file_type_subtype
);
390 * Editor modelines - https://www.wireshark.org/tools/modelines.html
395 * indent-tabs-mode: nil
398 * vi: set shiftwidth=4 tabstop=8 expandtab:
399 * :indentSize=4:tabSize=8:noTabs=true: