3 * Juniper NetScreen snoop output parser
4 * Created by re-using a lot of code from cosine.c
5 * Copyright (c) 2007 by Sake Blok <sake@euronet.nl>
8 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include "netscreen.h"
16 #include "file_wrappers.h"
23 * o Construct a list of interfaces, with interface names, give
24 * them link-layer types based on the interface name and packet
25 * data, and supply interface IDs with each packet (i.e., make
26 * this supply a pcapng-style set of interfaces and associate
27 * packets with interfaces). This is probably the right way
28 * to "Pass the interface names and the traffic direction to either
29 * the frame-structure, a pseudo-header or use PPI." See the
32 * https://lists.wireshark.org/archives/wireshark-dev/200708/msg00029.html
34 * to see whether any further discussion is still needed. I suspect
35 * it doesn't; pcapng existed at the time, as per the final
36 * message in that thread:
38 * https://lists.wireshark.org/archives/wireshark-dev/200708/msg00039.html
40 * but I don't think we fully *supported* it at that point. Now
41 * that we do, we have the infrastructure to support this, except
42 * that we currently have no way to translate interface IDs to
43 * interface names in the "frame" dissector or to supply interface
44 * information as part of the packet metadata from Wiretap modules.
45 * That should be fixed so that we can show interface information,
46 * such as the interface name, in packet dissections from, for example,
50 static bool info_line(const char *line
);
51 static int64_t netscreen_seek_next_packet(wtap
*wth
, int *err
, char **err_info
,
53 static bool netscreen_check_file_type(wtap
*wth
, int *err
,
55 static bool netscreen_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
56 int *err
, char **err_info
, int64_t *data_offset
);
57 static bool netscreen_seek_read(wtap
*wth
, int64_t seek_off
,
58 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
);
59 static bool parse_netscreen_packet(FILE_T fh
, wtap_rec
*rec
,
60 Buffer
* buf
, char *line
, int *err
, char **err_info
);
61 static int parse_single_hex_dump_line(char* rec
, uint8_t *buf
,
62 unsigned byte_offset
, unsigned pkt_len
);
64 /* Error returns from parse_single_hex_dump_line() */
65 #define PARSE_LINE_INVALID_CHARACTER -1
66 #define PARSE_LINE_NO_BYTES_SEEN -2
67 #define PARSE_LINE_TOO_MANY_BYTES_SEEN -3
69 static int netscreen_file_type_subtype
= -1;
71 void register_netscreen(void);
73 /* Returns true if the line appears to be a line with protocol info.
74 Otherwise it returns false. */
75 static bool info_line(const char *line
)
77 int i
=NETSCREEN_SPACES_ON_INFO_LINE
;
80 if (g_ascii_isspace(*line
)) {
90 /* Seeks to the beginning of the next packet, and returns the
91 byte offset. Copy the header line to hdr. Returns -1 on failure,
92 and sets "*err" to the error and sets "*err_info" to null or an
93 additional error string. */
94 static int64_t netscreen_seek_next_packet(wtap
*wth
, int *err
, char **err_info
,
98 char buf
[NETSCREEN_LINE_LENGTH
];
101 cur_off
= file_tell(wth
->fh
);
104 *err
= file_error(wth
->fh
, err_info
);
107 if (file_gets(buf
, sizeof(buf
), wth
->fh
) == NULL
) {
109 *err
= file_error(wth
->fh
, err_info
);
112 if (strstr(buf
, NETSCREEN_REC_MAGIC_STR1
) ||
113 strstr(buf
, NETSCREEN_REC_MAGIC_STR2
)) {
114 (void) g_strlcpy(hdr
, buf
, NETSCREEN_LINE_LENGTH
);
121 /* Look through the first part of a file to see if this is
122 * NetScreen snoop output.
124 * Returns true if it is, false if it isn't or if we get an I/O error;
125 * if we get an I/O error, "*err" will be set to a non-zero value and
126 * "*err_info" is set to null or an additional error string.
128 static bool netscreen_check_file_type(wtap
*wth
, int *err
, char **err_info
)
130 char buf
[NETSCREEN_LINE_LENGTH
];
131 unsigned reclen
, line
;
133 buf
[NETSCREEN_LINE_LENGTH
-1] = '\0';
135 for (line
= 0; line
< NETSCREEN_HEADER_LINES_TO_CHECK
; line
++) {
136 if (file_gets(buf
, NETSCREEN_LINE_LENGTH
, wth
->fh
) == NULL
) {
138 *err
= file_error(wth
->fh
, err_info
);
142 reclen
= (unsigned) strlen(buf
);
143 if (reclen
< MIN(strlen(NETSCREEN_HDR_MAGIC_STR1
), strlen(NETSCREEN_HDR_MAGIC_STR2
))) {
147 if (strstr(buf
, NETSCREEN_HDR_MAGIC_STR1
) ||
148 strstr(buf
, NETSCREEN_HDR_MAGIC_STR2
)) {
157 wtap_open_return_val
netscreen_open(wtap
*wth
, int *err
, char **err_info
)
160 /* Look for a NetScreen snoop header line */
161 if (!netscreen_check_file_type(wth
, err
, err_info
)) {
162 if (*err
!= 0 && *err
!= WTAP_ERR_SHORT_READ
)
163 return WTAP_OPEN_ERROR
;
164 return WTAP_OPEN_NOT_MINE
;
167 if (file_seek(wth
->fh
, 0L, SEEK_SET
, err
) == -1) /* rewind */
168 return WTAP_OPEN_ERROR
;
170 wth
->file_encap
= WTAP_ENCAP_UNKNOWN
;
171 wth
->file_type_subtype
= netscreen_file_type_subtype
;
172 wth
->snapshot_length
= 0; /* not known */
173 wth
->subtype_read
= netscreen_read
;
174 wth
->subtype_seek_read
= netscreen_seek_read
;
175 wth
->file_tsprec
= WTAP_TSPREC_100_MSEC
;
177 return WTAP_OPEN_MINE
;
180 /* Find the next packet and parse it; called from wtap_read(). */
181 static bool netscreen_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
182 int *err
, char **err_info
, int64_t *data_offset
)
185 char line
[NETSCREEN_LINE_LENGTH
];
187 /* Find the next packet */
188 offset
= netscreen_seek_next_packet(wth
, err
, err_info
, line
);
192 /* Parse the header and convert the ASCII hex dump to binary data */
193 if (!parse_netscreen_packet(wth
->fh
, rec
, buf
, line
, err
, err_info
))
197 * If the per-file encapsulation isn't known, set it to this
198 * packet's encapsulation.
200 * If it *is* known, and it isn't this packet's encapsulation,
201 * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
202 * have a single encapsulation for all packets in the file.
204 if (wth
->file_encap
== WTAP_ENCAP_UNKNOWN
)
205 wth
->file_encap
= rec
->rec_header
.packet_header
.pkt_encap
;
207 if (wth
->file_encap
!= rec
->rec_header
.packet_header
.pkt_encap
)
208 wth
->file_encap
= WTAP_ENCAP_PER_PACKET
;
211 *data_offset
= offset
;
215 /* Used to read packets in random-access fashion */
217 netscreen_seek_read(wtap
*wth
, int64_t seek_off
, wtap_rec
*rec
, Buffer
*buf
,
218 int *err
, char **err_info
)
220 char line
[NETSCREEN_LINE_LENGTH
];
222 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1) {
226 if (file_gets(line
, NETSCREEN_LINE_LENGTH
, wth
->random_fh
) == NULL
) {
227 *err
= file_error(wth
->random_fh
, err_info
);
229 *err
= WTAP_ERR_SHORT_READ
;
234 return parse_netscreen_packet(wth
->random_fh
, rec
, buf
, line
,
238 /* Parses a packet record header. There are a few possible formats:
240 * XXX list extra formats here!
241 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
242 192.168.1.1 -> 192.168.1.10/6
243 vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
244 tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
245 00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00 .`.h.Y.....2..E.
246 00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8 .T.k..@.c.......
247 01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18 ..........[..(P.
248 1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09 ..y!...v.d......
249 31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61 1..(.X....=e...a
250 2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9 ,!...`..5.......
253 * The first line of a packet is in the form
255 <secs>.<dsecs>: <iface>({i,o}) len=<length>:<llinfo>>
259 * <secs> and <dsecs> are a time stamp in seconds and deciseconds,
260 * giving the time since the firewall was booted;
262 * <iface> is the name of the interface on which the packet was
263 * received or on which it was transmitted;
265 * {i,o} is i for a received packet and o for a transmitted packet;
267 * <length> is the length of the packet on the network;
269 * <llinfo>, at least for Ethernet, appears to be a source MAC
270 * address, folowed by "->", folowed by a destination MAC
271 * address, followed by a sequence of Ethertypes, each
272 * preceded by a "/" (multiple Ethertypes if there are VLAN
273 * tags and the like), possibly followed by ", tag <tag>".
275 * Following that may be some "info lines", each of which is indented
276 * by 14 spaces, giving a dissection of the payload after the
279 * Following that is a hex/ASCII dump of the contents of the
280 * packet, with 16 octets per line.
283 parse_netscreen_packet(FILE_T fh
, wtap_rec
*rec
, Buffer
* buf
,
284 char *line
, int *err
, char **err_info
)
289 char cap_int
[NETSCREEN_MAX_INT_NAME_LENGTH
];
299 rec
->rec_type
= REC_TYPE_PACKET
;
300 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
301 rec
->presence_flags
= WTAP_HAS_TS
|WTAP_HAS_CAP_LEN
;
302 /* Suppress compiler warnings */
303 memset(cap_int
, 0, sizeof(cap_int
));
304 memset(cap_dst
, 0, sizeof(cap_dst
));
306 if (sscanf(line
, "%9d.%9d: %15[a-z0-9/:.-](%1[io]) len=%9u:%12s->%12s/",
307 &sec
, &dsec
, cap_int
, direction
, &pkt_len
, cap_src
, cap_dst
) < 5) {
308 *err
= WTAP_ERR_BAD_FILE
;
309 *err_info
= g_strdup("netscreen: Can't parse packet-header");
312 if (pkt_len
> WTAP_MAX_PACKET_SIZE_STANDARD
) {
314 * Probably a corrupt capture file; don't blow up trying
315 * to allocate space for an immensely-large packet.
317 *err
= WTAP_ERR_BAD_FILE
;
318 *err_info
= ws_strdup_printf("netscreen: File has %u-byte packet, bigger than maximum of %u",
319 pkt_len
, WTAP_MAX_PACKET_SIZE_STANDARD
);
324 * If direction[0] is 'o', the direction is NETSCREEN_EGRESS,
325 * otherwise it's NETSCREEN_INGRESS.
329 rec
->ts
.nsecs
= dsec
* 100000000;
330 rec
->rec_header
.packet_header
.len
= pkt_len
;
332 /* Make sure we have enough room for the packet */
333 ws_buffer_assure_space(buf
, pkt_len
);
334 pd
= ws_buffer_start_ptr(buf
);
338 /* The last packet is not delimited by an empty line, but by EOF
339 * So accept EOF as a valid delimiter too
341 if (file_gets(line
, NETSCREEN_LINE_LENGTH
, fh
) == NULL
) {
347 * The number of blanks is not fixed - for wireless
348 * interfaces, there may be 14 extra spaces before
351 for (p
= &line
[0]; g_ascii_isspace(*p
); p
++)
353 /* packets are delimited with empty lines */
358 n
= parse_single_hex_dump_line(p
, pd
, offset
, pkt_len
);
361 * The smallest packet has a length of 6 bytes.
362 * If the first line either gets an error when
363 * parsed as hex data, or has fewer than 6
364 * bytes of hex data, check whether it's an
365 * info line by see if it has at least
366 * NETSCREEN_SPACES_ON_INFO_LINE spaces at the
369 * If it does, count this line and, if we have,
370 * so far, skipped no more than NETSCREEN_MAX_INFOLINES
371 * lines, skip this line.
373 if (offset
== 0 && n
< 6) {
374 if (info_line(line
)) {
376 if (++i
<= NETSCREEN_MAX_INFOLINES
) {
382 *err
= WTAP_ERR_BAD_FILE
;
383 *err_info
= g_strdup("netscreen: first line of packet data has only %d hex bytes, < 6");
386 /* Otherwise, fall through to report error */
390 /* If there is no more data and the line was not empty,
391 * then there must be an error in the file
396 case PARSE_LINE_INVALID_CHARACTER
:
397 *err
= WTAP_ERR_BAD_FILE
;
398 *err_info
= g_strdup("netscreen: invalid character in hex data");
401 case PARSE_LINE_NO_BYTES_SEEN
:
402 *err
= WTAP_ERR_BAD_FILE
;
403 *err_info
= g_strdup("netscreen: no hex bytes seen in hex data");
406 case PARSE_LINE_TOO_MANY_BYTES_SEEN
:
407 *err
= WTAP_ERR_BAD_FILE
;
408 *err_info
= g_strdup("netscreen: number of hex bytes seen in hex data is greater than the packet length");
412 *err
= WTAP_ERR_INTERNAL
;
413 *err_info
= g_strdup_printf("netscreen: unknown error %d from parse_single_hex_dump_line()", n
);
420 /* Adjust the offset to the data that was just added to the buffer */
426 * Determine the encapsulation type, based on the
427 * first 4 characters of the interface name
429 * XXX convert this to a 'case' structure when adding more
430 * (non-ethernet) interfacetypes
432 if (strncmp(cap_int
, "adsl", 4) == 0) {
433 /* The ADSL interface can be bridged with or without
434 * PPP encapsulation. Check whether the first six bytes
435 * of the hex data are the same as the destination mac
436 * address in the header. If they are, assume ethernet
437 * LinkLayer or else PPP
439 snprintf(dststr
, 13, "%02x%02x%02x%02x%02x%02x",
440 pd
[0], pd
[1], pd
[2], pd
[3], pd
[4], pd
[5]);
441 if (strncmp(dststr
, cap_dst
, 12) == 0)
442 rec
->rec_header
.packet_header
.pkt_encap
= WTAP_ENCAP_ETHERNET
;
444 rec
->rec_header
.packet_header
.pkt_encap
= WTAP_ENCAP_PPP
;
446 else if (strncmp(cap_int
, "seri", 4) == 0)
447 rec
->rec_header
.packet_header
.pkt_encap
= WTAP_ENCAP_PPP
;
449 rec
->rec_header
.packet_header
.pkt_encap
= WTAP_ENCAP_ETHERNET
;
451 rec
->rec_header
.packet_header
.caplen
= offset
;
456 /* Take a string representing one line from a hex dump, with leading white
457 * space removed, and converts the text to binary data. We place the bytes
458 * in the buffer at the specified offset.
460 * Returns number of bytes successfully read, -1 if bad. */
462 parse_single_hex_dump_line(char* rec
, uint8_t *buf
, unsigned byte_offset
, unsigned pkt_len
)
464 int num_items_scanned
;
469 for (num_items_scanned
= 0; num_items_scanned
< 16; num_items_scanned
++) {
471 if (character
>= '0' && character
<= '9')
472 byte
= character
- '0' + 0;
473 else if (character
>= 'A' && character
<= 'F')
474 byte
= character
- 'A' + 0xA;
475 else if (character
>= 'a' && character
<= 'f')
476 byte
= character
- 'a' + 0xa;
477 else if (character
== ' ' || character
== '\r' || character
== '\n' || character
== '\0') {
478 /* Nothing more to parse */
481 return PARSE_LINE_INVALID_CHARACTER
; /* not a hex digit, space before ASCII dump, or EOL */
483 character
= *rec
++ & 0xFF;
484 if (character
>= '0' && character
<= '9')
485 byte
+= character
- '0' + 0;
486 else if (character
>= 'A' && character
<= 'F')
487 byte
+= character
- 'A' + 0xA;
488 else if (character
>= 'a' && character
<= 'f')
489 byte
+= character
- 'a' + 0xa;
491 return PARSE_LINE_INVALID_CHARACTER
; /* not a hex digit */
493 /* If there was more hex-data than was announced in the len=x
494 * header, then there must be an error in the file; quit
495 * now, as adding this byte will overflow the buffer.
497 if (byte_offset
+ num_items_scanned
>= pkt_len
) {
498 return PARSE_LINE_TOO_MANY_BYTES_SEEN
;
501 buf
[byte_offset
+ num_items_scanned
] = byte
;
502 character
= *rec
++ & 0xFF;
503 if (character
== '\0' || character
== '\r' || character
== '\n') {
504 /* Nothing more to parse */
506 } else if (character
!= ' ') {
507 /* not space before ASCII dump */
508 return PARSE_LINE_INVALID_CHARACTER
;
511 if (num_items_scanned
== 0)
512 return PARSE_LINE_NO_BYTES_SEEN
;
514 return num_items_scanned
;
517 static const struct supported_block_type netscreen_blocks_supported
[] = {
519 * We support packet blocks, with no comments or other options.
521 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
524 static const struct file_type_subtype_info netscreen_info
= {
525 "NetScreen snoop text file", "netscreen", "txt", NULL
,
526 false, BLOCKS_SUPPORTED(netscreen_blocks_supported
),
530 void register_netscreen(void)
532 netscreen_file_type_subtype
= wtap_register_file_type_subtype(&netscreen_info
);
535 * Register name for backwards compatibility with the
536 * wtap_filetypes table in Lua.
538 wtap_register_backwards_compatibility_lua_name("NETSCREEN",
539 netscreen_file_type_subtype
);
543 * Editor modelines - https://www.wireshark.org/tools/modelines.html
548 * indent-tabs-mode: t
551 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
552 * :indentSize=8:tabSize=8:noTabs=false: