5 * Juniper NetScreen snoop output parser
6 * Created by re-using a lot of code from cosine.c
7 * Copyright (c) 2007 by Sake Blok <sake@euronet.nl>
10 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include "netscreen.h"
31 #include "file_wrappers.h"
40 * o Construct a list of interfaces, with interface names, give
41 * them link-layer types based on the interface name and packet
42 * data, and supply interface IDs with each packet (i.e., make
43 * this supply a pcap-ng-style set of interfaces and associate
44 * packets with interfaces). This is probably the right way
45 * to "Pass the interface names and the traffic direction to either
46 * the frame-structure, a pseudo-header or use PPI." See the
49 * http://www.wireshark.org/lists/wireshark-dev/200708/msg00029.html
51 * to see whether any further discussion is still needed. I suspect
52 * it doesn't; pcap-NG existed at the time, as per the final
53 * message in that thread:
55 * http://www.wireshark.org/lists/wireshark-dev/200708/msg00039.html
57 * but I don't think we fully *supported* it at that point. Now
58 * that we do, we have the infrastructure to support this, except
59 * that we currently have no way to translate interface IDs to
60 * interface names in the "frame" dissector or to supply interface
61 * information as part of the packet metadata from Wiretap modules.
62 * That should be fixed so that we can show interface information,
63 * such as the interface name, in packet dissections from, for example,
67 static gboolean
info_line(const gchar
*line
);
68 static gint64
netscreen_seek_next_packet(wtap
*wth
, int *err
, gchar
**err_info
,
70 static gboolean
netscreen_check_file_type(wtap
*wth
, int *err
,
72 static gboolean
netscreen_read(wtap
*wth
, int *err
, gchar
**err_info
,
74 static gboolean
netscreen_seek_read(wtap
*wth
, gint64 seek_off
,
75 struct wtap_pkthdr
*phdr
, Buffer
*buf
,
76 int len
, int *err
, gchar
**err_info
);
77 static int parse_netscreen_rec_hdr(struct wtap_pkthdr
*phdr
, const char *line
,
78 char *cap_int
, gboolean
*cap_dir
, char *cap_dst
,
79 int *err
, gchar
**err_info
);
80 static gboolean
parse_netscreen_hex_dump(FILE_T fh
, int pkt_len
,
81 const char *cap_int
, const char *cap_dst
, struct wtap_pkthdr
*phdr
,
82 Buffer
* buf
, int *err
, gchar
**err_info
);
83 static int parse_single_hex_dump_line(char* rec
, guint8
*buf
,
86 /* Returns TRUE if the line appears to be a line with protocol info.
87 Otherwise it returns FALSE. */
88 static gboolean
info_line(const gchar
*line
)
90 int i
=NETSCREEN_SPACES_ON_INFO_LINE
;
93 if (isspace((guchar
)*line
)) {
103 /* Seeks to the beginning of the next packet, and returns the
104 byte offset. Copy the header line to hdr. Returns -1 on failure,
105 and sets "*err" to the error and sets "*err_info" to null or an
106 additional error string. */
107 static gint64
netscreen_seek_next_packet(wtap
*wth
, int *err
, gchar
**err_info
,
111 char buf
[NETSCREEN_LINE_LENGTH
];
114 cur_off
= file_tell(wth
->fh
);
117 *err
= file_error(wth
->fh
, err_info
);
120 if (file_gets(buf
, sizeof(buf
), wth
->fh
) == NULL
) {
122 *err
= file_error(wth
->fh
, err_info
);
125 if (strstr(buf
, NETSCREEN_REC_MAGIC_STR1
) ||
126 strstr(buf
, NETSCREEN_REC_MAGIC_STR2
)) {
127 g_strlcpy(hdr
, buf
, NETSCREEN_LINE_LENGTH
);
134 /* Look through the first part of a file to see if this is
135 * NetScreen snoop output.
137 * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
138 * if we get an I/O error, "*err" will be set to a non-zero value and
139 * "*err_info" is set to null or an additional error string.
141 static gboolean
netscreen_check_file_type(wtap
*wth
, int *err
, gchar
**err_info
)
143 char buf
[NETSCREEN_LINE_LENGTH
];
146 buf
[NETSCREEN_LINE_LENGTH
-1] = '\0';
148 for (line
= 0; line
< NETSCREEN_HEADER_LINES_TO_CHECK
; line
++) {
149 if (file_gets(buf
, NETSCREEN_LINE_LENGTH
, wth
->fh
) == NULL
) {
151 *err
= file_error(wth
->fh
, err_info
);
155 reclen
= (guint
) strlen(buf
);
156 if (reclen
< strlen(NETSCREEN_HDR_MAGIC_STR1
) ||
157 reclen
< strlen(NETSCREEN_HDR_MAGIC_STR2
)) {
161 if (strstr(buf
, NETSCREEN_HDR_MAGIC_STR1
) ||
162 strstr(buf
, NETSCREEN_HDR_MAGIC_STR2
)) {
171 int netscreen_open(wtap
*wth
, int *err
, gchar
**err_info
)
174 /* Look for a NetScreen snoop header line */
175 if (!netscreen_check_file_type(wth
, err
, err_info
)) {
176 if (*err
!= 0 && *err
!= WTAP_ERR_SHORT_READ
)
181 if (file_seek(wth
->fh
, 0L, SEEK_SET
, err
) == -1) /* rewind */
184 wth
->file_encap
= WTAP_ENCAP_UNKNOWN
;
185 wth
->file_type_subtype
= WTAP_FILE_TYPE_SUBTYPE_NETSCREEN
;
186 wth
->snapshot_length
= 0; /* not known */
187 wth
->subtype_read
= netscreen_read
;
188 wth
->subtype_seek_read
= netscreen_seek_read
;
189 wth
->tsprecision
= WTAP_FILE_TSPREC_DSEC
;
194 /* Find the next packet and parse it; called from wtap_read(). */
195 static gboolean
netscreen_read(wtap
*wth
, int *err
, gchar
**err_info
,
200 char line
[NETSCREEN_LINE_LENGTH
];
201 char cap_int
[NETSCREEN_MAX_INT_NAME_LENGTH
];
205 /* Find the next packet */
206 offset
= netscreen_seek_next_packet(wth
, err
, err_info
, line
);
210 /* Parse the header */
211 pkt_len
= parse_netscreen_rec_hdr(&wth
->phdr
, line
, cap_int
, &cap_dir
, cap_dst
,
216 /* Convert the ASCII hex dump to binary data, and fill in some
217 struct wtap_pkthdr fields */
218 if (!parse_netscreen_hex_dump(wth
->fh
, pkt_len
, cap_int
,
219 cap_dst
, &wth
->phdr
, wth
->frame_buffer
, err
, err_info
))
223 * If the per-file encapsulation isn't known, set it to this
224 * packet's encapsulation.
226 * If it *is* known, and it isn't this packet's encapsulation,
227 * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
228 * have a single encapsulation for all packets in the file.
230 if (wth
->file_encap
== WTAP_ENCAP_UNKNOWN
)
231 wth
->file_encap
= wth
->phdr
.pkt_encap
;
233 if (wth
->file_encap
!= wth
->phdr
.pkt_encap
)
234 wth
->file_encap
= WTAP_ENCAP_PER_PACKET
;
237 *data_offset
= offset
;
241 /* Used to read packets in random-access fashion */
243 netscreen_seek_read(wtap
*wth
, gint64 seek_off
,
244 struct wtap_pkthdr
*phdr
, Buffer
*buf
, int len
,
245 int *err
, gchar
**err_info
)
247 char line
[NETSCREEN_LINE_LENGTH
];
248 char cap_int
[NETSCREEN_MAX_INT_NAME_LENGTH
];
252 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1) {
256 if (file_gets(line
, NETSCREEN_LINE_LENGTH
, wth
->random_fh
) == NULL
) {
257 *err
= file_error(wth
->random_fh
, err_info
);
259 *err
= WTAP_ERR_SHORT_READ
;
264 if (parse_netscreen_rec_hdr(phdr
, line
, cap_int
, &cap_dir
, cap_dst
,
265 err
, err_info
) == -1) {
269 if (!parse_netscreen_hex_dump(wth
->random_fh
, len
, cap_int
, cap_dst
,
270 phdr
, buf
, err
, err_info
))
275 /* Parses a packet record header. There are a few possible formats:
277 * XXX list extra formats here!
278 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
279 192.168.1.1 -> 192.168.1.10/6
280 vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
281 tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
282 00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00 .`.h.Y.....2..E.
283 00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8 .T.k..@.c.......
284 01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18 ..........[..(P.
285 1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09 ..y!...v.d......
286 31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61 1..(.X....=e...a
287 2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9 ,!...`..5.......
293 parse_netscreen_rec_hdr(struct wtap_pkthdr
*phdr
, const char *line
, char *cap_int
,
294 gboolean
*cap_dir
, char *cap_dst
, int *err
, gchar
**err_info
)
301 phdr
->presence_flags
= WTAP_HAS_TS
|WTAP_HAS_CAP_LEN
;
303 if (sscanf(line
, "%9d.%9d: %15[a-z0-9/:.-](%1[io]) len=%9d:%12s->%12s/",
304 &sec
, &dsec
, cap_int
, direction
, &pkt_len
, cap_src
, cap_dst
) < 5) {
305 *err
= WTAP_ERR_BAD_FILE
;
306 *err_info
= g_strdup("netscreen: Can't parse packet-header");
310 *cap_dir
= (direction
[0] == 'o' ? NETSCREEN_EGRESS
: NETSCREEN_INGRESS
);
313 phdr
->ts
.nsecs
= dsec
* 100000000;
319 /* Converts ASCII hex dump to binary data, and fills in some struct
320 wtap_pkthdr fields. Returns TRUE on success and FALSE on any error. */
322 parse_netscreen_hex_dump(FILE_T fh
, int pkt_len
, const char *cap_int
,
323 const char *cap_dst
, struct wtap_pkthdr
*phdr
, Buffer
* buf
,
324 int *err
, gchar
**err_info
)
327 gchar line
[NETSCREEN_LINE_LENGTH
];
329 int n
, i
= 0, offset
= 0;
332 /* Make sure we have enough room for the packet */
333 buffer_assure_space(buf
, NETSCREEN_MAX_PACKET_LEN
);
334 pd
= 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]; isspace((guchar
)*p
); p
++)
353 /* packets are delimited with empty lines */
358 n
= parse_single_hex_dump_line(p
, pd
, offset
);
360 /* the smallest packet has a length of 6 bytes, if
361 * the first hex-data is less then check whether
362 * it is a info-line and act accordingly
364 if (offset
== 0 && n
< 6) {
365 if (info_line(line
)) {
366 if (++i
<= NETSCREEN_MAX_INFOLINES
) {
370 *err
= WTAP_ERR_BAD_FILE
;
371 *err_info
= g_strdup("netscreen: cannot parse hex-data");
376 /* If there is no more data and the line was not empty,
377 * then there must be an error in the file
380 *err
= WTAP_ERR_BAD_FILE
;
381 *err_info
= g_strdup("netscreen: cannot parse hex-data");
385 /* Adjust the offset to the data that was just added to the buffer */
388 /* If there was more hex-data than was announced in the len=x
389 * header, then then there must be an error in the file
391 if(offset
> pkt_len
) {
392 *err
= WTAP_ERR_BAD_FILE
;
393 *err_info
= g_strdup("netscreen: too much hex-data");
399 * Determine the encapsulation type, based on the
400 * first 4 characters of the interface name
402 * XXX convert this to a 'case' structure when adding more
403 * (non-ethernet) interfacetypes
405 if (strncmp(cap_int
, "adsl", 4) == 0) {
406 /* The ADSL interface can be bridged with or without
407 * PPP encapsulation. Check whether the first six bytes
408 * of the hex data are the same as the destination mac
409 * address in the header. If they are, assume ethernet
410 * LinkLayer or else PPP
412 g_snprintf(dststr
, 13, "%02x%02x%02x%02x%02x%02x",
413 pd
[0], pd
[1], pd
[2], pd
[3], pd
[4], pd
[5]);
414 if (strncmp(dststr
, cap_dst
, 12) == 0)
415 phdr
->pkt_encap
= WTAP_ENCAP_ETHERNET
;
417 phdr
->pkt_encap
= WTAP_ENCAP_PPP
;
419 else if (strncmp(cap_int
, "seri", 4) == 0)
420 phdr
->pkt_encap
= WTAP_ENCAP_PPP
;
422 phdr
->pkt_encap
= WTAP_ENCAP_ETHERNET
;
424 phdr
->caplen
= offset
;
429 /* Take a string representing one line from a hex dump, with leading white
430 * space removed, and converts the text to binary data. We place the bytes
431 * in the buffer at the specified offset.
433 * Returns number of bytes successfully read, -1 if bad. */
435 parse_single_hex_dump_line(char* rec
, guint8
*buf
, guint byte_offset
)
437 int num_items_scanned
;
442 for (num_items_scanned
= 0; num_items_scanned
< 16; num_items_scanned
++) {
444 if (character
>= '0' && character
<= '9')
445 byte
= character
- '0' + 0;
446 else if (character
>= 'A' && character
<= 'F')
447 byte
= character
- 'A' + 0xA;
448 else if (character
>= 'a' && character
<= 'f')
449 byte
= character
- 'a' + 0xa;
450 else if (character
== ' ' || character
== '\r' || character
== '\n' || character
== '\0') {
451 /* Nothing more to parse */
454 return -1; /* not a hex digit, space before ASCII dump, or EOL */
456 character
= *rec
++ & 0xFF;
457 if (character
>= '0' && character
<= '9')
458 byte
+= character
- '0' + 0;
459 else if (character
>= 'A' && character
<= 'F')
460 byte
+= character
- 'A' + 0xA;
461 else if (character
>= 'a' && character
<= 'f')
462 byte
+= character
- 'a' + 0xa;
464 return -1; /* not a hex digit */
465 buf
[byte_offset
+ num_items_scanned
] = byte
;
466 character
= *rec
++ & 0xFF;
467 if (character
== '\0' || character
== '\r' || character
== '\n') {
468 /* Nothing more to parse */
470 } else if (character
!= ' ') {
471 /* not space before ASCII dump */
475 if (num_items_scanned
== 0)
478 return num_items_scanned
;