/src/wireshark/wiretap/capsa.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* capsa.c |
2 | | * |
3 | | * Wiretap Library |
4 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
5 | | * |
6 | | * SPDX-License-Identifier: GPL-2.0-or-later |
7 | | */ |
8 | | |
9 | | #include "config.h" |
10 | | #include "capsa.h" |
11 | | |
12 | | #include <string.h> |
13 | | #include "wtap-int.h" |
14 | | #include "file_wrappers.h" |
15 | | #include <wsutil/ws_assert.h> |
16 | | |
17 | | /* |
18 | | * A file begins with a header containing: |
19 | | * |
20 | | * a 4-byte magic number, with 'c', 'p', 's', 'e'; |
21 | | * |
22 | | * either a 2-byte little-endian "format indicator" (version number?), |
23 | | * or a 1-byte major version number followed by a 1-byte minor version |
24 | | * number, or a 1-byte "format indicator" followed by something else |
25 | | * that's always been 0; |
26 | | * |
27 | | * a 2-byte 0xe8 0x03 (1000 - a data rate? megabits/second?) |
28 | | * |
29 | | * 4 bytes of 0x01 0x00 0x01 0x00; |
30 | | * |
31 | | * either a 4-byte little-endian file size followed by 0x00 0x00 0x00 0x00 |
32 | | * or an 8-byte little-endian file size; |
33 | | * |
34 | | * a 4-byte little-endian packet count (in dns_error_of_udp, it exceeds?) |
35 | | * |
36 | | * a 4-byte little-endian number? |
37 | | * |
38 | | * hex 2c 01 c8 00 00 00 da 36 00 00 00 00 00 00; |
39 | | * |
40 | | * the same 4-byte little-endian number as above (yes, misaligned); |
41 | | * |
42 | | * 0x01 or 0x03; |
43 | | * |
44 | | * a bunch of 0s, up to an offset of 0x36d6; |
45 | | * |
46 | | * more stuff. |
47 | | * |
48 | | * Following that is a sequence of { record offset block, up to 200 records } |
49 | | * pairs. |
50 | | * |
51 | | * A record offset block has 1 byte with the value 0xfe, a sequence of |
52 | | * up to 200 4-byte little-endian record offsets, and 4 or more bytes |
53 | | * of unknown data, making the block 805 bytes long. |
54 | | * |
55 | | * The record offsets are offsets, from the beginning of the record offset |
56 | | * block (i.e., from the 0xfe byte), of the records following the block. |
57 | | */ |
58 | | |
59 | | /* Magic number in Capsa files. */ |
60 | | static const char capsa_magic[] = { |
61 | | 'c', 'p', 's', 'e' |
62 | | }; |
63 | | |
64 | | /* |
65 | | * Before each group of 200 or fewer records there's a block of frame |
66 | | * offsets, giving the offsets, from the beginning of that block minus |
67 | | * one(1), of the next N records. |
68 | | */ |
69 | 0 | #define N_RECORDS_PER_GROUP 200 |
70 | | |
71 | | /* Capsa (format indicator 1) record header. */ |
72 | | struct capsarec_hdr { |
73 | | uint32_t unknown1; /* low-order 32 bits of a number? */ |
74 | | uint32_t unknown2; /* 0x00 0x00 0x00 0x00 */ |
75 | | uint32_t timestamplo; /* low-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */ |
76 | | uint32_t timestamphi; /* high-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */ |
77 | | uint16_t rec_len; /* length of record */ |
78 | | uint16_t incl_len; /* number of octets captured in file */ |
79 | | uint16_t orig_len; /* actual length of packet */ |
80 | | uint16_t unknown5; /* 0x00 0x00 */ |
81 | | uint8_t count1; /* count1*4 bytes after unknown8 */ |
82 | | uint8_t count2; /* count2*4 bytes after that */ |
83 | | uint16_t unknown7; /* 0x01 0x10 */ |
84 | | uint32_t unknown8; /* 0x00 0x00 0x00 0x00 or random numbers */ |
85 | | }; |
86 | | |
87 | | /* Packet Builder (format indicator 2) record header. */ |
88 | | struct pbrec_hdr { |
89 | | uint16_t rec_len; /* length of record */ |
90 | | uint16_t incl_len; /* number of octets captured in file */ |
91 | | uint16_t orig_len; /* actual length of packet */ |
92 | | uint16_t unknown1; |
93 | | uint16_t unknown2; |
94 | | uint16_t unknown3; |
95 | | uint32_t unknown4; |
96 | | uint32_t timestamplo; /* low-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */ |
97 | | uint32_t timestamphi; /* high-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */ |
98 | | uint32_t unknown5; |
99 | | uint32_t unknown6; |
100 | | }; |
101 | | |
102 | | typedef struct { |
103 | | uint16_t format_indicator; |
104 | | uint32_t number_of_frames; |
105 | | uint32_t frame_count; |
106 | | int64_t base_offset; |
107 | | uint32_t record_offsets[N_RECORDS_PER_GROUP]; |
108 | | } capsa_t; |
109 | | |
110 | | static bool capsa_read(wtap *wth, wtap_rec *rec, |
111 | | int *err, char **err_info, int64_t *data_offset); |
112 | | static bool capsa_seek_read(wtap *wth, int64_t seek_off, |
113 | | wtap_rec *rec, int *err, char **err_info); |
114 | | static int capsa_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, |
115 | | int *err, char **err_info); |
116 | | |
117 | | static int capsa_file_type_subtype = -1; |
118 | | static int packet_builder_file_type_subtype = -1; |
119 | | |
120 | | void register_capsa(void); |
121 | | |
122 | | wtap_open_return_val capsa_open(wtap *wth, int *err, char **err_info) |
123 | 0 | { |
124 | 0 | char magic[sizeof capsa_magic]; |
125 | 0 | uint16_t format_indicator; |
126 | 0 | int file_type_subtype; |
127 | 0 | uint32_t number_of_frames; |
128 | 0 | capsa_t *capsa; |
129 | | |
130 | | /* Read in the string that should be at the start of a Capsa file */ |
131 | 0 | if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info)) { |
132 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
133 | 0 | return WTAP_OPEN_ERROR; |
134 | 0 | return WTAP_OPEN_NOT_MINE; |
135 | 0 | } |
136 | | |
137 | 0 | if (memcmp(magic, capsa_magic, sizeof capsa_magic) != 0) { |
138 | 0 | return WTAP_OPEN_NOT_MINE; |
139 | 0 | } |
140 | | |
141 | | /* Read the mysterious "format indicator" */ |
142 | 0 | if (!wtap_read_bytes(wth->fh, &format_indicator, sizeof format_indicator, |
143 | 0 | err, err_info)) |
144 | 0 | return WTAP_OPEN_ERROR; |
145 | 0 | format_indicator = GUINT16_FROM_LE(format_indicator); |
146 | | |
147 | | /* |
148 | | * Make sure it's a format we support. |
149 | | */ |
150 | 0 | switch (format_indicator) { |
151 | | |
152 | 0 | case 1: /* Capsa */ |
153 | 0 | file_type_subtype = capsa_file_type_subtype; |
154 | 0 | break; |
155 | | |
156 | 0 | case 2: /* Packet Builder */ |
157 | 0 | file_type_subtype = packet_builder_file_type_subtype; |
158 | 0 | break; |
159 | | |
160 | 0 | default: |
161 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
162 | 0 | *err_info = ws_strdup_printf("capsa: format indicator %u unsupported", |
163 | 0 | format_indicator); |
164 | 0 | return WTAP_OPEN_ERROR; |
165 | 0 | } |
166 | | |
167 | | /* |
168 | | * Link speed, in megabytes/second? |
169 | | */ |
170 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 2, err, err_info)) |
171 | 0 | return WTAP_OPEN_ERROR; |
172 | | |
173 | | /* |
174 | | * Flags of some sort? Four 1-byte numbers, two of which are 1 |
175 | | * and two of which are zero? Two 2-byte numbers or flag fields, |
176 | | * both of which are 1? |
177 | | */ |
178 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info)) |
179 | 0 | return WTAP_OPEN_ERROR; |
180 | | |
181 | | /* |
182 | | * File size, in bytes. |
183 | | */ |
184 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info)) |
185 | 0 | return WTAP_OPEN_ERROR; |
186 | | |
187 | | /* |
188 | | * Zeroes? Or upper 4 bytes of file size? |
189 | | */ |
190 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info)) |
191 | 0 | return WTAP_OPEN_ERROR; |
192 | | |
193 | | /* |
194 | | * Count of packets. |
195 | | */ |
196 | 0 | if (!wtap_read_bytes(wth->fh, &number_of_frames, sizeof number_of_frames, |
197 | 0 | err, err_info)) |
198 | 0 | return WTAP_OPEN_ERROR; |
199 | 0 | number_of_frames = GUINT32_FROM_LE(number_of_frames); |
200 | | |
201 | | /* |
202 | | * Skip past what we think is file header. |
203 | | */ |
204 | 0 | if (!file_seek(wth->fh, 0x44ef, SEEK_SET, err)) |
205 | 0 | return WTAP_OPEN_ERROR; |
206 | | |
207 | 0 | wth->file_type_subtype = file_type_subtype; |
208 | 0 | capsa = g_new(capsa_t, 1); |
209 | 0 | capsa->format_indicator = format_indicator; |
210 | 0 | capsa->number_of_frames = number_of_frames; |
211 | 0 | capsa->frame_count = 0; |
212 | 0 | wth->priv = (void *)capsa; |
213 | 0 | wth->subtype_read = capsa_read; |
214 | 0 | wth->subtype_seek_read = capsa_seek_read; |
215 | | /* |
216 | | * XXX - we've never seen a Wi-Fi Capsa capture, so we don't |
217 | | * yet know how to handle them. |
218 | | */ |
219 | 0 | wth->file_encap = WTAP_ENCAP_ETHERNET; |
220 | 0 | wth->snapshot_length = 0; /* not available in header */ |
221 | 0 | wth->file_tsprec = WTAP_TSPREC_USEC; |
222 | | |
223 | | /* |
224 | | * Add an IDB; we don't know how many interfaces were |
225 | | * involved, so we just say one interface, about which |
226 | | * we only know the link-layer type, snapshot length, |
227 | | * and time stamp resolution. |
228 | | */ |
229 | 0 | wtap_add_generated_idb(wth); |
230 | |
|
231 | 0 | return WTAP_OPEN_MINE; |
232 | 0 | } |
233 | | |
234 | | /* Read the next packet */ |
235 | | static bool capsa_read(wtap *wth, wtap_rec *rec, |
236 | | int *err, char **err_info, int64_t *data_offset) |
237 | 0 | { |
238 | 0 | capsa_t *capsa = (capsa_t *)wth->priv; |
239 | 0 | uint32_t frame_within_block; |
240 | 0 | int padbytes; |
241 | |
|
242 | 0 | if (capsa->frame_count == capsa->number_of_frames) { |
243 | | /* |
244 | | * No more frames left. Return an EOF. |
245 | | */ |
246 | 0 | *err = 0; |
247 | 0 | return false; |
248 | 0 | } |
249 | 0 | frame_within_block = capsa->frame_count % N_RECORDS_PER_GROUP; |
250 | 0 | if (frame_within_block == 0) { |
251 | | /* |
252 | | * Here's a record offset block. |
253 | | * Get the offset of the block, and then skip the |
254 | | * first byte. |
255 | | */ |
256 | 0 | capsa->base_offset = file_tell(wth->fh); |
257 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 1, err, err_info)) |
258 | 0 | return false; |
259 | | |
260 | | /* |
261 | | * Now read the record offsets. |
262 | | */ |
263 | 0 | if (!wtap_read_bytes(wth->fh, &capsa->record_offsets, |
264 | 0 | sizeof capsa->record_offsets, err, err_info)) |
265 | 0 | return false; |
266 | | |
267 | | /* |
268 | | * And finish processing all 805 bytes by skipping |
269 | | * the last 4 bytes. |
270 | | */ |
271 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info)) |
272 | 0 | return false; |
273 | 0 | } |
274 | | |
275 | 0 | *data_offset = capsa->base_offset + |
276 | 0 | GUINT32_FROM_LE(capsa->record_offsets[frame_within_block]); |
277 | 0 | if (!file_seek(wth->fh, *data_offset, SEEK_SET, err)) |
278 | 0 | return false; |
279 | | |
280 | 0 | padbytes = capsa_read_packet(wth, wth->fh, rec, err, err_info); |
281 | 0 | if (padbytes == -1) |
282 | 0 | return false; |
283 | | |
284 | | /* |
285 | | * Skip over the padding, if any. |
286 | | */ |
287 | 0 | if (padbytes != 0) { |
288 | 0 | if (!wtap_read_bytes(wth->fh, NULL, padbytes, err, err_info)) |
289 | 0 | return false; |
290 | 0 | } |
291 | | |
292 | 0 | capsa->frame_count++; |
293 | |
|
294 | 0 | return true; |
295 | 0 | } |
296 | | |
297 | | static bool |
298 | | capsa_seek_read(wtap *wth, int64_t seek_off, |
299 | | wtap_rec *rec, int *err, char **err_info) |
300 | 0 | { |
301 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
302 | 0 | return false; |
303 | | |
304 | 0 | if (capsa_read_packet(wth, wth->random_fh, rec, err, err_info) == -1) { |
305 | 0 | if (*err == 0) |
306 | 0 | *err = WTAP_ERR_SHORT_READ; |
307 | 0 | return false; |
308 | 0 | } |
309 | 0 | return true; |
310 | 0 | } |
311 | | |
312 | | static int |
313 | | capsa_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, |
314 | | int *err, char **err_info) |
315 | 0 | { |
316 | 0 | capsa_t *capsa = (capsa_t *)wth->priv; |
317 | 0 | struct capsarec_hdr capsarec_hdr; |
318 | 0 | struct pbrec_hdr pbrec_hdr; |
319 | 0 | uint32_t rec_size; |
320 | 0 | uint32_t packet_size; |
321 | 0 | uint32_t orig_size; |
322 | 0 | uint32_t header_size; |
323 | 0 | uint64_t timestamp; |
324 | | |
325 | | /* Read record header. */ |
326 | 0 | switch (capsa->format_indicator) { |
327 | | |
328 | 0 | case 1: |
329 | 0 | if (!wtap_read_bytes_or_eof(fh, &capsarec_hdr, |
330 | 0 | sizeof capsarec_hdr, err, err_info)) |
331 | 0 | return -1; |
332 | 0 | rec_size = GUINT16_FROM_LE(capsarec_hdr.rec_len); |
333 | 0 | orig_size = GUINT16_FROM_LE(capsarec_hdr.orig_len); |
334 | 0 | packet_size = GUINT16_FROM_LE(capsarec_hdr.incl_len); |
335 | 0 | header_size = sizeof capsarec_hdr; |
336 | 0 | timestamp = (((uint64_t)GUINT32_FROM_LE(capsarec_hdr.timestamphi))<<32) + GUINT32_FROM_LE(capsarec_hdr.timestamplo); |
337 | | |
338 | | /* |
339 | | * OK, the rest of this is variable-length. |
340 | | * We skip: (count1+count2)*4 bytes. |
341 | | * XXX - what is that? Measured statistics? |
342 | | * Calculated statistics? |
343 | | */ |
344 | 0 | if (!wtap_read_bytes(fh, NULL, |
345 | 0 | (capsarec_hdr.count1 + capsarec_hdr.count2)*4, |
346 | 0 | err, err_info)) |
347 | 0 | return -1; |
348 | 0 | header_size += (capsarec_hdr.count1 + capsarec_hdr.count2)*4; |
349 | 0 | break; |
350 | | |
351 | 0 | case 2: |
352 | 0 | if (!wtap_read_bytes_or_eof(fh, &pbrec_hdr, |
353 | 0 | sizeof pbrec_hdr, err, err_info)) |
354 | 0 | return -1; |
355 | 0 | rec_size = GUINT16_FROM_LE(pbrec_hdr.rec_len); |
356 | 0 | orig_size = GUINT16_FROM_LE(pbrec_hdr.orig_len); |
357 | 0 | packet_size = GUINT16_FROM_LE(pbrec_hdr.incl_len); |
358 | 0 | header_size = sizeof pbrec_hdr; |
359 | 0 | timestamp = (((uint64_t)GUINT32_FROM_LE(pbrec_hdr.timestamphi))<<32) + GUINT32_FROM_LE(pbrec_hdr.timestamplo); |
360 | | /* |
361 | | * XXX - from the results of some conversions between |
362 | | * Capsa format and pcap by Colasoft Packet Builder, |
363 | | * I do not trust its conversion of time stamps (at |
364 | | * least one of Colasoft's sample files, when |
365 | | * converted to pcap format, has, as its time stamps, |
366 | | * time stamps on the day after the conversion was |
367 | | * done, which seems like more than just coincidence). |
368 | | */ |
369 | 0 | break; |
370 | | |
371 | 0 | default: |
372 | 0 | ws_assert_not_reached(); |
373 | 0 | *err = WTAP_ERR_INTERNAL; |
374 | 0 | *err_info = ws_strdup_printf("capsa: format indicator is %u", capsa->format_indicator); |
375 | 0 | return -1; |
376 | 0 | } |
377 | 0 | if (orig_size > WTAP_MAX_PACKET_SIZE_STANDARD) { |
378 | | /* |
379 | | * Probably a corrupt capture file; don't blow up trying |
380 | | * to allocate space for an immensely-large packet. |
381 | | */ |
382 | 0 | *err = WTAP_ERR_BAD_FILE; |
383 | 0 | *err_info = ws_strdup_printf("capsa: File has %u-byte original length, bigger than maximum of %u", |
384 | 0 | orig_size, WTAP_MAX_PACKET_SIZE_STANDARD); |
385 | 0 | return -1; |
386 | 0 | } |
387 | 0 | if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) { |
388 | | /* |
389 | | * Probably a corrupt capture file; don't blow up trying |
390 | | * to allocate space for an immensely-large packet. |
391 | | */ |
392 | 0 | *err = WTAP_ERR_BAD_FILE; |
393 | 0 | *err_info = ws_strdup_printf("capsa: File has %u-byte packet, bigger than maximum of %u", |
394 | 0 | packet_size, WTAP_MAX_PACKET_SIZE_STANDARD); |
395 | 0 | return -1; |
396 | 0 | } |
397 | 0 | if (header_size + packet_size > rec_size) { |
398 | | /* |
399 | | * Probably a corrupt capture file. |
400 | | */ |
401 | 0 | *err = WTAP_ERR_BAD_FILE; |
402 | 0 | *err_info = ws_strdup_printf("capsa: File has %u-byte packet with %u-byte record header, bigger than record size %u", |
403 | 0 | packet_size, header_size, rec_size); |
404 | 0 | return -1; |
405 | 0 | } |
406 | | |
407 | | /* |
408 | | * The "on the wire" record size always includes the CRC. |
409 | | * If it's greater than the "captured" size by 4, then |
410 | | * we subtract 4 from it, to reflect the way the "on the wire" |
411 | | * record size works for other file formats. |
412 | | */ |
413 | 0 | if (orig_size == packet_size + 4) |
414 | 0 | orig_size = packet_size; |
415 | | |
416 | | /* |
417 | | * We assume there's no FCS in this frame. |
418 | | * XXX - is there ever one? |
419 | | */ |
420 | 0 | rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0; |
421 | |
|
422 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
423 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
424 | 0 | rec->rec_header.packet_header.caplen = packet_size; |
425 | 0 | rec->rec_header.packet_header.len = orig_size; |
426 | 0 | rec->presence_flags = WTAP_HAS_CAP_LEN|WTAP_HAS_TS; |
427 | 0 | rec->ts.secs = (time_t)(timestamp / 1000000); |
428 | 0 | rec->ts.nsecs = ((int)(timestamp % 1000000))*1000; |
429 | | |
430 | | /* |
431 | | * Read the packet data. |
432 | | */ |
433 | 0 | if (!wtap_read_bytes_buffer(fh, &rec->data, |
434 | 0 | rec->rec_header.packet_header.caplen, err, err_info)) |
435 | 0 | return -1; /* failed */ |
436 | | |
437 | 0 | return rec_size - (header_size + packet_size); |
438 | 0 | } |
439 | | |
440 | | static const struct supported_block_type capsa_blocks_supported[] = { |
441 | | /* |
442 | | * We support packet blocks, with no comments or other options. |
443 | | */ |
444 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
445 | | }; |
446 | | |
447 | | static const struct file_type_subtype_info capsa_info = { |
448 | | "Colasoft Capsa format", "capsa", "cscpkt", NULL, |
449 | | false, BLOCKS_SUPPORTED(capsa_blocks_supported), |
450 | | NULL, NULL, NULL |
451 | | }; |
452 | | |
453 | | static const struct supported_block_type packet_builder_blocks_supported[] = { |
454 | | /* |
455 | | * We support packet blocks, with no comments or other options. |
456 | | */ |
457 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
458 | | }; |
459 | | |
460 | | static const struct file_type_subtype_info packet_builder_info = { |
461 | | "Colasoft Packet Builder format", "colasoft-pb", "cscpkt", NULL, |
462 | | false, BLOCKS_SUPPORTED(packet_builder_blocks_supported), |
463 | | NULL, NULL, NULL |
464 | | }; |
465 | | |
466 | | void register_capsa(void) |
467 | 14 | { |
468 | 14 | capsa_file_type_subtype = wtap_register_file_type_subtype(&capsa_info); |
469 | 14 | packet_builder_file_type_subtype = wtap_register_file_type_subtype(&packet_builder_info); |
470 | | |
471 | | /* |
472 | | * Register names for backwards compatibility with the |
473 | | * wtap_filetypes table in Lua. |
474 | | */ |
475 | 14 | wtap_register_backwards_compatibility_lua_name("COLASOFT_CAPSA", |
476 | 14 | capsa_file_type_subtype); |
477 | 14 | wtap_register_backwards_compatibility_lua_name("COLASOFT_PACKET_BUILDER", |
478 | 14 | packet_builder_file_type_subtype); |
479 | 14 | } |
480 | | |
481 | | /* |
482 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
483 | | * |
484 | | * Local variables: |
485 | | * c-basic-offset: 8 |
486 | | * tab-width: 8 |
487 | | * indent-tabs-mode: t |
488 | | * End: |
489 | | * |
490 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
491 | | * :indentSize=8:tabSize=8:noTabs=false: |
492 | | */ |