/src/wireshark/wiretap/radcom.c
Line | Count | Source |
1 | | /* radcom.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 "radcom.h" |
11 | | |
12 | | #include <string.h> |
13 | | |
14 | | #include <wsutil/pint.h> |
15 | | |
16 | | #include "wtap_module.h" |
17 | | #include "file_wrappers.h" |
18 | | |
19 | | /* |
20 | | * RADCOM WAN/LAN Analyzers |
21 | | * |
22 | | * Olivier Abad has added code to read Ethernet and LAPB captures from |
23 | | * RADCOM WAN/LAN Analyzers (see https://web.archive.org/web/20031231213434/http://www.radcom-inc.com/). |
24 | | */ |
25 | | |
26 | | struct frame_date { |
27 | | uint16_t year; |
28 | | uint8_t month; |
29 | | uint8_t day; |
30 | | uint32_t sec; /* seconds since midnight */ |
31 | | uint32_t usec; |
32 | | }; |
33 | | |
34 | | struct unaligned_frame_date { |
35 | | char year[2]; |
36 | | char month; |
37 | | char day; |
38 | | char sec[4]; /* seconds since midnight */ |
39 | | char usec[4]; |
40 | | }; |
41 | | |
42 | | /* Found at the beginning of the file. Bytes 2 and 3 (D2:00) seem to be |
43 | | * different in some captures */ |
44 | | static const uint8_t radcom_magic[8] = { |
45 | | 0x42, 0xD2, 0x00, 0x34, 0x12, 0x66, 0x22, 0x88 |
46 | | }; |
47 | | |
48 | | static const uint8_t encap_magic[4] = { |
49 | | 0x00, 0x42, 0x43, 0x09 |
50 | | }; |
51 | | |
52 | | static const uint8_t active_time_magic[11] = { |
53 | | 'A', 'c', 't', 'i', 'v', 'e', ' ', 'T', 'i', 'm', 'e' |
54 | | }; |
55 | | |
56 | | /* RADCOM record header - followed by frame data (perhaps including FCS). |
57 | | |
58 | | "data_length" appears to be the length of packet data following |
59 | | the record header. It's 0 in the last record. |
60 | | |
61 | | "length" appears to be the amount of captured packet data, and |
62 | | "real_length" might be the actual length of the frame on the wire - |
63 | | in some captures, it's the same as "length", and, in others, |
64 | | it's greater than "length". In the last record, however, those |
65 | | may have bogus values (or is that some kind of trailer record?). |
66 | | |
67 | | "xxx" appears to be all-zero in all but the last record in one |
68 | | capture; if so, perhaps this indicates that the last record is, |
69 | | in fact, a trailer of some sort, and some field in the header |
70 | | is a record type. */ |
71 | | struct radcomrec_hdr { |
72 | | char xxx[4]; /* unknown */ |
73 | | char data_length[2]; /* packet length? */ |
74 | | char xxy[5]; /* unknown */ |
75 | | struct unaligned_frame_date date; /* date/time stamp of packet */ |
76 | | char real_length[2]; /* actual length of packet */ |
77 | | char length[2]; /* captured length of packet */ |
78 | | char xxz[2]; /* unknown */ |
79 | | char dce; /* DCE/DTE flag (and other flags?) */ |
80 | | char xxw[9]; /* unknown */ |
81 | | }; |
82 | | |
83 | | static bool radcom_read(wtap *wth, wtap_rec *rec, |
84 | | int *err, char **err_info, int64_t *data_offset); |
85 | | static bool radcom_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, |
86 | | int *err, char **err_info); |
87 | | static bool radcom_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec, |
88 | | int *err, char **err_info); |
89 | | |
90 | | static int radcom_file_type_subtype = -1; |
91 | | |
92 | | void register_radcom(void); |
93 | | |
94 | | wtap_open_return_val radcom_open(wtap *wth, int *err, char **err_info) |
95 | 0 | { |
96 | 0 | uint8_t r_magic[8], t_magic[11], search_encap[7]; |
97 | 0 | struct frame_date start_date; |
98 | | #if 0 |
99 | | uint32_t sec; |
100 | | struct tm tm; |
101 | | #endif |
102 | | |
103 | | /* Read in the string that should be at the start of a RADCOM file */ |
104 | 0 | if (!wtap_read_bytes(wth->fh, r_magic, 8, err, err_info)) { |
105 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
106 | 0 | return WTAP_OPEN_ERROR; |
107 | 0 | return WTAP_OPEN_NOT_MINE; |
108 | 0 | } |
109 | | |
110 | | /* XXX: bytes 2 and 3 of the "magic" header seem to be different in some |
111 | | * captures. We force them to our standard value so that the test |
112 | | * succeeds (until we find if they have a special meaning, perhaps a |
113 | | * version number ?) */ |
114 | 0 | r_magic[1] = 0xD2; |
115 | 0 | r_magic[2] = 0x00; |
116 | 0 | if (memcmp(r_magic, radcom_magic, 8) != 0) { |
117 | 0 | return WTAP_OPEN_NOT_MINE; |
118 | 0 | } |
119 | | |
120 | | /* Look for the "Active Time" string. The "frame_date" structure should |
121 | | * be located 32 bytes before the beginning of this string */ |
122 | 0 | if (!wtap_read_bytes(wth->fh, t_magic, 11, err, err_info)) { |
123 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
124 | 0 | return WTAP_OPEN_ERROR; |
125 | 0 | return WTAP_OPEN_NOT_MINE; |
126 | 0 | } |
127 | 0 | while (memcmp(t_magic, active_time_magic, 11) != 0) |
128 | 0 | { |
129 | 0 | if (file_seek(wth->fh, -10, SEEK_CUR, err) == -1) |
130 | 0 | return WTAP_OPEN_ERROR; |
131 | 0 | if (!wtap_read_bytes(wth->fh, t_magic, 11, 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 | 0 | } |
137 | 0 | if (file_seek(wth->fh, -43, SEEK_CUR, err) == -1) |
138 | 0 | return WTAP_OPEN_ERROR; |
139 | | |
140 | | /* Get capture start time */ |
141 | 0 | if (!wtap_read_bytes(wth->fh, &start_date, sizeof(struct frame_date), |
142 | 0 | err, err_info)) { |
143 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
144 | 0 | return WTAP_OPEN_ERROR; |
145 | 0 | return WTAP_OPEN_NOT_MINE; |
146 | 0 | } |
147 | | |
148 | | /* So what time is this? */ |
149 | 0 | if (!wtap_read_bytes(wth->fh, NULL, sizeof(struct frame_date), |
150 | 0 | err, err_info)) { |
151 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
152 | 0 | return WTAP_OPEN_ERROR; |
153 | 0 | return WTAP_OPEN_NOT_MINE; |
154 | 0 | } |
155 | | |
156 | 0 | for (;;) { |
157 | 0 | if (!wtap_read_bytes(wth->fh, search_encap, 4, |
158 | 0 | err, err_info)) { |
159 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
160 | 0 | return WTAP_OPEN_ERROR; |
161 | 0 | return WTAP_OPEN_NOT_MINE; |
162 | 0 | } |
163 | | |
164 | 0 | if (memcmp(encap_magic, search_encap, 4) == 0) |
165 | 0 | break; |
166 | | |
167 | | /* |
168 | | * OK, that's not it, go forward 1 byte - reading |
169 | | * the magic moved us forward 4 bytes, so seeking |
170 | | * backward 3 bytes moves forward 1 byte - and |
171 | | * try the 4 bytes at that offset. |
172 | | */ |
173 | 0 | if (file_seek(wth->fh, -3, SEEK_CUR, err) == -1) |
174 | 0 | return WTAP_OPEN_ERROR; |
175 | 0 | } |
176 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 12, err, err_info)) { |
177 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
178 | 0 | return WTAP_OPEN_ERROR; |
179 | 0 | return WTAP_OPEN_NOT_MINE; |
180 | 0 | } |
181 | 0 | if (!wtap_read_bytes(wth->fh, search_encap, 4, err, err_info)) { |
182 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
183 | 0 | return WTAP_OPEN_ERROR; |
184 | 0 | return WTAP_OPEN_NOT_MINE; |
185 | 0 | } |
186 | | |
187 | | /* This is a radcom file */ |
188 | 0 | wth->file_type_subtype = radcom_file_type_subtype; |
189 | 0 | wth->subtype_read = radcom_read; |
190 | 0 | wth->subtype_seek_read = radcom_seek_read; |
191 | 0 | wth->snapshot_length = 0; /* not available in header, only in frame */ |
192 | 0 | wth->file_tsprec = WTAP_TSPREC_USEC; |
193 | |
|
194 | | #if 0 |
195 | | tm.tm_year = pletohu16(&start_date.year)-1900; |
196 | | tm.tm_mon = start_date.month-1; |
197 | | tm.tm_mday = start_date.day; |
198 | | sec = pletohu32(&start_date.sec); |
199 | | tm.tm_hour = sec/3600; |
200 | | tm.tm_min = (sec%3600)/60; |
201 | | tm.tm_sec = sec%60; |
202 | | tm.tm_isdst = -1; |
203 | | #endif |
204 | |
|
205 | 0 | if (memcmp(search_encap, "LAPB", 4) == 0) |
206 | 0 | wth->file_encap = WTAP_ENCAP_LAPB; |
207 | 0 | else if (memcmp(search_encap, "Ethe", 4) == 0) |
208 | 0 | wth->file_encap = WTAP_ENCAP_ETHERNET; |
209 | 0 | else if (memcmp(search_encap, "ATM/", 4) == 0) |
210 | 0 | wth->file_encap = WTAP_ENCAP_ATM_RFC1483; |
211 | 0 | else { |
212 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
213 | 0 | *err_info = ws_strdup_printf("radcom: network type \"%.4s\" unknown", search_encap); |
214 | 0 | return WTAP_OPEN_ERROR; |
215 | 0 | } |
216 | | |
217 | | #if 0 |
218 | | if (!wtap_read_bytes(wth->fh, &next_date, sizeof(struct frame_date), |
219 | | err, err_info)) |
220 | | return WTAP_OPEN_ERROR; |
221 | | |
222 | | while (memcmp(&start_date, &next_date, 4)) { |
223 | | if (file_seek(wth->fh, 1-sizeof(struct frame_date), SEEK_CUR, err) == -1) |
224 | | return WTAP_OPEN_ERROR; |
225 | | if (!wtap_read_bytes(wth->fh, &next_date, sizeof(struct frame_date), |
226 | | err, err_info)) |
227 | | return WTAP_OPEN_ERROR; |
228 | | } |
229 | | #endif |
230 | | |
231 | 0 | if (wth->file_encap == WTAP_ENCAP_ETHERNET) { |
232 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 294, err, err_info)) |
233 | 0 | return WTAP_OPEN_ERROR; |
234 | 0 | } else if (wth->file_encap == WTAP_ENCAP_LAPB) { |
235 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 297, err, err_info)) |
236 | 0 | return WTAP_OPEN_ERROR; |
237 | 0 | } else if (wth->file_encap == WTAP_ENCAP_ATM_RFC1483) { |
238 | 0 | if (!wtap_read_bytes(wth->fh, NULL, 504, err, err_info)) |
239 | 0 | return WTAP_OPEN_ERROR; |
240 | 0 | } |
241 | | |
242 | | /* |
243 | | * Add an IDB; we don't know how many interfaces were involved, |
244 | | * so we just say one interface, about which we only know |
245 | | * the link-layer type, snapshot length, and time stamp |
246 | | * resolution. |
247 | | */ |
248 | 0 | wtap_add_generated_idb(wth); |
249 | |
|
250 | 0 | return WTAP_OPEN_MINE; |
251 | 0 | } |
252 | | |
253 | | /* Read the next packet */ |
254 | | static bool radcom_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, |
255 | | int64_t *data_offset) |
256 | 0 | { |
257 | 0 | char fcs[2]; |
258 | |
|
259 | 0 | *data_offset = file_tell(wth->fh); |
260 | | |
261 | | /* Read record. */ |
262 | 0 | if (!radcom_read_rec(wth, wth->fh, rec, err, err_info)) { |
263 | | /* Read error or EOF */ |
264 | 0 | return false; |
265 | 0 | } |
266 | | |
267 | 0 | if (wth->file_encap == WTAP_ENCAP_LAPB) { |
268 | | /* Read the FCS. |
269 | | XXX - should we have some way of indicating the |
270 | | presence and size of an FCS to our caller? |
271 | | That'd let us handle other file types as well. */ |
272 | 0 | if (!wtap_read_bytes(wth->fh, &fcs, sizeof fcs, err, err_info)) |
273 | 0 | return false; |
274 | 0 | } |
275 | | |
276 | 0 | return true; |
277 | 0 | } |
278 | | |
279 | | static bool |
280 | | radcom_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, |
281 | | int *err, char **err_info) |
282 | 0 | { |
283 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
284 | 0 | return false; |
285 | | |
286 | | /* Read record. */ |
287 | 0 | if (!radcom_read_rec(wth, wth->random_fh, rec, err, err_info)) { |
288 | | /* Read error or EOF */ |
289 | 0 | if (*err == 0) { |
290 | | /* EOF means "short read" in random-access mode */ |
291 | 0 | *err = WTAP_ERR_SHORT_READ; |
292 | 0 | } |
293 | 0 | return false; |
294 | 0 | } |
295 | 0 | return true; |
296 | 0 | } |
297 | | |
298 | | static bool |
299 | | radcom_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec, int *err, char **err_info) |
300 | 0 | { |
301 | 0 | struct radcomrec_hdr hdr; |
302 | 0 | uint16_t data_length, real_length, length; |
303 | 0 | uint32_t sec; |
304 | 0 | struct tm tm; |
305 | 0 | uint8_t atmhdr[8]; |
306 | |
|
307 | 0 | if (!wtap_read_bytes_or_eof(fh, &hdr, sizeof hdr, err, err_info)) |
308 | 0 | return false; |
309 | | |
310 | 0 | data_length = pletohu16(&hdr.data_length); |
311 | 0 | if (data_length == 0) { |
312 | | /* |
313 | | * The last record appears to have 0 in its "data_length" |
314 | | * field, but non-zero values in other fields, so we |
315 | | * check for that and treat it as an EOF indication. |
316 | | */ |
317 | 0 | *err = 0; |
318 | 0 | return false; |
319 | 0 | } |
320 | 0 | length = pletohu16(&hdr.length); |
321 | 0 | real_length = pletohu16(&hdr.real_length); |
322 | | /* |
323 | | * The maximum value of length is 65535, which is less than |
324 | | * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check |
325 | | * it. |
326 | | */ |
327 | |
|
328 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
329 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
330 | 0 | rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; |
331 | |
|
332 | 0 | tm.tm_year = pletohu16(&hdr.date.year)-1900; |
333 | 0 | tm.tm_mon = (hdr.date.month&0x0f)-1; |
334 | 0 | tm.tm_mday = hdr.date.day; |
335 | 0 | sec = pletohu32(&hdr.date.sec); |
336 | 0 | tm.tm_hour = sec/3600; |
337 | 0 | tm.tm_min = (sec%3600)/60; |
338 | 0 | tm.tm_sec = sec%60; |
339 | 0 | tm.tm_isdst = -1; |
340 | 0 | rec->ts.secs = mktime(&tm); |
341 | 0 | rec->ts.nsecs = pletohu32(&hdr.date.usec) * 1000; |
342 | |
|
343 | 0 | switch (wth->file_encap) { |
344 | | |
345 | 0 | case WTAP_ENCAP_ETHERNET: |
346 | | /* XXX - is there an FCS? */ |
347 | 0 | rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1; |
348 | 0 | break; |
349 | | |
350 | 0 | case WTAP_ENCAP_LAPB: |
351 | 0 | rec->rec_header.packet_header.pseudo_header.dte_dce.flags = (hdr.dce & 0x1) ? |
352 | 0 | 0x00 : FROM_DCE; |
353 | 0 | length -= 2; /* FCS */ |
354 | 0 | real_length -= 2; |
355 | 0 | break; |
356 | | |
357 | 0 | case WTAP_ENCAP_ATM_RFC1483: |
358 | | /* |
359 | | * XXX - is this stuff a pseudo-header? |
360 | | * The direction appears to be in the "hdr.dce" field. |
361 | | */ |
362 | 0 | if (!wtap_read_bytes(fh, atmhdr, sizeof atmhdr, err, |
363 | 0 | err_info)) |
364 | 0 | return false; /* Read error */ |
365 | 0 | length -= 8; |
366 | 0 | real_length -= 8; |
367 | 0 | break; |
368 | 0 | } |
369 | | |
370 | 0 | rec->rec_header.packet_header.len = real_length; |
371 | 0 | rec->rec_header.packet_header.caplen = length; |
372 | | |
373 | | /* |
374 | | * Read the packet data. |
375 | | */ |
376 | 0 | if (!wtap_read_bytes_buffer(fh, &rec->data, length, err, err_info)) |
377 | 0 | return false; /* Read error */ |
378 | | |
379 | 0 | return true; |
380 | 0 | } |
381 | | |
382 | | static const struct supported_block_type radcom_blocks_supported[] = { |
383 | | /* |
384 | | * We support packet blocks, with no comments or other options. |
385 | | */ |
386 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
387 | | }; |
388 | | |
389 | | static const struct file_type_subtype_info radcom_info = { |
390 | | "RADCOM WAN/LAN analyzer", "radcom", NULL, NULL, |
391 | | false, BLOCKS_SUPPORTED(radcom_blocks_supported), |
392 | | NULL, NULL, NULL |
393 | | }; |
394 | | |
395 | | void register_radcom(void) |
396 | 14 | { |
397 | 14 | radcom_file_type_subtype = wtap_register_file_type_subtype(&radcom_info); |
398 | | |
399 | | /* |
400 | | * Register name for backwards compatibility with the |
401 | | * wtap_filetypes table in Lua. |
402 | | */ |
403 | 14 | wtap_register_backwards_compatibility_lua_name("RADCOM", |
404 | 14 | radcom_file_type_subtype); |
405 | 14 | } |
406 | | |
407 | | /* |
408 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
409 | | * |
410 | | * Local variables: |
411 | | * c-basic-offset: 8 |
412 | | * tab-width: 8 |
413 | | * indent-tabs-mode: t |
414 | | * End: |
415 | | * |
416 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
417 | | * :indentSize=8:tabSize=8:noTabs=false: |
418 | | */ |