Add data structures necessary to support multiple Section Header blocks.
[metze/wireshark/wip.git] / wiretap / logcat.c
1 /* logcat.c
2  *
3  * Copyright 2014, Michal Labedzki for Tieto Corporation
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include "config.h"
21
22 #include <string.h>
23
24 #include "wtap-int.h"
25 #include "file_wrappers.h"
26
27 #include "logcat.h"
28
29 /* Returns '?' for invalid priorities */
30 static gchar get_priority(const guint8 priority) {
31     static gchar priorities[] = "??VDIWEFS";
32
33     if (priority >= (guint8) sizeof(priorities))
34         return '?';
35
36     return priorities[priority];
37 }
38
39 /*
40  * Returns:
41  *
42  *  -2 if we get an EOF at the beginning;
43  *  -1 on an I/O error;
44  *  0 if the record doesn't appear to be valid;
45  *  1-{max gint} as a version number if we got a valid record.
46  */
47 static gint detect_version(FILE_T fh, int *err, gchar **err_info)
48 {
49     guint16                  payload_length;
50     guint16                  hdr_size;
51     guint16                  read_sofar;
52     guint16                  entry_len;
53     gint                     version;
54     struct logger_entry     *log_entry;
55     struct logger_entry_v2  *log_entry_v2;
56     guint8                  *buffer;
57     guint16                  tmp;
58     guint8                  *msg_payload;
59     guint8                  *msg_part;
60     guint8                  *msg_end;
61     guint16                  msg_len;
62
63     /* 16-bit payload length */
64     if (!wtap_read_bytes_or_eof(fh, &tmp, 2, err, err_info)) {
65         if (*err == 0) {
66             /*
67              * Got an EOF at the beginning.
68              */
69             return -2;
70         }
71         if (*err != WTAP_ERR_SHORT_READ)
72             return -1;
73         return 0;
74     }
75     payload_length = pletoh16(&tmp);
76
77     /* must contain at least priority and two nulls as separator */
78     if (payload_length < 3)
79         return 0;
80     /* payload length may not exceed the maximum payload size */
81     if (payload_length > LOGGER_ENTRY_MAX_PAYLOAD)
82         return 0;
83
84     /* 16-bit header length (or padding, equal to 0x0000) */
85     if (!wtap_read_bytes(fh, &tmp, 2, err, err_info)) {
86         if (*err != WTAP_ERR_SHORT_READ)
87             return -1;
88         return 0;
89     }
90     hdr_size = pletoh16(&tmp);
91     read_sofar = 4;
92
93     /* ensure buffer is large enough for all versions */
94     buffer = (guint8 *) g_malloc(sizeof(*log_entry_v2) + payload_length);
95     log_entry_v2 = (struct logger_entry_v2 *)(void *) buffer;
96     log_entry = (struct logger_entry *)(void *) buffer;
97
98     /* cannot rely on __pad being 0 for v1, use heuristics to find out what
99      * version is in use. First assume the smallest msg. */
100     for (version = 1; version <= 2; ++version) {
101         if (version == 1) {
102             msg_payload = (guint8 *) (log_entry + 1);
103             entry_len = sizeof(*log_entry) + payload_length;
104         } else if (version == 2) {
105             /* v2 is 4 bytes longer */
106             msg_payload = (guint8 *) (log_entry_v2 + 1);
107             entry_len = sizeof(*log_entry_v2) + payload_length;
108             if (hdr_size != sizeof(*log_entry_v2))
109                 continue;
110         } else {
111             continue;
112         }
113
114         if (!wtap_read_bytes(fh, buffer + read_sofar, entry_len - read_sofar, err, err_info)) {
115             g_free(buffer);
116             if (*err != WTAP_ERR_SHORT_READ)
117                 return -1;
118             return 0;
119         }
120         read_sofar += entry_len - read_sofar;
121
122         /* A v2 msg has a 32-bit userid instead of v1 priority */
123         if (get_priority(msg_payload[0]) == '?')
124             continue;
125
126         /* Is there a terminating '\0' for the tag? */
127         msg_part = (guint8 *) memchr(msg_payload, '\0', payload_length - 1);
128         if (msg_part == NULL)
129             continue;
130
131         /* if msg is '\0'-terminated, is it equal to the payload len? */
132         ++msg_part;
133         msg_len = (guint16)(payload_length - (msg_part - msg_payload));
134         msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
135         /* is the end of the buffer (-1) equal to the end of msg? */
136         if (msg_end && (msg_payload + payload_length - 1 != msg_end))
137             continue;
138
139         g_free(buffer);
140         return version;
141     }
142
143     /* No version number is valid */
144     g_free(buffer);
145     return 0;
146 }
147
148 gint logcat_exported_pdu_length(const guint8 *pd) {
149     const guint16  *tag;
150     const guint16  *tag_length;
151     gint            length = 0;
152
153     tag = (const guint16 *)(const void *) pd;
154
155     while(GINT16_FROM_BE(*tag)) {
156         tag_length = (const guint16 *)(const void *) (pd + 2);
157         length += 2 + 2 + GINT16_FROM_BE(*tag_length);
158
159         pd += 2 + 2 + GINT16_FROM_BE(*tag_length);
160         tag = (const guint16 *)(const void *) pd;
161     }
162
163     length += 2 + 2;
164
165     return length;
166 }
167
168 static gboolean logcat_read_packet(struct logcat_phdr *logcat, FILE_T fh,
169     struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
170 {
171     gint                 packet_size;
172     guint16              payload_length;
173     guint                tmp[2];
174     guint8              *pd;
175     struct logger_entry *log_entry;
176
177     if (!wtap_read_bytes_or_eof(fh, &tmp, 2, err, err_info)) {
178         return FALSE;
179     }
180     payload_length = pletoh16(tmp);
181
182     if (logcat->version == 1) {
183         packet_size = (gint)sizeof(struct logger_entry) + payload_length;
184     } else if (logcat->version == 2) {
185         packet_size = (gint)sizeof(struct logger_entry_v2) + payload_length;
186     } else {
187         return FALSE;
188     }
189     /*
190      * The maximum value of payload_length is 65535, which, even after
191      * the size of the logger entry structure is added to it, is less
192      * than WTAP_MAX_PACKET_SIZE will ever be, so we don't need to check
193      * it.
194      */
195
196     ws_buffer_assure_space(buf, packet_size);
197     pd = ws_buffer_start_ptr(buf);
198     log_entry = (struct logger_entry *)(void *) pd;
199
200     /* Copy the first two bytes of the packet. */
201     memcpy(pd, tmp, 2);
202
203     /* Read the rest of the packet. */
204     if (!wtap_read_bytes(fh, pd + 2, packet_size - 2, err, err_info)) {
205         return FALSE;
206     }
207
208     phdr->rec_type = REC_TYPE_PACKET;
209     phdr->presence_flags = WTAP_HAS_TS;
210     phdr->ts.secs = (time_t) GINT32_FROM_LE(log_entry->sec);
211     phdr->ts.nsecs = GINT32_FROM_LE(log_entry->nsec);
212     phdr->caplen = packet_size;
213     phdr->len = packet_size;
214
215     phdr->pseudo_header.logcat.version = logcat->version;
216
217     return TRUE;
218 }
219
220 static gboolean logcat_read(wtap *wth, int *err, gchar **err_info,
221     gint64 *data_offset)
222 {
223     *data_offset = file_tell(wth->fh);
224
225     return logcat_read_packet((struct logcat_phdr *) wth->priv, wth->fh,
226         &wth->phdr, wth->frame_buffer, err, err_info);
227 }
228
229 static gboolean logcat_seek_read(wtap *wth, gint64 seek_off,
230     struct wtap_pkthdr *phdr, Buffer *buf,
231     int *err, gchar **err_info)
232 {
233     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
234         return FALSE;
235
236     if (!logcat_read_packet((struct logcat_phdr *) wth->priv, wth->random_fh,
237          phdr, buf, err, err_info)) {
238         if (*err == 0)
239             *err = WTAP_ERR_SHORT_READ;
240         return FALSE;
241     }
242     return TRUE;
243 }
244
245 wtap_open_return_val logcat_open(wtap *wth, int *err, gchar **err_info)
246 {
247     gint                version;
248     gint                tmp_version;
249     struct logcat_phdr *logcat;
250
251     /* check first 3 packets (or 2 or 1 if EOF) versions to check file format is correct */
252     version = detect_version(wth->fh, err, err_info); /* first packet */
253     if (version == -1)
254         return WTAP_OPEN_ERROR; /* I/O error */
255     if (version == 0)
256         return WTAP_OPEN_NOT_MINE;  /* not a logcat file */
257     if (version == -2)
258         return WTAP_OPEN_NOT_MINE;  /* empty file, so not any type of file */
259
260     tmp_version = detect_version(wth->fh, err, err_info); /* second packet */
261     if (tmp_version == -1)
262         return WTAP_OPEN_ERROR; /* I/O error */
263     if (tmp_version == 0)
264         return WTAP_OPEN_NOT_MINE;  /* not a logcat file */
265     if (tmp_version != -2) {
266         /* we've read two packets; do they have the same version? */
267         if (tmp_version != version) {
268             /* no, so this is presumably not a logcat file */
269             return WTAP_OPEN_NOT_MINE;
270         }
271
272         tmp_version = detect_version(wth->fh, err, err_info); /* third packet */
273         if (tmp_version < 0)
274             return WTAP_OPEN_ERROR; /* I/O error */
275         if (tmp_version == 0)
276             return WTAP_OPEN_NOT_MINE;  /* not a logcat file */
277         if (tmp_version != -2) {
278             /*
279              * we've read three packets and the first two have the same
280              * version; does the third have the same version?
281              */
282             if (tmp_version != version) {
283                 /* no, so this is presumably not a logcat file */
284                 return WTAP_OPEN_NOT_MINE;
285             }
286         }
287     }
288
289     if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
290         return WTAP_OPEN_ERROR;
291
292     logcat = (struct logcat_phdr *) g_malloc(sizeof(struct logcat_phdr));
293     logcat->version = version;
294
295     wth->priv = logcat;
296
297     wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT;
298     wth->file_encap = WTAP_ENCAP_LOGCAT;
299     wth->snapshot_length = 0;
300
301     wth->subtype_read = logcat_read;
302     wth->subtype_seek_read = logcat_seek_read;
303     wth->file_tsprec = WTAP_TSPREC_USEC;
304
305     return WTAP_OPEN_MINE;
306 }
307
308 int logcat_dump_can_write_encap(int encap)
309 {
310     if (encap == WTAP_ENCAP_PER_PACKET)
311         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
312
313     if (encap != WTAP_ENCAP_LOGCAT && encap != WTAP_ENCAP_WIRESHARK_UPPER_PDU)
314         return WTAP_ERR_UNWRITABLE_ENCAP;
315
316     return 0;
317 }
318
319 static gboolean logcat_binary_dump(wtap_dumper *wdh,
320     const struct wtap_pkthdr *phdr,
321     const guint8 *pd, int *err, gchar **err_info _U_)
322 {
323     int caplen;
324
325     /* We can only write packet records. */
326     if (phdr->rec_type != REC_TYPE_PACKET) {
327         *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
328         return FALSE;
329     }
330
331     caplen = phdr->caplen;
332
333     /* Skip EXPORTED_PDU*/
334     if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
335         gint skipped_length;
336
337         skipped_length = logcat_exported_pdu_length(pd);
338         pd += skipped_length;
339         caplen -= skipped_length;
340     }
341
342     if (!wtap_dump_file_write(wdh, pd, caplen, err))
343         return FALSE;
344
345     wdh->bytes_dumped += caplen;
346
347     return TRUE;
348 }
349
350 gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err)
351 {
352     wdh->subtype_write = logcat_binary_dump;
353
354     switch (wdh->encap) {
355         case WTAP_ENCAP_LOGCAT:
356         case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
357             wdh->tsprecision = WTAP_TSPREC_USEC;
358             break;
359
360         default:
361             *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
362             return FALSE;
363     }
364
365     return TRUE;
366 }
367
368 /*
369  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
370  *
371  * Local variables:
372  * c-basic-offset: 4
373  * tab-width: 8
374  * indent-tabs-mode: nil
375  * End:
376  *
377  * vi: set shiftwidth=4 tabstop=8 expandtab:
378  * :indentSize=4:tabSize=8:noTabs=true:
379  */