/src/wireshark/wiretap/dbs-etherwatch.c
Line | Count | Source |
1 | | /* dbs-etherwatch.c |
2 | | * |
3 | | * Wiretap Library |
4 | | * Copyright (c) 2001 by Marc Milgram <ethereal@mmilgram.NOSPAMmail.net> |
5 | | * |
6 | | * SPDX-License-Identifier: GPL-2.0-or-later |
7 | | */ |
8 | | |
9 | | #include "config.h" |
10 | | #include "dbs-etherwatch.h" |
11 | | #include "wtap_module.h" |
12 | | #include "file_wrappers.h" |
13 | | |
14 | | #include <stdlib.h> |
15 | | #include <string.h> |
16 | | |
17 | | #include <wsutil/array.h> |
18 | | #include <wsutil/pint.h> |
19 | | |
20 | | /* |
21 | | * DBS Etherwatch (text format) |
22 | | * |
23 | | * Text output from DBS Etherwatch is supported. DBS Etherwatch is available |
24 | | * from: https://web.archive.org/web/20070612033348/http://www.users.bigpond.com/dbsneddon/software.htm. |
25 | | */ |
26 | | |
27 | | /* This module reads the text output of the 'DBS-ETHERTRACE' command in VMS |
28 | | * It was initially based on vms.c. |
29 | | */ |
30 | | |
31 | | /* |
32 | | Example 'ETHERWATCH' output data (with "printable" characters in the |
33 | | "printable characters" section of the output replaced by "." if they have |
34 | | the 8th bit set, so as not to upset compilers that are expecting text |
35 | | in comments to be in a particular character encoding that can't handle |
36 | | those values): |
37 | | ETHERWATCH X5-008 |
38 | | 42 names and addresses were loaded |
39 | | Reading recorded data from PERSISTENCE |
40 | | ------------------------------------------------------------------------------ |
41 | | From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB] |
42 | | Protocol 08-00 00 00-00-00-00-00, 60 byte buffer at 10-OCT-2001 10:20:45.16 |
43 | | [E..<8...........]- 0-[45 00 00 3C 38 93 00 00 1D 06 D2 12 80 93 11 1A] |
44 | | [.........(......]- 16-[80 93 80 D6 02 D2 02 03 00 28 A4 90 00 00 00 00] |
45 | | [................]- 32-[A0 02 FF FF 95 BD 00 00 02 04 05 B4 03 03 04 01] |
46 | | [............ ]- 48-[01 01 08 0A 90 90 E5 14 00 00 00 00] |
47 | | ------------------------------------------------------------------------------ |
48 | | From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB] |
49 | | Protocol 08-00 00 00-00-00-00-00, 50 byte buffer at 10-OCT-2001 10:20:45.17 |
50 | | [E..(8......%....]- 0-[45 00 00 28 38 94 00 00 1D 06 D2 25 80 93 11 1A] |
51 | | [.........(..Z.4w]- 16-[80 93 80 D6 02 D2 02 03 00 28 A4 91 5A 1C 34 77] |
52 | | [P.#(.s..........]- 32-[50 10 23 28 C1 73 00 00 02 04 05 B4 03 03 00 00] |
53 | | [.. ]- 48-[02 04] |
54 | | |
55 | | |
56 | | Alternative HEX only output, slightly more efficient and all wireshark needs: |
57 | | ------------------------------------------------------------------------------ |
58 | | From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB] |
59 | | Protocol 08-00 00 00-00-00-00-00, 50 byte buffer at 10-OCT-2001 10:20:45.17 |
60 | | 0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A 80 93 80 D6] |
61 | | 20-[02 D2 02 03 00 28 A4 BF 5A 1C 34 79 50 10 23 28 C1 43 00 00] |
62 | | 40-[03 30 30 30 30 30 00 00 03 30] |
63 | | */ |
64 | | |
65 | | /* Magic text to check for DBS-ETHERWATCH-ness of file */ |
66 | | static const char dbs_etherwatch_hdr_magic[] = |
67 | | { 'E', 'T', 'H', 'E', 'R', 'W', 'A', 'T', 'C', 'H', ' '}; |
68 | | #define DBS_ETHERWATCH_HDR_MAGIC_SIZE \ |
69 | 0 | array_length(dbs_etherwatch_hdr_magic) |
70 | | |
71 | | /* Magic text for start of packet */ |
72 | | static const char dbs_etherwatch_rec_magic[] = |
73 | | {'F', 'r', 'o', 'm', ' '}; |
74 | | #define DBS_ETHERWATCH_REC_MAGIC_SIZE \ |
75 | 0 | array_length(dbs_etherwatch_rec_magic) |
76 | | |
77 | | /* |
78 | | * Default packet size - maximum normal Ethernet packet size, without an |
79 | | * FCS. |
80 | | */ |
81 | 0 | #define DBS_ETHERWATCH_MAX_ETHERNET_PACKET_LEN 1514 |
82 | | |
83 | | static bool dbs_etherwatch_read(wtap *wth, wtap_rec *rec, |
84 | | int *err, char **err_info, int64_t *data_offset); |
85 | | static bool dbs_etherwatch_seek_read(wtap *wth, int64_t seek_off, |
86 | | wtap_rec *rec, int *err, char **err_info); |
87 | | static bool parse_dbs_etherwatch_packet(wtap *wth, FILE_T fh, wtap_rec *rec, |
88 | | int *err, char **err_info); |
89 | | static unsigned parse_single_hex_dump_line(char* rec, Buffer *buf, |
90 | | int byte_offset); |
91 | | static unsigned parse_hex_dump(char* dump, Buffer *buf, char separator, char end); |
92 | | |
93 | | static int dbs_etherwatch_file_type_subtype = -1; |
94 | | |
95 | | void register_dbs_etherwatch(void); |
96 | | |
97 | | /* Seeks to the beginning of the next packet, and returns the |
98 | | byte offset. Returns -1 on failure, and sets "*err" to the error |
99 | | and "*err_info" to null or an additional error string. */ |
100 | | static int64_t dbs_etherwatch_seek_next_packet(wtap *wth, int *err, |
101 | | char **err_info) |
102 | 0 | { |
103 | 0 | int byte; |
104 | 0 | unsigned int level = 0; |
105 | 0 | int64_t cur_off; |
106 | |
|
107 | 0 | while ((byte = file_getc(wth->fh)) != EOF) { |
108 | 0 | if (byte == dbs_etherwatch_rec_magic[level]) { |
109 | 0 | level++; |
110 | 0 | if (level >= DBS_ETHERWATCH_REC_MAGIC_SIZE) { |
111 | | /* note: we're leaving file pointer right after the magic characters */ |
112 | 0 | cur_off = file_tell(wth->fh); |
113 | 0 | if (cur_off == -1) { |
114 | | /* Error. */ |
115 | 0 | *err = file_error(wth->fh, err_info); |
116 | 0 | return -1; |
117 | 0 | } |
118 | 0 | return cur_off + 1; |
119 | 0 | } |
120 | 0 | } else { |
121 | 0 | level = 0; |
122 | 0 | } |
123 | 0 | } |
124 | | /* EOF or error. */ |
125 | 0 | *err = file_error(wth->fh, err_info); |
126 | 0 | return -1; |
127 | 0 | } |
128 | | |
129 | 0 | #define DBS_ETHERWATCH_HEADER_LINES_TO_CHECK 200 |
130 | 0 | #define DBS_ETHERWATCH_LINE_LENGTH 240 |
131 | | |
132 | | /* Look through the first part of a file to see if this is |
133 | | * a DBS Ethertrace text trace file. |
134 | | * |
135 | | * Returns true if it is, false if it isn't or if we get an I/O error; |
136 | | * if we get an I/O error, "*err" will be set to a non-zero value and |
137 | | * "*err_info" will be set to null or an error string. |
138 | | */ |
139 | | static bool dbs_etherwatch_check_file_type(wtap *wth, int *err, |
140 | | char **err_info) |
141 | 0 | { |
142 | 0 | char buf[DBS_ETHERWATCH_LINE_LENGTH]; |
143 | 0 | int line, byte; |
144 | 0 | size_t reclen; |
145 | 0 | unsigned int i, level; |
146 | |
|
147 | 0 | buf[DBS_ETHERWATCH_LINE_LENGTH-1] = 0; |
148 | |
|
149 | 0 | for (line = 0; line < DBS_ETHERWATCH_HEADER_LINES_TO_CHECK; line++) { |
150 | 0 | if (file_gets(buf, DBS_ETHERWATCH_LINE_LENGTH, wth->fh) == NULL) { |
151 | | /* EOF or error. */ |
152 | 0 | *err = file_error(wth->fh, err_info); |
153 | 0 | return false; |
154 | 0 | } |
155 | | |
156 | 0 | reclen = strlen(buf); |
157 | 0 | if (reclen < DBS_ETHERWATCH_HDR_MAGIC_SIZE) |
158 | 0 | continue; |
159 | | |
160 | 0 | level = 0; |
161 | 0 | for (i = 0; i < reclen; i++) { |
162 | 0 | byte = buf[i]; |
163 | 0 | if (byte == dbs_etherwatch_hdr_magic[level]) { |
164 | 0 | level++; |
165 | 0 | if (level >= |
166 | 0 | DBS_ETHERWATCH_HDR_MAGIC_SIZE) { |
167 | 0 | return true; |
168 | 0 | } |
169 | 0 | } |
170 | 0 | else |
171 | 0 | level = 0; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | *err = 0; |
175 | 0 | return false; |
176 | 0 | } |
177 | | |
178 | | |
179 | | wtap_open_return_val dbs_etherwatch_open(wtap *wth, int *err, char **err_info) |
180 | 0 | { |
181 | | /* Look for DBS ETHERWATCH header */ |
182 | 0 | if (!dbs_etherwatch_check_file_type(wth, err, err_info)) { |
183 | 0 | if (*err != 0 && *err != WTAP_ERR_SHORT_READ) |
184 | 0 | return WTAP_OPEN_ERROR; |
185 | 0 | return WTAP_OPEN_NOT_MINE; |
186 | 0 | } |
187 | | |
188 | 0 | wth->file_encap = WTAP_ENCAP_ETHERNET; |
189 | 0 | wth->file_type_subtype = dbs_etherwatch_file_type_subtype; |
190 | 0 | wth->snapshot_length = 0; /* not known */ |
191 | 0 | wth->subtype_read = dbs_etherwatch_read; |
192 | 0 | wth->subtype_seek_read = dbs_etherwatch_seek_read; |
193 | 0 | wth->file_tsprec = WTAP_TSPREC_10_MSEC; |
194 | | |
195 | | /* |
196 | | * Add an IDB; we don't know how many interfaces were |
197 | | * involved, so we just say one interface, about which |
198 | | * we only know the link-layer type, snapshot length, |
199 | | * and time stamp resolution. |
200 | | */ |
201 | 0 | wtap_add_generated_idb(wth); |
202 | |
|
203 | 0 | return WTAP_OPEN_MINE; |
204 | 0 | } |
205 | | |
206 | | /* Find the next packet and parse it; called from wtap_read(). */ |
207 | | static bool dbs_etherwatch_read(wtap *wth, wtap_rec *rec, |
208 | | int *err, char **err_info, int64_t *data_offset) |
209 | 0 | { |
210 | 0 | int64_t offset; |
211 | | |
212 | | /* Find the next packet */ |
213 | 0 | offset = dbs_etherwatch_seek_next_packet(wth, err, err_info); |
214 | 0 | if (offset < 1) |
215 | 0 | return false; |
216 | 0 | *data_offset = offset; |
217 | | |
218 | | /* Parse the packet */ |
219 | 0 | return parse_dbs_etherwatch_packet(wth, wth->fh, rec, err, err_info); |
220 | 0 | } |
221 | | |
222 | | /* Used to read packets in random-access fashion */ |
223 | | static bool |
224 | | dbs_etherwatch_seek_read(wtap *wth, int64_t seek_off, |
225 | | wtap_rec *rec, int *err, char **err_info) |
226 | 0 | { |
227 | 0 | if (file_seek(wth->random_fh, seek_off - 1, SEEK_SET, err) == -1) |
228 | 0 | return false; |
229 | | |
230 | 0 | return parse_dbs_etherwatch_packet(wth, wth->random_fh, rec, err, err_info); |
231 | 0 | } |
232 | | |
233 | | /* Parse a packet */ |
234 | | /* |
235 | | Packet header: |
236 | | 1 2 3 4 |
237 | | 0123456789012345678901234567890123456789012345 |
238 | | From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB] |
239 | | Protocol 08-00 00 00-00-00-00-00, 50 byte buffer at 10-OCT-2001 10:20:45.17 |
240 | | */ |
241 | 0 | #define MAC_ADDR_LENGTH 6 /* Length MAC address */ |
242 | 0 | #define DEST_MAC_PREFIX "] to " /* Prefix to the dest. MAC address */ |
243 | 0 | #define PROTOCOL_LENGTH 2 /* Length protocol */ |
244 | 0 | #define PROTOCOL_POS 9 /* Position protocol */ |
245 | 0 | #define SAP_LENGTH 2 /* Length DSAP+SSAP */ |
246 | 0 | #define SAP_POS 9 /* Position DSAP+SSAP */ |
247 | 0 | #define CTL_UNNUMB_LENGTH 1 /* Length unnumbered control field */ |
248 | 0 | #define CTL_NUMB_LENGTH 2 /* Length numbered control field */ |
249 | 0 | #define CTL_POS 15 /* Position control field */ |
250 | 0 | #define PID_LENGTH 5 /* Length PID */ |
251 | 0 | #define PID_POS 18 /* Position PID */ |
252 | 0 | #define LENGTH_POS 33 /* Position length */ |
253 | 0 | #define HEX_HDR_SPR '-' /* Separator char header hex values */ |
254 | 0 | #define HEX_HDR_END ' ' /* End char hdr. hex val. except PID */ |
255 | 0 | #define HEX_PID_END ',' /* End char PID hex value */ |
256 | 0 | #define IEEE802_LEN_LEN 2 /* Length of the IEEE 802 len. field */ |
257 | | /* |
258 | | To check whether it is Ethernet II or IEEE 802 we check the values of the |
259 | | control field and PID, when they are all 0's we assume it is Ethernet II |
260 | | else IEEE 802. In IEEE 802 the DSAP and SSAP are behind protocol, the |
261 | | length in the IEEE data we have to construct. |
262 | | */ |
263 | 0 | #define ETH_II_CHECK_POS 15 |
264 | 0 | #define ETH_II_CHECK_STR "00 00-00-00-00-00," |
265 | | /* |
266 | | To check whether it IEEE 802.3 with SNAP we check that both the DSAP & SSAP |
267 | | values are 0xAA and the control field 0x03. |
268 | | */ |
269 | 0 | #define SNAP_CHECK_POS 9 |
270 | 0 | #define SNAP_CHECK_STR "AA-AA 03" |
271 | | /* |
272 | | To check whether the control field is 1 or two octets we check if it is |
273 | | unnumbered. Unnumbered has length 1, numbered 2. |
274 | | */ |
275 | 0 | #define CTL_UNNUMB_MASK 0x03 |
276 | 0 | #define CTL_UNNUMB_VALUE 0x03 |
277 | | static bool |
278 | | parse_dbs_etherwatch_packet(wtap *wth, FILE_T fh, wtap_rec *rec, |
279 | | int *err, char **err_info) |
280 | 0 | { |
281 | 0 | uint8_t *pd; |
282 | 0 | char line[DBS_ETHERWATCH_LINE_LENGTH]; |
283 | 0 | int num_items_scanned; |
284 | 0 | int pkt_len_signed, csec_signed; |
285 | 0 | unsigned eth_hdr_len, pkt_len, csec; |
286 | 0 | unsigned length_pos, length_from, length; |
287 | 0 | struct tm tm; |
288 | 0 | char mon[4] = "xxx"; |
289 | 0 | char *p; |
290 | 0 | static const char months[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; |
291 | 0 | unsigned count, line_count; |
292 | | |
293 | | /* Make sure we have enough room for a regular Ethernet packet */ |
294 | 0 | ws_buffer_assure_space(&rec->data, DBS_ETHERWATCH_MAX_ETHERNET_PACKET_LEN); |
295 | 0 | pd = ws_buffer_start_ptr(&rec->data); |
296 | |
|
297 | 0 | eth_hdr_len = 0; |
298 | 0 | memset(&tm, 0, sizeof(tm)); |
299 | | /* Our file pointer should be on the first line containing the |
300 | | * summary information for a packet. Read in that line and |
301 | | * extract the useful information |
302 | | */ |
303 | 0 | if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) { |
304 | 0 | *err = file_error(fh, err_info); |
305 | 0 | if (*err == 0) { |
306 | 0 | *err = WTAP_ERR_SHORT_READ; |
307 | 0 | } |
308 | 0 | return false; |
309 | 0 | } |
310 | | |
311 | | /* Get the destination address */ |
312 | 0 | p = strstr(line, DEST_MAC_PREFIX); |
313 | 0 | if(!p) { |
314 | 0 | *err = WTAP_ERR_BAD_FILE; |
315 | 0 | *err_info = g_strdup("dbs_etherwatch: destination address not found"); |
316 | 0 | return false; |
317 | 0 | } |
318 | 0 | p += strlen(DEST_MAC_PREFIX); |
319 | 0 | if(parse_hex_dump(p, &rec->data, HEX_HDR_SPR, HEX_HDR_END) |
320 | 0 | != MAC_ADDR_LENGTH) { |
321 | 0 | *err = WTAP_ERR_BAD_FILE; |
322 | 0 | *err_info = g_strdup("dbs_etherwatch: destination address not valid"); |
323 | 0 | return false; |
324 | 0 | } |
325 | 0 | eth_hdr_len += MAC_ADDR_LENGTH; |
326 | | |
327 | | /* Get the source address */ |
328 | | /* |
329 | | * Since the first part of the line is already skipped in order to find |
330 | | * the start of the record we cannot index, just look for the first |
331 | | * 'HEX' character |
332 | | */ |
333 | 0 | p = line; |
334 | 0 | while(!g_ascii_isxdigit(*p)) { |
335 | 0 | p++; |
336 | 0 | } |
337 | 0 | if(parse_hex_dump(p, &rec->data, HEX_HDR_SPR, |
338 | 0 | HEX_HDR_END) != MAC_ADDR_LENGTH) { |
339 | 0 | *err = WTAP_ERR_BAD_FILE; |
340 | 0 | *err_info = g_strdup("dbs_etherwatch: source address not valid"); |
341 | 0 | return false; |
342 | 0 | } |
343 | 0 | eth_hdr_len += MAC_ADDR_LENGTH; |
344 | | |
345 | | /* Read the next line of the record header */ |
346 | 0 | if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) { |
347 | 0 | *err = file_error(fh, err_info); |
348 | 0 | if (*err == 0) { |
349 | 0 | *err = WTAP_ERR_SHORT_READ; |
350 | 0 | } |
351 | 0 | return false; |
352 | 0 | } |
353 | | |
354 | | /* Check the lines is as least as long as the length position */ |
355 | 0 | if(strlen(line) < LENGTH_POS) { |
356 | 0 | *err = WTAP_ERR_BAD_FILE; |
357 | 0 | *err_info = g_strdup("dbs_etherwatch: line too short"); |
358 | 0 | return false; |
359 | 0 | } |
360 | | |
361 | | /* |
362 | | * %u doesn't fail if the value has a sign; it works like the |
363 | | * strto*() routines. |
364 | | * |
365 | | * Use %d, scan into signed values, and then check whether they're |
366 | | * < 0. |
367 | | */ |
368 | 0 | num_items_scanned = sscanf(line + LENGTH_POS, |
369 | 0 | "%9d byte buffer at %2d-%3s-%4d %2d:%2d:%2d.%9d", |
370 | 0 | &pkt_len_signed, |
371 | 0 | &tm.tm_mday, mon, |
372 | 0 | &tm.tm_year, &tm.tm_hour, &tm.tm_min, |
373 | 0 | &tm.tm_sec, &csec_signed); |
374 | |
|
375 | 0 | if (num_items_scanned != 8) { |
376 | 0 | *err = WTAP_ERR_BAD_FILE; |
377 | 0 | *err_info = g_strdup("dbs_etherwatch: header line not valid"); |
378 | 0 | return false; |
379 | 0 | } |
380 | | |
381 | 0 | if (pkt_len_signed < 0) { |
382 | 0 | *err = WTAP_ERR_BAD_FILE; |
383 | 0 | *err_info = g_strdup("dbs_etherwatch: packet header has a negative packet length"); |
384 | 0 | return false; |
385 | 0 | } |
386 | | |
387 | 0 | if (csec_signed < 0) { |
388 | 0 | *err = WTAP_ERR_BAD_FILE; |
389 | 0 | *err_info = g_strdup("dbs_etherwatch: packet header has a negative microseconds time stamp"); |
390 | 0 | return false; |
391 | 0 | } |
392 | | |
393 | 0 | pkt_len = (unsigned) pkt_len_signed; |
394 | 0 | csec = (unsigned) csec_signed; |
395 | | |
396 | | /* Determine whether it is Ethernet II or IEEE 802 */ |
397 | 0 | if(strncmp(&line[ETH_II_CHECK_POS], ETH_II_CHECK_STR, |
398 | 0 | strlen(ETH_II_CHECK_STR)) == 0) { |
399 | | /* Ethernet II */ |
400 | | /* Get the Protocol */ |
401 | 0 | if(parse_hex_dump(&line[PROTOCOL_POS], &rec->data, HEX_HDR_SPR, |
402 | 0 | HEX_HDR_END) != PROTOCOL_LENGTH) { |
403 | 0 | *err = WTAP_ERR_BAD_FILE; |
404 | 0 | *err_info = g_strdup("dbs_etherwatch: Ethernet II protocol value not valid"); |
405 | 0 | return false; |
406 | 0 | } |
407 | 0 | eth_hdr_len += PROTOCOL_LENGTH; |
408 | 0 | } else { |
409 | | /* IEEE 802 */ |
410 | | /* Remember where to put the length in the header */ |
411 | 0 | length_pos = eth_hdr_len; |
412 | | /* Leave room in the header for the length */ |
413 | 0 | eth_hdr_len += IEEE802_LEN_LEN; |
414 | | /* Remember how much of the header should not be added to the length */ |
415 | 0 | length_from = eth_hdr_len; |
416 | | /* Get the DSAP + SSAP */ |
417 | 0 | if(parse_hex_dump(&line[SAP_POS], &rec->data, HEX_HDR_SPR, |
418 | 0 | HEX_HDR_END) != SAP_LENGTH) { |
419 | 0 | *err = WTAP_ERR_BAD_FILE; |
420 | 0 | *err_info = g_strdup("dbs_etherwatch: 802.2 DSAP+SSAP value not valid"); |
421 | 0 | return false; |
422 | 0 | } |
423 | 0 | eth_hdr_len += SAP_LENGTH; |
424 | | /* Get the (first part of the) control field */ |
425 | 0 | if(parse_hex_dump(&line[CTL_POS], &rec->data, HEX_HDR_SPR, |
426 | 0 | HEX_HDR_END) != CTL_UNNUMB_LENGTH) { |
427 | 0 | *err = WTAP_ERR_BAD_FILE; |
428 | 0 | *err_info = g_strdup("dbs_etherwatch: 802.2 control field first part not valid"); |
429 | 0 | return false; |
430 | 0 | } |
431 | | /* Determine whether the control is numbered, and thus longer */ |
432 | 0 | if((pd[eth_hdr_len] & CTL_UNNUMB_MASK) != CTL_UNNUMB_VALUE) { |
433 | | /* Get the rest of the control field, the first octet in the PID */ |
434 | 0 | if(parse_hex_dump(&line[PID_POS], &rec->data, HEX_HDR_END, |
435 | 0 | HEX_HDR_SPR) != CTL_NUMB_LENGTH - CTL_UNNUMB_LENGTH) { |
436 | 0 | *err = WTAP_ERR_BAD_FILE; |
437 | 0 | *err_info = g_strdup("dbs_etherwatch: 802.2 control field second part value not valid"); |
438 | 0 | return false; |
439 | 0 | } |
440 | 0 | eth_hdr_len += CTL_NUMB_LENGTH; |
441 | 0 | } else { |
442 | 0 | eth_hdr_len += CTL_UNNUMB_LENGTH; |
443 | 0 | } |
444 | | /* Determine whether it is SNAP */ |
445 | 0 | if(strncmp(&line[SNAP_CHECK_POS], SNAP_CHECK_STR, |
446 | 0 | strlen(SNAP_CHECK_STR)) == 0) { |
447 | | /* Get the PID */ |
448 | 0 | if(parse_hex_dump(&line[PID_POS], &rec->data, HEX_HDR_SPR, |
449 | 0 | HEX_PID_END) != PID_LENGTH) { |
450 | 0 | *err = WTAP_ERR_BAD_FILE; |
451 | 0 | *err_info = g_strdup("dbs_etherwatch: 802.2 PID value not valid"); |
452 | 0 | return false; |
453 | 0 | } |
454 | 0 | eth_hdr_len += PID_LENGTH; |
455 | 0 | } |
456 | | /* Write the length in the header */ |
457 | 0 | length = (eth_hdr_len - length_from) + pkt_len; |
458 | 0 | phtonu16(&pd[length_pos], length); |
459 | 0 | } |
460 | | |
461 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
462 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
463 | 0 | rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; |
464 | |
|
465 | 0 | p = strstr(months, mon); |
466 | 0 | if (p) |
467 | 0 | tm.tm_mon = (int)(p - months) / 3; |
468 | 0 | tm.tm_year -= 1900; |
469 | |
|
470 | 0 | tm.tm_isdst = -1; |
471 | 0 | rec->ts.secs = mktime(&tm); |
472 | 0 | rec->ts.nsecs = csec * 10000000; |
473 | 0 | rec->rec_header.packet_header.caplen = eth_hdr_len + pkt_len; |
474 | 0 | rec->rec_header.packet_header.len = eth_hdr_len + pkt_len; |
475 | |
|
476 | 0 | if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) { |
477 | | /* |
478 | | * Probably a corrupt capture file; return an error, |
479 | | * so that our caller doesn't blow up trying to allocate |
480 | | * space for an immensely-large packet. |
481 | | */ |
482 | 0 | *err = WTAP_ERR_BAD_FILE; |
483 | 0 | *err_info = ws_strdup_printf("dbs_etherwatch: File has %u-byte packet, bigger than maximum of %u", |
484 | 0 | rec->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD); |
485 | 0 | return false; |
486 | 0 | } |
487 | | |
488 | | /* Make sure we have enough room, even for an oversized Ethernet packet */ |
489 | 0 | ws_buffer_assure_space(&rec->data, rec->rec_header.packet_header.caplen); |
490 | 0 | pd = ws_buffer_start_ptr(&rec->data); |
491 | | |
492 | | /* |
493 | | * We don't have an FCS in this frame. |
494 | | */ |
495 | 0 | rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0; |
496 | | |
497 | | /* Parse the hex dump */ |
498 | 0 | count = 0; |
499 | 0 | while (count < pkt_len) { |
500 | 0 | if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) { |
501 | 0 | *err = file_error(fh, err_info); |
502 | 0 | if (*err == 0) { |
503 | 0 | *err = WTAP_ERR_SHORT_READ; |
504 | 0 | } |
505 | 0 | return false; |
506 | 0 | } |
507 | 0 | if (!(line_count = parse_single_hex_dump_line(line, |
508 | 0 | &rec->data, count))) { |
509 | 0 | *err = WTAP_ERR_BAD_FILE; |
510 | 0 | *err_info = g_strdup("dbs_etherwatch: packet data value not valid"); |
511 | 0 | return false; |
512 | 0 | } |
513 | 0 | count += line_count; |
514 | 0 | if (count > pkt_len) { |
515 | 0 | *err = WTAP_ERR_BAD_FILE; |
516 | 0 | *err_info = g_strdup("dbs_etherwatch: packet data value has too many bytes"); |
517 | 0 | return false; |
518 | 0 | } |
519 | 0 | } |
520 | 0 | return true; |
521 | 0 | } |
522 | | |
523 | | /* Parse a hex dump line */ |
524 | | /* |
525 | | /DISPLAY=BOTH output: |
526 | | |
527 | | 1 2 3 4 |
528 | | 0123456789012345678901234567890123456789012345 |
529 | | [E..(8...........]- 0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A] |
530 | | [.........(..Z.4y]- 16-[80 93 80 D6 02 D2 02 03 00 28 A4 BF 5A 1C 34 79] |
531 | | [P.#(.C...00000..]- 32-[50 10 23 28 C1 43 00 00 03 30 30 30 30 30 00 00] |
532 | | [.0 ]- 48-[03 30] |
533 | | |
534 | | /DISPLAY=HEXADECIMAL output: |
535 | | |
536 | | 1 2 3 4 |
537 | | 0123456789012345678901234567890123456789012345 |
538 | | 0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A 80 93 80 D6] |
539 | | 20-[02 D2 02 03 00 28 A4 BF 5A 1C 34 79 50 10 23 28 C1 43 00 00] |
540 | | 40-[03 30 30 30 30 30 00 00 03 30] |
541 | | |
542 | | */ |
543 | | |
544 | 0 | #define TYPE_CHECK_POS 2 /* Position to check the type of hex dump */ |
545 | 0 | #define TYPE_CHECK_BOTH '[' /* Value at pos. that indicates BOTH type */ |
546 | 0 | #define COUNT_POS_BOTH 21 /* Count position BOTH type */ |
547 | 0 | #define COUNT_POS_HEX 1 /* Count position HEX type */ |
548 | 0 | #define COUNT_SIZE 5 /* Length counter */ |
549 | 0 | #define HEX_DUMP_START '[' /* Start char */ |
550 | 0 | #define HEX_DUMP_SPR ' ' /* Separator char */ |
551 | 0 | #define HEX_DUMP_END ']' /* End char */ |
552 | | |
553 | | /* Take a string representing one line from a hex dump and converts the |
554 | | * text to binary data. We check the printed offset with the offset |
555 | | * we are passed to validate the record. We place the bytes in the buffer |
556 | | * at the specified offset. |
557 | | * |
558 | | * Returns length parsed if a good hex dump, 0 if bad. |
559 | | */ |
560 | | static unsigned |
561 | 0 | parse_single_hex_dump_line(char* rec, Buffer *buf, int byte_offset) { |
562 | |
|
563 | 0 | int pos, i; |
564 | 0 | int value; |
565 | | |
566 | | |
567 | | /* Check that the record is as least as long as the check offset */ |
568 | 0 | for(i = 0; i < TYPE_CHECK_POS; i++) |
569 | 0 | { |
570 | 0 | if(rec[i] == '\0') { |
571 | 0 | return 0; |
572 | 0 | } |
573 | 0 | } |
574 | | /* determine the format and thus the counter offset and hex dump length */ |
575 | 0 | if(rec[TYPE_CHECK_POS] == TYPE_CHECK_BOTH) |
576 | 0 | { |
577 | 0 | pos = COUNT_POS_BOTH; |
578 | 0 | } |
579 | 0 | else |
580 | 0 | { |
581 | 0 | pos = COUNT_POS_HEX; |
582 | 0 | } |
583 | | |
584 | | /* Check that the record is as least as long as the start position */ |
585 | 0 | while(i < pos) |
586 | 0 | { |
587 | 0 | if(rec[i] == '\0') { |
588 | 0 | return 0; |
589 | 0 | } |
590 | 0 | i++; |
591 | 0 | } |
592 | | |
593 | | /* Get the byte_offset directly from the record */ |
594 | 0 | value = 0; |
595 | 0 | for(i = 0; i < COUNT_SIZE; i++) { |
596 | 0 | if(!g_ascii_isspace(rec[pos])) { |
597 | 0 | if(g_ascii_isdigit(rec[pos])) { |
598 | 0 | value *= 10; |
599 | 0 | value += rec[pos] - '0'; |
600 | 0 | } else { |
601 | 0 | return 0; |
602 | 0 | } |
603 | 0 | } |
604 | 0 | pos++; |
605 | 0 | } |
606 | | |
607 | 0 | if (value != byte_offset) { |
608 | 0 | return 0; |
609 | 0 | } |
610 | | |
611 | | /* find the start of the hex dump */ |
612 | 0 | while(rec[pos] != HEX_DUMP_START) { |
613 | 0 | if(rec[pos] == '\0') { |
614 | 0 | return 0; |
615 | 0 | } |
616 | 0 | pos++; |
617 | 0 | } |
618 | 0 | pos++; |
619 | 0 | return parse_hex_dump(&rec[pos], buf, HEX_DUMP_SPR, HEX_DUMP_END); |
620 | 0 | } |
621 | | |
622 | | /* Parse a hex dump */ |
623 | | static unsigned |
624 | 0 | parse_hex_dump(char* dump, Buffer *buf, char separator, char end) { |
625 | 0 | int pos, count; |
626 | | |
627 | | /* Parse the hex dump */ |
628 | 0 | pos = 0; |
629 | 0 | count = 0; |
630 | 0 | uint8_t *pd = ws_buffer_end_ptr(buf); |
631 | 0 | while(dump[pos] != end) { |
632 | | /* Check the hex value */ |
633 | 0 | if(!(g_ascii_isxdigit(dump[pos]) && |
634 | 0 | g_ascii_isxdigit(dump[pos + 1]))) { |
635 | 0 | return 0; |
636 | 0 | } |
637 | | /* Get the hex value */ |
638 | 0 | if(g_ascii_isdigit(dump[pos])) { |
639 | 0 | pd[count] = (dump[pos] - '0') << 4; |
640 | 0 | } else { |
641 | 0 | pd[count] = (g_ascii_toupper(dump[pos]) - 'A' + 10) << 4; |
642 | 0 | } |
643 | 0 | pos++; |
644 | 0 | if(g_ascii_isdigit(dump[pos])) { |
645 | 0 | pd[count] += dump[pos] - '0'; |
646 | 0 | } else { |
647 | 0 | pd[count] += g_ascii_toupper(dump[pos]) - 'A' + 10; |
648 | 0 | } |
649 | 0 | pos++; |
650 | 0 | count++; |
651 | | /* Skip the separator characters */ |
652 | 0 | while(dump[pos] == separator) { |
653 | 0 | pos++; |
654 | 0 | } |
655 | 0 | } |
656 | 0 | ws_buffer_increase_length(buf, count); |
657 | 0 | return count; |
658 | 0 | } |
659 | | |
660 | | static const struct supported_block_type dbs_etherwatch_blocks_supported[] = { |
661 | | /* |
662 | | * We support packet blocks, with no comments or other options. |
663 | | */ |
664 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
665 | | }; |
666 | | |
667 | | static const struct file_type_subtype_info dbs_etherwatch_info = { |
668 | | "DBS Etherwatch (VMS)", "etherwatch", "txt", NULL, |
669 | | false, BLOCKS_SUPPORTED(dbs_etherwatch_blocks_supported), |
670 | | NULL, NULL, NULL |
671 | | }; |
672 | | |
673 | | void register_dbs_etherwatch(void) |
674 | 14 | { |
675 | 14 | dbs_etherwatch_file_type_subtype = wtap_register_file_type_subtype(&dbs_etherwatch_info); |
676 | | |
677 | | /* |
678 | | * Register name for backwards compatibility with the |
679 | | * wtap_filetypes table in Lua. |
680 | | */ |
681 | 14 | wtap_register_backwards_compatibility_lua_name("DBS_ETHERWATCH", |
682 | 14 | dbs_etherwatch_file_type_subtype); |
683 | 14 | } |
684 | | |
685 | | /* |
686 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
687 | | * |
688 | | * Local variables: |
689 | | * c-basic-offset: 4 |
690 | | * tab-width: 8 |
691 | | * indent-tabs-mode: nil |
692 | | * End: |
693 | | * |
694 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
695 | | * :indentSize=4:tabSize=8:noTabs=true: |
696 | | */ |