add WERR_INVALID_STATE
[wireshark-wip.git] / wiretap / netscreen.c
blob947cc8a53314cdd340a6308eb19677e9d8127203
1 /* netscreen.c
3 * $Id$
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>
9 * Wiretap Library
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.
27 #include "config.h"
28 #include "wtap-int.h"
29 #include "buffer.h"
30 #include "netscreen.h"
31 #include "file_wrappers.h"
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
38 /* XXX TODO:
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
47 * message at
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,
64 * pcap-NG captures.
67 static gboolean info_line(const gchar *line);
68 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
69 char *hdr);
70 static gboolean netscreen_check_file_type(wtap *wth, int *err,
71 gchar **err_info);
72 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
73 gint64 *data_offset);
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,
84 guint byte_offset);
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;
92 while (i-- > 0) {
93 if (isspace((guchar)*line)) {
94 line++;
95 continue;
96 } else {
97 return FALSE;
100 return TRUE;
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,
108 char *hdr)
110 gint64 cur_off;
111 char buf[NETSCREEN_LINE_LENGTH];
113 while (1) {
114 cur_off = file_tell(wth->fh);
115 if (cur_off == -1) {
116 /* Error */
117 *err = file_error(wth->fh, err_info);
118 return -1;
120 if (file_gets(buf, sizeof(buf), wth->fh) == NULL) {
121 /* EOF or error. */
122 *err = file_error(wth->fh, err_info);
123 break;
125 if (strstr(buf, NETSCREEN_REC_MAGIC_STR1) ||
126 strstr(buf, NETSCREEN_REC_MAGIC_STR2)) {
127 g_strlcpy(hdr, buf, NETSCREEN_LINE_LENGTH);
128 return cur_off;
131 return -1;
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];
144 guint reclen, line;
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) {
150 /* EOF or error. */
151 *err = file_error(wth->fh, err_info);
152 return FALSE;
155 reclen = (guint) strlen(buf);
156 if (reclen < strlen(NETSCREEN_HDR_MAGIC_STR1) ||
157 reclen < strlen(NETSCREEN_HDR_MAGIC_STR2)) {
158 continue;
161 if (strstr(buf, NETSCREEN_HDR_MAGIC_STR1) ||
162 strstr(buf, NETSCREEN_HDR_MAGIC_STR2)) {
163 return TRUE;
166 *err = 0;
167 return FALSE;
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)
177 return -1;
178 return 0;
181 if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) /* rewind */
182 return -1;
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;
191 return 1;
194 /* Find the next packet and parse it; called from wtap_read(). */
195 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
196 gint64 *data_offset)
198 gint64 offset;
199 int pkt_len;
200 char line[NETSCREEN_LINE_LENGTH];
201 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
202 gboolean cap_dir;
203 char cap_dst[13];
205 /* Find the next packet */
206 offset = netscreen_seek_next_packet(wth, err, err_info, line);
207 if (offset < 0)
208 return FALSE;
210 /* Parse the header */
211 pkt_len = parse_netscreen_rec_hdr(&wth->phdr, line, cap_int, &cap_dir, cap_dst,
212 err, err_info);
213 if (pkt_len == -1)
214 return FALSE;
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))
220 return FALSE;
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;
232 else {
233 if (wth->file_encap != wth->phdr.pkt_encap)
234 wth->file_encap = WTAP_ENCAP_PER_PACKET;
237 *data_offset = offset;
238 return TRUE;
241 /* Used to read packets in random-access fashion */
242 static gboolean
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];
249 gboolean cap_dir;
250 char cap_dst[13];
252 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
253 return FALSE;
256 if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) {
257 *err = file_error(wth->random_fh, err_info);
258 if (*err == 0) {
259 *err = WTAP_ERR_SHORT_READ;
261 return FALSE;
264 if (parse_netscreen_rec_hdr(phdr, line, cap_int, &cap_dir, cap_dst,
265 err, err_info) == -1) {
266 return FALSE;
269 if (!parse_netscreen_hex_dump(wth->random_fh, len, cap_int, cap_dst,
270 phdr, buf, err, err_info))
271 return FALSE;
272 return TRUE;
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.......
288 1d 0b ..
292 static int
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)
296 int sec;
297 int dsec, pkt_len;
298 char direction[2];
299 char cap_src[13];
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");
307 return -1;
310 *cap_dir = (direction[0] == 'o' ? NETSCREEN_EGRESS : NETSCREEN_INGRESS);
312 phdr->ts.secs = sec;
313 phdr->ts.nsecs = dsec * 100000000;
314 phdr->len = pkt_len;
316 return pkt_len;
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. */
321 static gboolean
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)
326 guint8 *pd;
327 gchar line[NETSCREEN_LINE_LENGTH];
328 gchar *p;
329 int n, i = 0, offset = 0;
330 gchar dststr[13];
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);
336 while(1) {
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) {
342 break;
346 * Skip blanks.
347 * The number of blanks is not fixed - for wireless
348 * interfaces, there may be 14 extra spaces before
349 * the hex data.
351 for (p = &line[0]; isspace((guchar)*p); p++)
353 /* packets are delimited with empty lines */
354 if (*p == '\0') {
355 break;
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) {
367 continue;
369 } else {
370 *err = WTAP_ERR_BAD_FILE;
371 *err_info = g_strdup("netscreen: cannot parse hex-data");
372 return FALSE;
376 /* If there is no more data and the line was not empty,
377 * then there must be an error in the file
379 if(n == -1) {
380 *err = WTAP_ERR_BAD_FILE;
381 *err_info = g_strdup("netscreen: cannot parse hex-data");
382 return FALSE;
385 /* Adjust the offset to the data that was just added to the buffer */
386 offset += n;
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");
394 return FALSE;
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;
416 else
417 phdr->pkt_encap = WTAP_ENCAP_PPP;
419 else if (strncmp(cap_int, "seri", 4) == 0)
420 phdr->pkt_encap = WTAP_ENCAP_PPP;
421 else
422 phdr->pkt_encap = WTAP_ENCAP_ETHERNET;
424 phdr->caplen = offset;
426 return TRUE;
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. */
434 static int
435 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
437 int num_items_scanned;
438 guint8 character;
439 guint8 byte;
442 for (num_items_scanned = 0; num_items_scanned < 16; num_items_scanned++) {
443 character = *rec++;
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 */
452 break;
453 } else
454 return -1; /* not a hex digit, space before ASCII dump, or EOL */
455 byte <<= 4;
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;
463 else
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 */
469 break;
470 } else if (character != ' ') {
471 /* not space before ASCII dump */
472 return -1;
475 if (num_items_scanned == 0)
476 return -1;
478 return num_items_scanned;