From babi: Fix three overflows (bugs 3150 and 3151).
[metze/wireshark/wip.git] / wiretap / netscreen.c
1 /* netscreen.c
2  *
3  * $Id$
4  *
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>
8  *
9  * Wiretap Library
10  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
11  *
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.
16  *
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.
21  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 #include "wtap-int.h"
31 #include "buffer.h"
32 #include "netscreen.h"
33 #include "file_wrappers.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 /* XXX TODO:
41  *
42  * o  Create a wiki-page with instruction on how to make tracefiles
43  *    on Juniper NetScreen devices. Also put a few examples up
44  *    on the wiki (Done: wiki-page added 2007-08-03)
45  *
46  * o  Use the interface names to properly detect the encapsulation
47  *    type (ie adsl packets are now not properly dissected)
48  *    (Done: adsl packets are now correctly seen as PPP, 2007-08-03)
49  *
50  * o  Pass the interface names and the traffic direction to either
51  *    the frame-structure, a pseudo-header or use PPI. This needs
52  *    to be discussed on the dev-list first
53  *    (Posted a message to wireshark-dev abou this 2007-08-03)
54  *
55  */
56
57
58
59 static gboolean empty_line(const gchar *line);
60 static gboolean info_line(const gchar *line);
61 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, char *hdr);
62 static gboolean netscreen_check_file_type(wtap *wth, int *err);
63 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
64         gint64 *data_offset);
65 static gboolean netscreen_seek_read(wtap *wth, gint64 seek_off,
66         union wtap_pseudo_header *pseudo_header, guint8 *pd,
67         int len, int *err, gchar **err_info);
68 static int parse_netscreen_rec_hdr(wtap *wth, const char *line,
69         char *cap_int, gboolean *cap_dir,
70         union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
71 static int parse_netscreen_hex_dump(FILE_T fh, int pkt_len, guint8* buf,
72         int *err, gchar **err_info);
73 static int parse_single_hex_dump_line(char* rec, guint8 *buf,
74         guint byte_offset);
75
76 /* Returns TRUE if the line appears to be an empty line. Otherwise it
77    returns FALSE. */
78 static gboolean empty_line(const gchar *line)
79 {
80         while (*line) {
81                 if (isspace((guchar)*line)) {
82                         line++;
83                         continue;
84                 } else {
85                         break;
86                 }
87         }
88         if (*line == '\0')
89                 return TRUE;
90         else
91                 return FALSE;
92 }
93
94 /* Returns TRUE if the line appears to be a line with protocol info.
95    Otherwise it returns FALSE. */
96 static gboolean info_line(const gchar *line)
97 {
98         int i=NETSCREEN_SPACES_ON_INFO_LINE;
99         
100         while (i-- > 0) {
101                 if (isspace((guchar)*line)) {
102                         line++;
103                         continue;
104                 } else {
105                         return FALSE;
106                 }
107         }
108         return TRUE;
109 }
110
111 /* Seeks to the beginning of the next packet, and returns the
112    byte offset. Copy the header line to hdr. Returns -1 on failure,
113    and sets "*err" to the error and set hdr as NULL. */
114 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, char *hdr)
115 {
116         gint64 cur_off;
117         char buf[NETSCREEN_LINE_LENGTH];
118
119         while (1) {
120                 cur_off = file_tell(wth->fh);
121                 if (cur_off == -1) {
122                         /* Error */
123                         *err = file_error(wth->fh);
124                         hdr = NULL;
125                         return -1;
126                 }
127                 if (file_gets(buf, sizeof(buf), wth->fh) != NULL) {
128                         if (strstr(buf, NETSCREEN_REC_MAGIC_STR1) ||
129                             strstr(buf, NETSCREEN_REC_MAGIC_STR2)) {
130                                 g_strlcpy(hdr, buf, NETSCREEN_LINE_LENGTH);
131                                 return cur_off;
132                         }
133                 } else {
134                         if (file_eof(wth->fh)) {
135                                 /* We got an EOF. */
136                                 *err = 0;
137                         } else {
138                                 /* We (presumably) got an error (there's no
139                                    equivalent to "ferror()" in zlib, alas,
140                                    so we don't have a wrapper to check for
141                                    an error). */
142                                 *err = file_error(wth->fh);
143                         }
144                         break;
145                 }
146         }
147         hdr = NULL;
148         return -1;
149 }
150
151 /* Look through the first part of a file to see if this is
152  * NetScreen snoop output.
153  *
154  * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
155  * if we get an I/O error, "*err" will be set to a non-zero value.
156  */
157 static gboolean netscreen_check_file_type(wtap *wth, int *err)
158 {
159         char    buf[NETSCREEN_LINE_LENGTH];
160         guint   reclen, line;
161
162         buf[NETSCREEN_LINE_LENGTH-1] = '\0';
163
164         for (line = 0; line < NETSCREEN_HEADER_LINES_TO_CHECK; line++) {
165                 if (file_gets(buf, NETSCREEN_LINE_LENGTH, wth->fh) != NULL) {
166
167                         reclen = strlen(buf);
168                         if (reclen < strlen(NETSCREEN_HDR_MAGIC_STR1) ||
169                                 reclen < strlen(NETSCREEN_HDR_MAGIC_STR2)) {
170                                 continue;
171                         }
172
173                         if (strstr(buf, NETSCREEN_HDR_MAGIC_STR1) ||
174                             strstr(buf, NETSCREEN_HDR_MAGIC_STR2)) {
175                                 return TRUE;
176                         }
177                 } else {
178                         /* EOF or error. */
179                         if (file_eof(wth->fh))
180                                 *err = 0;
181                         else
182                                 *err = file_error(wth->fh);
183                         return FALSE;
184                 }
185         }
186         *err = 0;
187         return FALSE;
188 }
189
190
191 int netscreen_open(wtap *wth, int *err, gchar **err_info _U_)
192 {
193
194         /* Look for a NetScreen snoop header line */
195         if (!netscreen_check_file_type(wth, err)) {
196                 if (*err == 0)
197                         return 0;
198                 else
199                         return -1;
200         }
201
202         if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1)        /* rewind */
203                 return -1;
204
205         wth->data_offset = 0;
206         wth->file_encap = WTAP_ENCAP_UNKNOWN;
207         wth->file_type = WTAP_FILE_NETSCREEN;
208         wth->snapshot_length = 0; /* not known */
209         wth->subtype_read = netscreen_read;
210         wth->subtype_seek_read = netscreen_seek_read;
211         wth->tsprecision = WTAP_FILE_TSPREC_DSEC;
212         
213         return 1;
214 }
215
216 /* Find the next packet and parse it; called from wtap_read(). */
217 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
218     gint64 *data_offset)
219 {
220         gint64          offset;
221         guint8          *buf;
222         int             pkt_len, caplen;
223         char            line[NETSCREEN_LINE_LENGTH];
224         char            cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
225         gboolean        cap_dir;
226
227         /* Find the next packet */
228         offset = netscreen_seek_next_packet(wth, err, line);
229         if (offset < 0)
230                 return FALSE;
231
232         /* Parse the header */
233         pkt_len = parse_netscreen_rec_hdr(wth, line, cap_int, &cap_dir, 
234                 &wth->pseudo_header, err, err_info);
235         if (pkt_len == -1)
236                 return FALSE;
237
238         /* Make sure we have enough room for the packet */
239         buffer_assure_space(wth->frame_buffer, NETSCREEN_MAX_PACKET_LEN);
240         buf = buffer_start_ptr(wth->frame_buffer);
241
242         /* Convert the ASCII hex dump to binary data */
243         if ((caplen = parse_netscreen_hex_dump(wth->fh, pkt_len, buf, err,
244             err_info)) == -1) {
245                 return FALSE;
246         }
247
248         /*
249          * Determine the encapsulation type, based on the
250          * first 4 characters of the interface name
251          *
252          * XXX  convert this to a 'case' structure when adding more
253          *      (non-ethernet) interfacetypes
254          */
255         if (strncmp(cap_int, "adsl", 4) == 0) 
256                 wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
257         else if (strncmp(cap_int, "seri", 4) == 0)
258                 wth->phdr.pkt_encap = WTAP_ENCAP_PPP;
259         else
260                 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
261
262         /*
263          * If the per-file encapsulation isn't known, set it to this
264          * packet's encapsulation.
265          *
266          * If it *is* known, and it isn't this packet's encapsulation,
267          * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
268          * have a single encapsulation for all packets in the file.
269          */
270         if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
271                 wth->file_encap = wth->phdr.pkt_encap;
272         else {
273                 if (wth->file_encap != wth->phdr.pkt_encap)
274                         wth->file_encap = WTAP_ENCAP_PER_PACKET;
275         }
276
277         wth->data_offset = offset;
278         wth->phdr.caplen = caplen;
279         *data_offset = offset;
280         return TRUE;
281 }
282
283 /* Used to read packets in random-access fashion */
284 static gboolean
285 netscreen_seek_read (wtap *wth, gint64 seek_off,
286         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
287         int *err, gchar **err_info)
288 {
289         char            line[NETSCREEN_LINE_LENGTH];
290         char            cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
291         gboolean        cap_dir;
292
293         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
294                 return FALSE;
295         }
296
297         if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) {
298                 *err = file_error(wth->random_fh);
299                 if (*err == 0) {
300                         *err = WTAP_ERR_SHORT_READ;
301                 }
302                 return FALSE;
303         }
304
305         if (parse_netscreen_rec_hdr(NULL, line, cap_int, &cap_dir, pseudo_header, 
306            err, err_info) == -1) {
307                 return FALSE;
308         }
309
310         return parse_netscreen_hex_dump(wth->random_fh, len, pd, err, err_info);
311 }
312
313 /* Parses a packet record header. There are a few possible formats:
314  * 
315  * XXX list extra formats here!
316 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
317               192.168.1.1 -> 192.168.1.10/6
318               vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
319               tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
320               00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00     .`.h.Y.....2..E.
321               00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8     .T.k..@.c.......
322               01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18     ..........[..(P.
323               1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09     ..y!...v.d......
324               31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61     1..(.X....=e...a
325               2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9     ,!...`..5.......
326               1d 0b                                               ..
327
328
329  */
330 static int
331 parse_netscreen_rec_hdr(wtap *wth, const char *line, char *cap_int, gboolean *cap_dir,
332     union wtap_pseudo_header *pseudo_header _U_, int *err, gchar **err_info)
333 {
334         int     sec;
335         int     dsec, pkt_len;
336         char    direction[2];
337
338         if (sscanf(line, "%d.%d: %15[a-z0-9/:.](%1[io]) len=%d:",
339                    &sec, &dsec, cap_int, direction, &pkt_len) != 5) {
340                 *err = WTAP_ERR_BAD_RECORD;
341                 *err_info = g_strdup("netscreen: Can't parse packet-header");
342                 return -1;
343         }
344
345         *cap_dir = (direction[0] == 'o' ? NETSCREEN_EGRESS : NETSCREEN_INGRESS);
346
347         if (wth) {
348                 wth->phdr.ts.secs  = sec;
349                 wth->phdr.ts.nsecs = dsec * 100000000;
350                 wth->phdr.len = pkt_len;
351         }
352
353         return pkt_len;
354 }
355
356 /* Converts ASCII hex dump to binary data. Returns the capture length.
357    If any error is encountered, -1 is returned. */
358 static int
359 parse_netscreen_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err, gchar **err_info)
360 {
361         gchar   line[NETSCREEN_LINE_LENGTH];
362         int     n, i = 0, offset = 0;
363
364         while(1) {
365
366                 /* The last packet is not delimited by an empty line, but by EOF
367                  * So accept EOF as a valid delimiter too
368                  */
369                 if (file_gets(line, NETSCREEN_LINE_LENGTH, fh) == NULL) {
370                         break;
371                 }
372
373                 /* packets are delimited with empty lines */
374                 if (empty_line(line)) {
375                         break;
376                 }
377                 
378                 /* terminate the line before the ascii-data to prevent the 
379                  * parser from parsing one or more extra bytes from the 
380                  * ascii-data.
381                  * Check for longer lines to prevent wireless hexdumps to
382                  * be cut in the middle (they can have 14 extra spaces
383                  * before the hex-data)
384                  */
385                 if(strlen(line) != 98) 
386                         line[62] = '\0';
387                 else
388                         line[76] = '\0';
389
390                 n = parse_single_hex_dump_line(line, buf, offset);
391
392                 /* the smallest packet has a length of 6 bytes, if
393                  * the first hex-data is less then check whether 
394                  * it is a info-line and act accordingly
395                  */
396                 if (offset == 0 && n < 6) {
397                         if (info_line(line)) {
398                                 if (++i <= NETSCREEN_MAX_INFOLINES) {
399                                         continue;
400                                 }
401                         } else {
402                                 *err = WTAP_ERR_BAD_RECORD;
403                                 *err_info = g_strdup("netscreen: cannot parse hex-data");
404                                 return -1;
405                         }
406                 }
407
408                 /* If there is no more data and the line was not empty,
409                  * then there must be an error in the file
410                  */
411                 if(n == -1) {
412                         *err = WTAP_ERR_BAD_RECORD;
413                         *err_info = g_strdup("netscreen: cannot parse hex-data");
414                         return -1;
415                 }
416
417                 /* Adjust the offset to the data that was just added to the buffer */
418                 offset += n;
419
420                 /* If there was more hex-data than was announced in the len=x 
421                  * header, then then there must be an error in the file
422                  */
423                 if(offset > pkt_len) {
424                         *err = WTAP_ERR_BAD_RECORD;
425                         *err_info = g_strdup("netscreen: to much hex-data");
426                         return -1;
427                 }
428         }
429         return offset;
430 }
431
432
433 /* Take a string representing one line from a hex dump and converts
434  * the text to binary data. We place the bytes in the buffer at the
435  * specified offset.
436  *
437  * Returns number of bytes successfully read, -1 if bad.  */
438 static int
439 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
440 {
441         int num_items_scanned, i;
442         unsigned int bytes[16];
443
444         num_items_scanned = sscanf(rec, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
445                                &bytes[0], &bytes[1], &bytes[2], &bytes[3],
446                                &bytes[4], &bytes[5], &bytes[6], &bytes[7],
447                                &bytes[8], &bytes[9], &bytes[10], &bytes[11],
448                                &bytes[12], &bytes[13], &bytes[14], &bytes[15]);
449         if (num_items_scanned == 0)
450                 return -1;
451
452         if (num_items_scanned > 16)
453                 num_items_scanned = 16;
454
455         for (i=0; i<num_items_scanned; i++) {
456                 buf[byte_offset + i] = (guint8)bytes[i];
457         }
458
459         return num_items_scanned;
460 }