/src/wireshark/wiretap/peekclassic.c
Line | Count | Source |
1 | | /* peekclassic.c |
2 | | * Routines for opening files in what Savvius (formerly WildPackets) calls |
3 | | * the classic file format in the description of their "PeekRdr Sample |
4 | | * Application" (C++ source code to read their capture files, downloading |
5 | | * of which requires a maintenance contract, so it's not free as in beer |
6 | | * and probably not as in speech, either). |
7 | | * |
8 | | * As that description says, it's used by AiroPeek and AiroPeek NX prior |
9 | | * to 2.0, EtherPeek prior to 6.0, and EtherPeek NX prior to 3.0. It |
10 | | * was probably also used by TokenPeek. |
11 | | * |
12 | | * This handles versions 5, 6, and 7 of that format (the format version |
13 | | * number is what appears in the file, and is distinct from the application |
14 | | * version number). |
15 | | * |
16 | | * Copyright (c) 2001, Daniel Thompson <d.thompson@gmx.net> |
17 | | * |
18 | | * Wiretap Library |
19 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
20 | | * |
21 | | * SPDX-License-Identifier: GPL-2.0-or-later |
22 | | */ |
23 | | |
24 | | #include "config.h" |
25 | | #include "peekclassic.h" |
26 | | |
27 | | #include <string.h> |
28 | | |
29 | | #include <wsutil/epochs.h> |
30 | | #include <wsutil/802_11-utils.h> |
31 | | #include <wsutil/ws_assert.h> |
32 | | #include <wsutil/pint.h> |
33 | | |
34 | | #include "wtap_module.h" |
35 | | #include "file_wrappers.h" |
36 | | |
37 | | /* CREDITS |
38 | | * |
39 | | * This file decoder could not have been written without examining how |
40 | | * tcptrace (http://www.tcptrace.org/) handles EtherPeek files. |
41 | | */ |
42 | | |
43 | | /* master header */ |
44 | | typedef struct peekclassic_master_header { |
45 | | uint8_t version; |
46 | | uint8_t status; |
47 | | } peekclassic_master_header_t; |
48 | | #define PEEKCLASSIC_MASTER_HDR_SIZE 2 |
49 | | |
50 | | /* secondary header (V5,V6,V7) */ |
51 | | typedef struct peekclassic_v567_header { |
52 | | uint32_t filelength; |
53 | | uint32_t numPackets; |
54 | | uint32_t timeDate; |
55 | | uint32_t timeStart; |
56 | | uint32_t timeStop; |
57 | | uint32_t mediaType; /* Media Type Ethernet=0 Token Ring = 1 */ |
58 | | uint32_t physMedium; /* Physical Medium native=0 802.1=1 */ |
59 | | uint32_t appVers; /* App Version Number Maj.Min.Bug.Build */ |
60 | | uint32_t linkSpeed; /* Link Speed Bits/sec */ |
61 | | uint32_t reserved[3]; |
62 | | } peekclassic_v567_header_t; |
63 | | #define PEEKCLASSIC_V567_HDR_SIZE 48 |
64 | | |
65 | | /* full header */ |
66 | | typedef struct peekclassic_header { |
67 | | peekclassic_master_header_t master; |
68 | | union { |
69 | | peekclassic_v567_header_t v567; |
70 | | } secondary; |
71 | | } peekclassic_header_t; |
72 | | |
73 | | /* |
74 | | * Packet header (V5, V6). |
75 | | * |
76 | | * NOTE: the time stamp, although it's a 32-bit number, is only aligned |
77 | | * on a 16-bit boundary. (Does this date back to 68K Macs? The 68000 |
78 | | * only required 16-bit alignment of 32-bit quantities, as did the 68010, |
79 | | * and the 68020/68030/68040 required no alignment.) |
80 | | * |
81 | | * As such, we cannot declare this as a C structure, as compilers on |
82 | | * most platforms will put 2 bytes of padding before the time stamp to |
83 | | * align it on a 32-bit boundary. |
84 | | * |
85 | | * So, instead, we #define numbers as the offsets of the fields. |
86 | | */ |
87 | 0 | #define PEEKCLASSIC_V56_LENGTH_OFFSET 0 |
88 | 0 | #define PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET 2 |
89 | 0 | #define PEEKCLASSIC_V56_FLAGS_OFFSET 4 |
90 | | #define PEEKCLASSIC_V56_STATUS_OFFSET 5 |
91 | 0 | #define PEEKCLASSIC_V56_TIMESTAMP_OFFSET 6 |
92 | | #define PEEKCLASSIC_V56_DESTNUM_OFFSET 10 |
93 | | #define PEEKCLASSIC_V56_SRCNUM_OFFSET 12 |
94 | | #define PEEKCLASSIC_V56_PROTONUM_OFFSET 14 |
95 | | #define PEEKCLASSIC_V56_PROTOSTR_OFFSET 16 |
96 | | #define PEEKCLASSIC_V56_FILTERNUM_OFFSET 24 |
97 | | #define PEEKCLASSIC_V56_PKT_SIZE 26 |
98 | | |
99 | | /* 64-bit time in micro seconds from the (Mac) epoch */ |
100 | | typedef struct peekclassic_utime { |
101 | | uint32_t upper; |
102 | | uint32_t lower; |
103 | | } peekclassic_utime; |
104 | | |
105 | | /* |
106 | | * Packet header (V7). |
107 | | * |
108 | | * This doesn't have the same alignment problem, but we do it with |
109 | | * #defines anyway. |
110 | | */ |
111 | | #define PEEKCLASSIC_V7_PROTONUM_OFFSET 0 |
112 | 0 | #define PEEKCLASSIC_V7_LENGTH_OFFSET 2 |
113 | 0 | #define PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET 4 |
114 | 0 | #define PEEKCLASSIC_V7_FLAGS_OFFSET 6 |
115 | 0 | #define PEEKCLASSIC_V7_STATUS_OFFSET 7 |
116 | 0 | #define PEEKCLASSIC_V7_TIMESTAMP_OFFSET 8 |
117 | | #define PEEKCLASSIC_V7_PKT_SIZE 16 |
118 | | |
119 | | /* |
120 | | * Flag bits. |
121 | | */ |
122 | | #define FLAGS_CONTROL_FRAME 0x01 /* Frame is a control frame */ |
123 | 0 | #define FLAGS_HAS_CRC_ERROR 0x02 /* Frame has a CRC error */ |
124 | | #define FLAGS_HAS_FRAME_ERROR 0x04 /* Frame has a frame error */ |
125 | | #define FLAGS_ROUTE_INFO 0x08 /* Frame has token ring routing information */ |
126 | 0 | #define FLAGS_FRAME_TOO_LONG 0x10 /* Frame too long */ |
127 | 0 | #define FLAGS_FRAME_TOO_SHORT 0x20 /* Frame too short (runt) */ |
128 | | #define FLAGS_TRIGGER 0x40 /* Trigger packet (?) */ |
129 | | #define FLAGS_SNAP 0x80 /* SNAP packet (SNAP header?) */ |
130 | | |
131 | | /* |
132 | | * Status bits. |
133 | | */ |
134 | | #define STATUS_SELECTED 0x01 /* Selected (in the *Peek GUI?) */ |
135 | | #define STATUS_TRUNCATED 0x02 /* Truncated (?) */ |
136 | | #define STATUS_APPLEPEEK 0x10 /* ApplePeek packet (?) */ |
137 | | #define STATUS_SLICED 0x20 /* Sliced (cut short by snaplen?) */ |
138 | | #define STATUS_HIDDEN 0x80 /* Hidden (in the *Peek GUI?) */ |
139 | | |
140 | | typedef struct { |
141 | | time_t reference_time; |
142 | | } peekclassic_t; |
143 | | |
144 | | static bool peekclassic_read_v7(wtap *wth, wtap_rec *rec, |
145 | | int *err, char **err_info, int64_t *data_offset); |
146 | | static bool peekclassic_seek_read_v7(wtap *wth, int64_t seek_off, |
147 | | wtap_rec *rec, int *err, char **err_info); |
148 | | static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh, |
149 | | wtap_rec *rec, int *err, char **err_info); |
150 | | static bool peekclassic_read_v56(wtap *wth, wtap_rec *rec, |
151 | | int *err, char **err_info, int64_t *data_offset); |
152 | | static bool peekclassic_seek_read_v56(wtap *wth, int64_t seek_off, |
153 | | wtap_rec *rec, int *err, char **err_info); |
154 | | static bool peekclassic_read_packet_v56(wtap *wth, FILE_T fh, |
155 | | wtap_rec *rec, int *err, char **err_info); |
156 | | |
157 | | static int peekclassic_v56_file_type_subtype = -1; |
158 | | static int peekclassic_v7_file_type_subtype = -1; |
159 | | |
160 | | void register_peekclassic(void); |
161 | | |
162 | | wtap_open_return_val peekclassic_open(wtap *wth, int *err, char **err_info) |
163 | 0 | { |
164 | 0 | peekclassic_header_t ep_hdr; |
165 | 0 | time_t reference_time; |
166 | 0 | int file_encap; |
167 | 0 | peekclassic_t *peekclassic; |
168 | | |
169 | | /* Peek classic files do not start with a magic value large enough |
170 | | * to be unique; hence we use the following algorithm to determine |
171 | | * the type of an unknown file: |
172 | | * - populate the master header and reject file if there is no match |
173 | | * - populate the secondary header and check that the reserved space |
174 | | * is zero, and check some other fields; this isn't perfect, |
175 | | * and we may have to add more checks at some point. |
176 | | */ |
177 | 0 | ws_assert(sizeof(ep_hdr.master) == PEEKCLASSIC_MASTER_HDR_SIZE); |
178 | 0 | if (!wtap_read_bytes(wth->fh, &ep_hdr.master, |
179 | 0 | (int)sizeof(ep_hdr.master), err, err_info)) { |
180 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
181 | 0 | return WTAP_OPEN_ERROR; |
182 | 0 | return WTAP_OPEN_NOT_MINE; |
183 | 0 | } |
184 | | |
185 | | /* |
186 | | * It appears that EtherHelp (a free application from WildPackets |
187 | | * that did blind capture, saving to a file, so that you could |
188 | | * give the resulting file to somebody with EtherPeek) saved |
189 | | * captures in EtherPeek format except that it ORed the 0x80 |
190 | | * bit on in the version number. |
191 | | * |
192 | | * We therefore strip off the 0x80 bit in the version number. |
193 | | * Perhaps there's some reason to care whether the capture |
194 | | * came from EtherHelp; if we discover one, we should check |
195 | | * that bit. |
196 | | */ |
197 | 0 | ep_hdr.master.version &= ~0x80; |
198 | | |
199 | | /* switch on the file version */ |
200 | 0 | switch (ep_hdr.master.version) { |
201 | | |
202 | 0 | case 5: |
203 | 0 | case 6: |
204 | 0 | case 7: |
205 | | /* get the secondary header */ |
206 | 0 | ws_assert(sizeof(ep_hdr.secondary.v567) == |
207 | 0 | PEEKCLASSIC_V567_HDR_SIZE); |
208 | 0 | if (!wtap_read_bytes(wth->fh, &ep_hdr.secondary.v567, |
209 | 0 | (int)sizeof(ep_hdr.secondary.v567), err, err_info)) { |
210 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
211 | 0 | return WTAP_OPEN_ERROR; |
212 | 0 | return WTAP_OPEN_NOT_MINE; |
213 | 0 | } |
214 | | |
215 | 0 | if ((0 != ep_hdr.secondary.v567.reserved[0]) || |
216 | 0 | (0 != ep_hdr.secondary.v567.reserved[1]) || |
217 | 0 | (0 != ep_hdr.secondary.v567.reserved[2])) { |
218 | | /* still unknown */ |
219 | 0 | return WTAP_OPEN_NOT_MINE; |
220 | 0 | } |
221 | | |
222 | | /* |
223 | | * Check the mediaType and physMedium fields. |
224 | | * We assume it's not a Peek classic file if |
225 | | * these aren't values we know, rather than |
226 | | * reporting them as invalid Peek classic files, |
227 | | * as, given the lack of a magic number, we need |
228 | | * all the checks we can get. |
229 | | */ |
230 | 0 | ep_hdr.secondary.v567.mediaType = |
231 | 0 | g_ntohl(ep_hdr.secondary.v567.mediaType); |
232 | 0 | ep_hdr.secondary.v567.physMedium = |
233 | 0 | g_ntohl(ep_hdr.secondary.v567.physMedium); |
234 | |
|
235 | 0 | switch (ep_hdr.secondary.v567.physMedium) { |
236 | | |
237 | 0 | case 0: |
238 | | /* |
239 | | * "Native" format, presumably meaning |
240 | | * Ethernet or Token Ring. |
241 | | */ |
242 | 0 | switch (ep_hdr.secondary.v567.mediaType) { |
243 | | |
244 | 0 | case 0: |
245 | 0 | file_encap = WTAP_ENCAP_ETHERNET; |
246 | 0 | break; |
247 | | |
248 | 0 | case 1: |
249 | 0 | file_encap = WTAP_ENCAP_TOKEN_RING; |
250 | 0 | break; |
251 | | |
252 | 0 | default: |
253 | | /* |
254 | | * Assume this isn't a Peek classic file. |
255 | | */ |
256 | 0 | return WTAP_OPEN_NOT_MINE; |
257 | 0 | } |
258 | 0 | break; |
259 | | |
260 | 0 | case 1: |
261 | 0 | switch (ep_hdr.secondary.v567.mediaType) { |
262 | | |
263 | 0 | case 0: |
264 | | /* |
265 | | * 802.11, with a private header giving |
266 | | * some radio information. Presumably |
267 | | * this is from AiroPeek. |
268 | | */ |
269 | 0 | file_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO; |
270 | 0 | break; |
271 | | |
272 | 0 | default: |
273 | | /* |
274 | | * Assume this isn't a Peek classic file. |
275 | | */ |
276 | 0 | return WTAP_OPEN_NOT_MINE; |
277 | 0 | } |
278 | 0 | break; |
279 | | |
280 | 0 | default: |
281 | | /* |
282 | | * Assume this isn't a Peek classic file. |
283 | | */ |
284 | 0 | return WTAP_OPEN_NOT_MINE; |
285 | 0 | } |
286 | | |
287 | | |
288 | | /* |
289 | | * Assume this is a V5, V6 or V7 Peek classic file, and |
290 | | * byte swap the rest of the fields in the secondary header. |
291 | | * |
292 | | * XXX - we could check the file length if the file were |
293 | | * uncompressed, but it might be compressed. |
294 | | */ |
295 | 0 | ep_hdr.secondary.v567.filelength = |
296 | 0 | g_ntohl(ep_hdr.secondary.v567.filelength); |
297 | 0 | ep_hdr.secondary.v567.numPackets = |
298 | 0 | g_ntohl(ep_hdr.secondary.v567.numPackets); |
299 | 0 | ep_hdr.secondary.v567.timeDate = |
300 | 0 | g_ntohl(ep_hdr.secondary.v567.timeDate); |
301 | 0 | ep_hdr.secondary.v567.timeStart = |
302 | 0 | g_ntohl(ep_hdr.secondary.v567.timeStart); |
303 | 0 | ep_hdr.secondary.v567.timeStop = |
304 | 0 | g_ntohl(ep_hdr.secondary.v567.timeStop); |
305 | 0 | ep_hdr.secondary.v567.appVers = |
306 | 0 | g_ntohl(ep_hdr.secondary.v567.appVers); |
307 | 0 | ep_hdr.secondary.v567.linkSpeed = |
308 | 0 | g_ntohl(ep_hdr.secondary.v567.linkSpeed); |
309 | | |
310 | | /* Get the reference time as a time_t */ |
311 | 0 | reference_time = ep_hdr.secondary.v567.timeDate - EPOCH_DELTA_1904_01_01_00_00_00_UTC; |
312 | 0 | break; |
313 | | |
314 | 0 | default: |
315 | | /* |
316 | | * Assume this isn't a Peek classic file. |
317 | | */ |
318 | 0 | return WTAP_OPEN_NOT_MINE; |
319 | 0 | } |
320 | | |
321 | | /* |
322 | | * This is a Peek classic file. |
323 | | * |
324 | | * At this point we have recognised the file type and have populated |
325 | | * the whole ep_hdr structure in host byte order. |
326 | | */ |
327 | 0 | peekclassic = g_new(peekclassic_t, 1); |
328 | 0 | wth->priv = (void *)peekclassic; |
329 | 0 | peekclassic->reference_time = reference_time; |
330 | 0 | wth->file_encap = file_encap; |
331 | 0 | switch (ep_hdr.master.version) { |
332 | | |
333 | 0 | case 5: |
334 | 0 | case 6: |
335 | 0 | wth->file_type_subtype = peekclassic_v56_file_type_subtype; |
336 | 0 | wth->subtype_read = peekclassic_read_v56; |
337 | 0 | wth->subtype_seek_read = peekclassic_seek_read_v56; |
338 | 0 | break; |
339 | | |
340 | 0 | case 7: |
341 | 0 | wth->file_type_subtype = peekclassic_v7_file_type_subtype; |
342 | 0 | wth->subtype_read = peekclassic_read_v7; |
343 | 0 | wth->subtype_seek_read = peekclassic_seek_read_v7; |
344 | 0 | break; |
345 | | |
346 | 0 | default: |
347 | | /* this is impossible */ |
348 | 0 | ws_assert_not_reached(); |
349 | 0 | } |
350 | | |
351 | 0 | wth->snapshot_length = 0; /* not available in header */ |
352 | 0 | wth->file_tsprec = WTAP_TSPREC_USEC; |
353 | | |
354 | | /* |
355 | | * Add an IDB; we don't know how many interfaces were |
356 | | * involved, so we just say one interface, about which |
357 | | * we only know the link-layer type, snapshot length, |
358 | | * and time stamp resolution. |
359 | | */ |
360 | 0 | wtap_add_generated_idb(wth); |
361 | |
|
362 | 0 | return WTAP_OPEN_MINE; |
363 | 0 | } |
364 | | |
365 | | static bool peekclassic_read_v7(wtap *wth, wtap_rec *rec, |
366 | | int *err, char **err_info, int64_t *data_offset) |
367 | 0 | { |
368 | 0 | int sliceLength; |
369 | |
|
370 | 0 | *data_offset = file_tell(wth->fh); |
371 | | |
372 | | /* Read the packet. */ |
373 | 0 | sliceLength = peekclassic_read_packet_v7(wth, wth->fh, rec, err, |
374 | 0 | err_info); |
375 | 0 | if (sliceLength < 0) |
376 | 0 | return false; |
377 | | |
378 | | /* Skip extra ignored data at the end of the packet. */ |
379 | 0 | if ((uint32_t)sliceLength > rec->rec_header.packet_header.caplen) { |
380 | 0 | if (!wtap_read_bytes(wth->fh, NULL, sliceLength - rec->rec_header.packet_header.caplen, |
381 | 0 | err, err_info)) |
382 | 0 | return false; |
383 | 0 | } |
384 | | |
385 | | /* Records are padded to an even length, so if the slice length |
386 | | is odd, read the padding byte. */ |
387 | 0 | if (sliceLength & 0x01) { |
388 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 1, err, err_info)) |
389 | 0 | return false; |
390 | 0 | } |
391 | | |
392 | 0 | return true; |
393 | 0 | } |
394 | | |
395 | | static bool peekclassic_seek_read_v7(wtap *wth, int64_t seek_off, |
396 | | wtap_rec *rec, int *err, char **err_info) |
397 | 0 | { |
398 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
399 | 0 | return false; |
400 | | |
401 | | /* Read the packet. */ |
402 | 0 | if (peekclassic_read_packet_v7(wth, wth->random_fh, rec, err, |
403 | 0 | err_info) == -1) { |
404 | 0 | if (*err == 0) |
405 | 0 | *err = WTAP_ERR_SHORT_READ; |
406 | 0 | return false; |
407 | 0 | } |
408 | 0 | return true; |
409 | 0 | } |
410 | | |
411 | 0 | #define RADIO_INFO_SIZE 4 |
412 | | |
413 | | static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh, wtap_rec *rec, |
414 | | int *err, char **err_info) |
415 | 0 | { |
416 | 0 | uint8_t ep_pkt[PEEKCLASSIC_V7_PKT_SIZE]; |
417 | | #if 0 |
418 | | uint16_t protoNum; |
419 | | #endif |
420 | 0 | uint16_t length; |
421 | 0 | uint16_t sliceLength; |
422 | 0 | uint8_t flags; |
423 | 0 | uint8_t status; |
424 | 0 | uint64_t timestamp; |
425 | 0 | time_t tsecs; |
426 | 0 | uint32_t tusecs; |
427 | 0 | uint32_t pack_flags; |
428 | 0 | uint8_t radio_info[RADIO_INFO_SIZE]; |
429 | |
|
430 | 0 | if (!wtap_read_bytes_or_eof(fh, ep_pkt, sizeof(ep_pkt), err, err_info)) |
431 | 0 | return -1; |
432 | | |
433 | | /* Extract the fields from the packet */ |
434 | | #if 0 |
435 | | protoNum = pntohu16(&ep_pkt[PEEKCLASSIC_V7_PROTONUM_OFFSET]); |
436 | | #endif |
437 | 0 | length = pntohu16(&ep_pkt[PEEKCLASSIC_V7_LENGTH_OFFSET]); |
438 | 0 | sliceLength = pntohu16(&ep_pkt[PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET]); |
439 | 0 | flags = ep_pkt[PEEKCLASSIC_V7_FLAGS_OFFSET]; |
440 | 0 | status = ep_pkt[PEEKCLASSIC_V7_STATUS_OFFSET]; |
441 | 0 | timestamp = pntohu64(&ep_pkt[PEEKCLASSIC_V7_TIMESTAMP_OFFSET]); |
442 | | |
443 | | /* force sliceLength to be the actual length of the packet */ |
444 | 0 | if (0 == sliceLength) { |
445 | 0 | sliceLength = length; |
446 | 0 | } |
447 | | /* |
448 | | * The maximum value of sliceLength and length are 65535, which |
449 | | * are less than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't |
450 | | * need to check them. |
451 | | */ |
452 | | |
453 | | /* fill in packet header values */ |
454 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
455 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
456 | 0 | rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; |
457 | 0 | tsecs = (time_t) (timestamp/1000000); |
458 | 0 | tusecs = (uint32_t) (timestamp - tsecs*1000000); |
459 | 0 | rec->ts.secs = tsecs - EPOCH_DELTA_1904_01_01_00_00_00_UTC; |
460 | 0 | rec->ts.nsecs = tusecs * 1000; |
461 | 0 | rec->rec_header.packet_header.len = length; |
462 | 0 | rec->rec_header.packet_header.caplen = sliceLength; |
463 | 0 | pack_flags = 0; |
464 | 0 | if (flags & FLAGS_HAS_CRC_ERROR) |
465 | 0 | pack_flags |= PACK_FLAGS_CRC_ERROR; |
466 | 0 | if (flags & FLAGS_FRAME_TOO_LONG) |
467 | 0 | pack_flags |= PACK_FLAGS_PACKET_TOO_LONG; |
468 | 0 | if (flags & FLAGS_FRAME_TOO_SHORT) |
469 | 0 | pack_flags |= PACK_FLAGS_PACKET_TOO_SHORT; |
470 | 0 | wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags); |
471 | |
|
472 | 0 | switch (wth->file_encap) { |
473 | | |
474 | 0 | case WTAP_ENCAP_IEEE_802_11_WITH_RADIO: |
475 | 0 | memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11)); |
476 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 0; /* no FCS */ |
477 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = false; |
478 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = false; |
479 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN; |
480 | | |
481 | | /* |
482 | | * Now process the radio information pseudo-header. |
483 | | * It's a 4-byte pseudo-header, consisting of: |
484 | | * |
485 | | * 1 byte of data rate, in units of 500 kb/s; |
486 | | * |
487 | | * 1 byte of channel number; |
488 | | * |
489 | | * 1 byte of signal strength as a percentage of |
490 | | * the maximum, i.e. (RXVECTOR RSSI/RXVECTOR RSSI_Max)*100, |
491 | | * or, at least, that's what I infer it is, given what |
492 | | * the WildPackets note "Converting Signal Strength |
493 | | * Percentage to dBm Values" says (it also says that |
494 | | * the conversion the percentage to a dBm value is |
495 | | * an adapter-dependent process, so, as we don't know |
496 | | * what type of adapter was used to do the capture, |
497 | | * we can't do the conversion); |
498 | | * |
499 | | * 1 byte of unknown content (padding?). |
500 | | */ |
501 | 0 | if (rec->rec_header.packet_header.len < RADIO_INFO_SIZE || rec->rec_header.packet_header.caplen < RADIO_INFO_SIZE) { |
502 | 0 | *err = WTAP_ERR_BAD_FILE; |
503 | 0 | *err_info = ws_strdup_printf("peekclassic: 802.11 packet has length < 4"); |
504 | 0 | return -1; |
505 | 0 | } |
506 | 0 | rec->rec_header.packet_header.len -= RADIO_INFO_SIZE; |
507 | 0 | rec->rec_header.packet_header.caplen -= RADIO_INFO_SIZE; |
508 | 0 | sliceLength -= RADIO_INFO_SIZE; |
509 | | |
510 | | /* read the pseudo-header */ |
511 | 0 | if (!wtap_read_bytes(fh, radio_info, RADIO_INFO_SIZE, err, err_info)) |
512 | 0 | return -1; |
513 | | |
514 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate = true; |
515 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate = radio_info[0]; |
516 | |
|
517 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel = true; |
518 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel = radio_info[1]; |
519 | |
|
520 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent = true; |
521 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent = radio_info[2]; |
522 | | |
523 | | /* |
524 | | * We don't know they PHY, but we do have the data rate; |
525 | | * try to guess it based on the data rate and channel. |
526 | | */ |
527 | 0 | if (RATE_IS_DSSS(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) { |
528 | | /* 11b */ |
529 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B; |
530 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = false; |
531 | 0 | } else if (RATE_IS_OFDM(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) { |
532 | | /* 11a or 11g, depending on the band. */ |
533 | 0 | if (CHAN_IS_BG(rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel)) { |
534 | | /* 11g */ |
535 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G; |
536 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode = false; |
537 | 0 | } else { |
538 | | /* 11a */ |
539 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A; |
540 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_channel_type = false; |
541 | 0 | rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type = false; |
542 | 0 | } |
543 | 0 | } |
544 | | |
545 | | /* |
546 | | * The last 4 bytes appear to be random data - the length |
547 | | * might include the FCS - so we reduce the length by 4. |
548 | | * |
549 | | * Or maybe this is just the same kind of random 4 bytes |
550 | | * of junk at the end you get in Wireless Sniffer |
551 | | * captures. |
552 | | */ |
553 | 0 | if (rec->rec_header.packet_header.len < 4 || rec->rec_header.packet_header.caplen < 4) { |
554 | 0 | *err = WTAP_ERR_BAD_FILE; |
555 | 0 | *err_info = ws_strdup_printf("peekclassic: 802.11 packet has length < 8"); |
556 | 0 | return -1; |
557 | 0 | } |
558 | 0 | rec->rec_header.packet_header.len -= 4; |
559 | 0 | rec->rec_header.packet_header.caplen -= 4; |
560 | 0 | break; |
561 | | |
562 | 0 | case WTAP_ENCAP_ETHERNET: |
563 | | /* XXX - it appears that if the low-order bit of |
564 | | "status" is 0, there's an FCS in this frame, |
565 | | and if it's 1, there's 4 bytes of 0. */ |
566 | 0 | rec->rec_header.packet_header.pseudo_header.eth.fcs_len = (status & 0x01) ? 0 : 4; |
567 | 0 | break; |
568 | 0 | } |
569 | | |
570 | | /* read the packet data */ |
571 | 0 | if (!wtap_read_bytes_buffer(fh, &rec->data, rec->rec_header.packet_header.caplen, err, err_info)) |
572 | 0 | return -1; |
573 | | |
574 | 0 | return sliceLength; |
575 | 0 | } |
576 | | |
577 | | static bool peekclassic_read_v56(wtap *wth, wtap_rec *rec, |
578 | | int *err, char **err_info, int64_t *data_offset) |
579 | 0 | { |
580 | 0 | *data_offset = file_tell(wth->fh); |
581 | | |
582 | | /* read the packet */ |
583 | 0 | if (!peekclassic_read_packet_v56(wth, wth->fh, rec, err, err_info)) |
584 | 0 | return false; |
585 | | |
586 | | /* |
587 | | * XXX - is the captured packet data padded to a multiple |
588 | | * of 2 bytes? |
589 | | */ |
590 | 0 | return true; |
591 | 0 | } |
592 | | |
593 | | static bool peekclassic_seek_read_v56(wtap *wth, int64_t seek_off, |
594 | | wtap_rec *rec, int *err, char **err_info) |
595 | 0 | { |
596 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
597 | 0 | return false; |
598 | | |
599 | | /* read the packet */ |
600 | 0 | if (!peekclassic_read_packet_v56(wth, wth->random_fh, rec, err, |
601 | 0 | err_info)) { |
602 | 0 | if (*err == 0) |
603 | 0 | *err = WTAP_ERR_SHORT_READ; |
604 | 0 | return false; |
605 | 0 | } |
606 | 0 | return true; |
607 | 0 | } |
608 | | |
609 | | static bool peekclassic_read_packet_v56(wtap *wth, FILE_T fh, wtap_rec *rec, |
610 | | int *err, char **err_info) |
611 | 0 | { |
612 | 0 | peekclassic_t *peekclassic = (peekclassic_t *)wth->priv; |
613 | 0 | uint8_t ep_pkt[PEEKCLASSIC_V56_PKT_SIZE]; |
614 | 0 | uint16_t length; |
615 | 0 | uint16_t sliceLength; |
616 | 0 | uint8_t flags; |
617 | | #if 0 |
618 | | uint8_t status; |
619 | | #endif |
620 | 0 | uint32_t timestamp; |
621 | | #if 0 |
622 | | uint16_t destNum; |
623 | | uint16_t srcNum; |
624 | | #endif |
625 | | #if 0 |
626 | | uint16_t protoNum; |
627 | | char protoStr[8]; |
628 | | #endif |
629 | 0 | uint32_t pack_flags; |
630 | |
|
631 | 0 | if (!wtap_read_bytes_or_eof(fh, ep_pkt, sizeof(ep_pkt), err, err_info)) |
632 | 0 | return false; |
633 | | |
634 | | /* Extract the fields from the packet */ |
635 | 0 | length = pntohu16(&ep_pkt[PEEKCLASSIC_V56_LENGTH_OFFSET]); |
636 | 0 | sliceLength = pntohu16(&ep_pkt[PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET]); |
637 | 0 | flags = ep_pkt[PEEKCLASSIC_V56_FLAGS_OFFSET]; |
638 | | #if 0 |
639 | | status = ep_pkt[PEEKCLASSIC_V56_STATUS_OFFSET]; |
640 | | #endif |
641 | 0 | timestamp = pntohu32(&ep_pkt[PEEKCLASSIC_V56_TIMESTAMP_OFFSET]); |
642 | | #if 0 |
643 | | destNum = pntohu16(&ep_pkt[PEEKCLASSIC_V56_DESTNUM_OFFSET]); |
644 | | srcNum = pntohu16(&ep_pkt[PEEKCLASSIC_V56_SRCNUM_OFFSET]); |
645 | | protoNum = pntohu16(&ep_pkt[PEEKCLASSIC_V56_PROTONUM_OFFSET]); |
646 | | memcpy(protoStr, &ep_pkt[PEEKCLASSIC_V56_PROTOSTR_OFFSET], |
647 | | sizeof protoStr); |
648 | | #endif |
649 | | |
650 | | /* |
651 | | * XXX - is the captured packet data padded to a multiple |
652 | | * of 2 bytes? |
653 | | */ |
654 | | |
655 | | /* force sliceLength to be the actual length of the packet */ |
656 | 0 | if (0 == sliceLength) { |
657 | 0 | sliceLength = length; |
658 | 0 | } |
659 | | /* |
660 | | * The maximum value of sliceLength and length are 65535, which |
661 | | * are less than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't |
662 | | * need to check them. |
663 | | */ |
664 | | |
665 | | /* fill in packet header values */ |
666 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
667 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
668 | 0 | rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; |
669 | | /* timestamp is in milliseconds since reference_time */ |
670 | 0 | rec->ts.secs = peekclassic->reference_time + (timestamp / 1000); |
671 | 0 | rec->ts.nsecs = 1000 * (timestamp % 1000) * 1000; |
672 | 0 | rec->rec_header.packet_header.len = length; |
673 | 0 | rec->rec_header.packet_header.caplen = sliceLength; |
674 | 0 | pack_flags = 0; |
675 | 0 | if (flags & FLAGS_HAS_CRC_ERROR) |
676 | 0 | pack_flags |= PACK_FLAGS_CRC_ERROR; |
677 | 0 | if (flags & FLAGS_FRAME_TOO_LONG) |
678 | 0 | pack_flags |= PACK_FLAGS_PACKET_TOO_LONG; |
679 | 0 | if (flags & FLAGS_FRAME_TOO_SHORT) |
680 | 0 | pack_flags |= PACK_FLAGS_PACKET_TOO_SHORT; |
681 | 0 | wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags); |
682 | |
|
683 | 0 | switch (wth->file_encap) { |
684 | | |
685 | 0 | case WTAP_ENCAP_ETHERNET: |
686 | | /* We assume there's no FCS in this frame. */ |
687 | 0 | rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0; |
688 | 0 | break; |
689 | 0 | } |
690 | | |
691 | | /* read the packet data */ |
692 | 0 | return wtap_read_bytes_buffer(fh, &rec->data, sliceLength, err, err_info); |
693 | 0 | } |
694 | | |
695 | | static const struct supported_block_type peekclassic_v56_blocks_supported[] = { |
696 | | /* |
697 | | * We support packet blocks, with no comments or other options. |
698 | | */ |
699 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
700 | | }; |
701 | | |
702 | | static const struct file_type_subtype_info peekclassic_v56_info = { |
703 | | "Savvius classic (V5 and V6)", "peekclassic56", "pkt", "tpc;apc;wpz", |
704 | | false, BLOCKS_SUPPORTED(peekclassic_v56_blocks_supported), |
705 | | NULL, NULL, NULL |
706 | | }; |
707 | | |
708 | | static const struct supported_block_type peekclassic_v7_blocks_supported[] = { |
709 | | /* |
710 | | * We support packet blocks, with no comments or other options. |
711 | | */ |
712 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
713 | | }; |
714 | | |
715 | | static const struct file_type_subtype_info peekclassic_v7_info = { |
716 | | "Savvius classic (V7)", "peekclassic7", "pkt", "tpc;apc;wpz", |
717 | | false, BLOCKS_SUPPORTED(peekclassic_v7_blocks_supported), |
718 | | NULL, NULL, NULL |
719 | | }; |
720 | | |
721 | | void register_peekclassic(void) |
722 | 14 | { |
723 | 14 | peekclassic_v56_file_type_subtype = wtap_register_file_type_subtype(&peekclassic_v56_info); |
724 | 14 | peekclassic_v7_file_type_subtype = wtap_register_file_type_subtype(&peekclassic_v7_info); |
725 | | |
726 | | /* |
727 | | * Register names for backwards compatibility with the |
728 | | * wtap_filetypes table in Lua. |
729 | | */ |
730 | 14 | wtap_register_backwards_compatibility_lua_name("PEEKCLASSIC_V56", |
731 | 14 | peekclassic_v56_file_type_subtype); |
732 | 14 | wtap_register_backwards_compatibility_lua_name("PEEKCLASSIC_V7", |
733 | 14 | peekclassic_v7_file_type_subtype); |
734 | 14 | } |
735 | | |
736 | | /* |
737 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
738 | | * |
739 | | * Local variables: |
740 | | * c-basic-offset: 8 |
741 | | * tab-width: 8 |
742 | | * indent-tabs-mode: t |
743 | | * End: |
744 | | * |
745 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
746 | | * :indentSize=8:tabSize=8:noTabs=false: |
747 | | */ |