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 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "netscreen.h"
28 #include "file_wrappers.h"
35 * o Construct a list of interfaces, with interface names, give
36 * them link-layer types based on the interface name and packet
37 * data, and supply interface IDs with each packet (i.e., make
38 * this supply a pcap-ng-style set of interfaces and associate
39 * packets with interfaces). This is probably the right way
40 * to "Pass the interface names and the traffic direction to either
41 * the frame-structure, a pseudo-header or use PPI." See the
44 * http://www.wireshark.org/lists/wireshark-dev/200708/msg00029.html
46 * to see whether any further discussion is still needed. I suspect
47 * it doesn't; pcap-NG existed at the time, as per the final
48 * message in that thread:
50 * http://www.wireshark.org/lists/wireshark-dev/200708/msg00039.html
52 * but I don't think we fully *supported* it at that point. Now
53 * that we do, we have the infrastructure to support this, except
54 * that we currently have no way to translate interface IDs to
55 * interface names in the "frame" dissector or to supply interface
56 * information as part of the packet metadata from Wiretap modules.
57 * That should be fixed so that we can show interface information,
58 * such as the interface name, in packet dissections from, for example,
62 static gboolean info_line(const gchar *line);
63 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
65 static gboolean netscreen_check_file_type(wtap *wth, int *err,
67 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
69 static gboolean netscreen_seek_read(wtap *wth, gint64 seek_off,
70 struct wtap_pkthdr *phdr, Buffer *buf,
71 int *err, gchar **err_info);
72 static int parse_netscreen_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
73 char *cap_int, gboolean *cap_dir, char *cap_dst,
74 int *err, gchar **err_info);
75 static gboolean parse_netscreen_hex_dump(FILE_T fh, int pkt_len,
76 const char *cap_int, const char *cap_dst, struct wtap_pkthdr *phdr,
77 Buffer* buf, int *err, gchar **err_info);
78 static int parse_single_hex_dump_line(char* rec, guint8 *buf,
81 /* Returns TRUE if the line appears to be a line with protocol info.
82 Otherwise it returns FALSE. */
83 static gboolean info_line(const gchar *line)
85 int i=NETSCREEN_SPACES_ON_INFO_LINE;
88 if (g_ascii_isspace(*line)) {
98 /* Seeks to the beginning of the next packet, and returns the
99 byte offset. Copy the header line to hdr. Returns -1 on failure,
100 and sets "*err" to the error and sets "*err_info" to null or an
101 additional error string. */
102 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
106 char buf[NETSCREEN_LINE_LENGTH];
109 cur_off = file_tell(wth->fh);
112 *err = file_error(wth->fh, err_info);
115 if (file_gets(buf, sizeof(buf), wth->fh) == NULL) {
117 *err = file_error(wth->fh, err_info);
120 if (strstr(buf, NETSCREEN_REC_MAGIC_STR1) ||
121 strstr(buf, NETSCREEN_REC_MAGIC_STR2)) {
122 g_strlcpy(hdr, buf, NETSCREEN_LINE_LENGTH);
129 /* Look through the first part of a file to see if this is
130 * NetScreen snoop output.
132 * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
133 * if we get an I/O error, "*err" will be set to a non-zero value and
134 * "*err_info" is set to null or an additional error string.
136 static gboolean netscreen_check_file_type(wtap *wth, int *err, gchar **err_info)
138 char buf[NETSCREEN_LINE_LENGTH];
141 buf[NETSCREEN_LINE_LENGTH-1] = '\0';
143 for (line = 0; line < NETSCREEN_HEADER_LINES_TO_CHECK; line++) {
144 if (file_gets(buf, NETSCREEN_LINE_LENGTH, wth->fh) == NULL) {
146 *err = file_error(wth->fh, err_info);
150 reclen = (guint) strlen(buf);
151 if (reclen < strlen(NETSCREEN_HDR_MAGIC_STR1) ||
152 reclen < strlen(NETSCREEN_HDR_MAGIC_STR2)) {
156 if (strstr(buf, NETSCREEN_HDR_MAGIC_STR1) ||
157 strstr(buf, NETSCREEN_HDR_MAGIC_STR2)) {
166 wtap_open_return_val netscreen_open(wtap *wth, int *err, gchar **err_info)
169 /* Look for a NetScreen snoop header line */
170 if (!netscreen_check_file_type(wth, err, err_info)) {
171 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
172 return WTAP_OPEN_ERROR;
173 return WTAP_OPEN_NOT_MINE;
176 if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) /* rewind */
177 return WTAP_OPEN_ERROR;
179 wth->file_encap = WTAP_ENCAP_UNKNOWN;
180 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_NETSCREEN;
181 wth->snapshot_length = 0; /* not known */
182 wth->subtype_read = netscreen_read;
183 wth->subtype_seek_read = netscreen_seek_read;
184 wth->file_tsprec = WTAP_TSPREC_DSEC;
186 return WTAP_OPEN_MINE;
189 /* Find the next packet and parse it; called from wtap_read(). */
190 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
195 char line[NETSCREEN_LINE_LENGTH];
196 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
200 /* Find the next packet */
201 offset = netscreen_seek_next_packet(wth, err, err_info, line);
205 /* Parse the header */
206 pkt_len = parse_netscreen_rec_hdr(&wth->phdr, line, cap_int, &cap_dir,
207 cap_dst, err, err_info);
211 /* Convert the ASCII hex dump to binary data, and fill in some
212 struct wtap_pkthdr fields */
213 if (!parse_netscreen_hex_dump(wth->fh, pkt_len, cap_int,
214 cap_dst, &wth->phdr, wth->frame_buffer, err, err_info))
218 * If the per-file encapsulation isn't known, set it to this
219 * packet's encapsulation.
221 * If it *is* known, and it isn't this packet's encapsulation,
222 * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
223 * have a single encapsulation for all packets in the file.
225 if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
226 wth->file_encap = wth->phdr.pkt_encap;
228 if (wth->file_encap != wth->phdr.pkt_encap)
229 wth->file_encap = WTAP_ENCAP_PER_PACKET;
232 *data_offset = offset;
236 /* Used to read packets in random-access fashion */
238 netscreen_seek_read(wtap *wth, gint64 seek_off,
239 struct wtap_pkthdr *phdr, Buffer *buf,
240 int *err, gchar **err_info)
243 char line[NETSCREEN_LINE_LENGTH];
244 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
248 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
252 if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) {
253 *err = file_error(wth->random_fh, err_info);
255 *err = WTAP_ERR_SHORT_READ;
260 pkt_len = parse_netscreen_rec_hdr(phdr, line, cap_int, &cap_dir,
261 cap_dst, err, err_info);
265 if (!parse_netscreen_hex_dump(wth->random_fh, pkt_len, cap_int,
266 cap_dst, phdr, buf, err, err_info))
271 /* Parses a packet record header. There are a few possible formats:
273 * XXX list extra formats here!
274 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
275 192.168.1.1 -> 192.168.1.10/6
276 vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
277 tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
278 00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00 .`.h.Y.....2..E.
279 00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8 .T.k..@.c.......
280 01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18 ..........[..(P.
281 1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09 ..y!...v.d......
282 31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61 1..(.X....=e...a
283 2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9 ,!...`..5.......
289 parse_netscreen_rec_hdr(struct wtap_pkthdr *phdr, const char *line, char *cap_int,
290 gboolean *cap_dir, char *cap_dst, int *err, gchar **err_info)
297 phdr->rec_type = REC_TYPE_PACKET;
298 phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
300 if (sscanf(line, "%9d.%9d: %15[a-z0-9/:.-](%1[io]) len=%9d:%12s->%12s/",
301 &sec, &dsec, cap_int, direction, &pkt_len, cap_src, cap_dst) < 5) {
302 *err = WTAP_ERR_BAD_FILE;
303 *err_info = g_strdup("netscreen: Can't parse packet-header");
307 *cap_dir = (direction[0] == 'o' ? NETSCREEN_EGRESS : NETSCREEN_INGRESS);
310 phdr->ts.nsecs = dsec * 100000000;
316 /* Converts ASCII hex dump to binary data, and fills in some struct
317 wtap_pkthdr fields. Returns TRUE on success and FALSE on any error. */
319 parse_netscreen_hex_dump(FILE_T fh, int pkt_len, const char *cap_int,
320 const char *cap_dst, struct wtap_pkthdr *phdr, Buffer* buf,
321 int *err, gchar **err_info)
324 gchar line[NETSCREEN_LINE_LENGTH];
326 int n, i = 0, offset = 0;
329 /* Make sure we have enough room for the packet */
330 ws_buffer_assure_space(buf, NETSCREEN_MAX_PACKET_LEN);
331 pd = ws_buffer_start_ptr(buf);
335 /* The last packet is not delimited by an empty line, but by EOF
336 * So accept EOF as a valid delimiter too
338 if (file_gets(line, NETSCREEN_LINE_LENGTH, fh) == NULL) {
344 * The number of blanks is not fixed - for wireless
345 * interfaces, there may be 14 extra spaces before
348 for (p = &line[0]; g_ascii_isspace(*p); p++)
350 /* packets are delimited with empty lines */
355 n = parse_single_hex_dump_line(p, pd, offset);
357 /* the smallest packet has a length of 6 bytes, if
358 * the first hex-data is less then check whether
359 * it is a info-line and act accordingly
361 if (offset == 0 && n < 6) {
362 if (info_line(line)) {
363 if (++i <= NETSCREEN_MAX_INFOLINES) {
367 *err = WTAP_ERR_BAD_FILE;
368 *err_info = g_strdup("netscreen: cannot parse hex-data");
373 /* If there is no more data and the line was not empty,
374 * then there must be an error in the file
377 *err = WTAP_ERR_BAD_FILE;
378 *err_info = g_strdup("netscreen: cannot parse hex-data");
382 /* Adjust the offset to the data that was just added to the buffer */
385 /* If there was more hex-data than was announced in the len=x
386 * header, then then there must be an error in the file
388 if(offset > pkt_len) {
389 *err = WTAP_ERR_BAD_FILE;
390 *err_info = g_strdup("netscreen: too much hex-data");
396 * Determine the encapsulation type, based on the
397 * first 4 characters of the interface name
399 * XXX convert this to a 'case' structure when adding more
400 * (non-ethernet) interfacetypes
402 if (strncmp(cap_int, "adsl", 4) == 0) {
403 /* The ADSL interface can be bridged with or without
404 * PPP encapsulation. Check whether the first six bytes
405 * of the hex data are the same as the destination mac
406 * address in the header. If they are, assume ethernet
407 * LinkLayer or else PPP
409 g_snprintf(dststr, 13, "%02x%02x%02x%02x%02x%02x",
410 pd[0], pd[1], pd[2], pd[3], pd[4], pd[5]);
411 if (strncmp(dststr, cap_dst, 12) == 0)
412 phdr->pkt_encap = WTAP_ENCAP_ETHERNET;
414 phdr->pkt_encap = WTAP_ENCAP_PPP;
416 else if (strncmp(cap_int, "seri", 4) == 0)
417 phdr->pkt_encap = WTAP_ENCAP_PPP;
419 phdr->pkt_encap = WTAP_ENCAP_ETHERNET;
421 phdr->caplen = offset;
426 /* Take a string representing one line from a hex dump, with leading white
427 * space removed, and converts the text to binary data. We place the bytes
428 * in the buffer at the specified offset.
430 * Returns number of bytes successfully read, -1 if bad. */
432 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
434 int num_items_scanned;
439 for (num_items_scanned = 0; num_items_scanned < 16; num_items_scanned++) {
441 if (character >= '0' && character <= '9')
442 byte = character - '0' + 0;
443 else if (character >= 'A' && character <= 'F')
444 byte = character - 'A' + 0xA;
445 else if (character >= 'a' && character <= 'f')
446 byte = character - 'a' + 0xa;
447 else if (character == ' ' || character == '\r' || character == '\n' || character == '\0') {
448 /* Nothing more to parse */
451 return -1; /* not a hex digit, space before ASCII dump, or EOL */
453 character = *rec++ & 0xFF;
454 if (character >= '0' && character <= '9')
455 byte += character - '0' + 0;
456 else if (character >= 'A' && character <= 'F')
457 byte += character - 'A' + 0xA;
458 else if (character >= 'a' && character <= 'f')
459 byte += character - 'a' + 0xa;
461 return -1; /* not a hex digit */
462 buf[byte_offset + num_items_scanned] = byte;
463 character = *rec++ & 0xFF;
464 if (character == '\0' || character == '\r' || character == '\n') {
465 /* Nothing more to parse */
467 } else if (character != ' ') {
468 /* not space before ASCII dump */
472 if (num_items_scanned == 0)
475 return num_items_scanned;
479 * Editor modelines - http://www.wireshark.org/tools/modelines.html
484 * indent-tabs-mode: t
487 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
488 * :indentSize=8:tabSize=8:noTabs=false: