opa: Add dissectors for Intel’s Omni-Path Architecture (OPA)
[metze/wireshark/wip.git] / wiretap / netscreen.c
1 /* netscreen.c
2  *
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>
6  *
7  * Wiretap Library
8  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
9  *
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.
14  *
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.
19  *
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.
23  */
24
25 #include "config.h"
26 #include "wtap-int.h"
27 #include "netscreen.h"
28 #include "file_wrappers.h"
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 /* XXX TODO:
34  *
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
42  *    message at
43  *
44  *        http://www.wireshark.org/lists/wireshark-dev/200708/msg00029.html
45  *
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:
49  *
50  *        http://www.wireshark.org/lists/wireshark-dev/200708/msg00039.html
51  *
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,
59  *    pcap-NG captures.
60  */
61
62 static gboolean info_line(const gchar *line);
63 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
64         char *hdr);
65 static gboolean netscreen_check_file_type(wtap *wth, int *err,
66         gchar **err_info);
67 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
68         gint64 *data_offset);
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 gboolean parse_netscreen_packet(FILE_T fh, struct wtap_pkthdr *phdr,
73         Buffer* buf, char *line, int *err, gchar **err_info);
74 static int parse_single_hex_dump_line(char* rec, guint8 *buf,
75         guint byte_offset);
76
77 /* Returns TRUE if the line appears to be a line with protocol info.
78    Otherwise it returns FALSE. */
79 static gboolean info_line(const gchar *line)
80 {
81         int i=NETSCREEN_SPACES_ON_INFO_LINE;
82
83         while (i-- > 0) {
84                 if (g_ascii_isspace(*line)) {
85                         line++;
86                         continue;
87                 } else {
88                         return FALSE;
89                 }
90         }
91         return TRUE;
92 }
93
94 /* Seeks to the beginning of the next packet, and returns the
95    byte offset. Copy the header line to hdr. Returns -1 on failure,
96    and sets "*err" to the error and sets "*err_info" to null or an
97    additional error string. */
98 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
99     char *hdr)
100 {
101         gint64 cur_off;
102         char buf[NETSCREEN_LINE_LENGTH];
103
104         while (1) {
105                 cur_off = file_tell(wth->fh);
106                 if (cur_off == -1) {
107                         /* Error */
108                         *err = file_error(wth->fh, err_info);
109                         return -1;
110                 }
111                 if (file_gets(buf, sizeof(buf), wth->fh) == NULL) {
112                         /* EOF or error. */
113                         *err = file_error(wth->fh, err_info);
114                         break;
115                 }
116                 if (strstr(buf, NETSCREEN_REC_MAGIC_STR1) ||
117                     strstr(buf, NETSCREEN_REC_MAGIC_STR2)) {
118                         g_strlcpy(hdr, buf, NETSCREEN_LINE_LENGTH);
119                         return cur_off;
120                 }
121         }
122         return -1;
123 }
124
125 /* Look through the first part of a file to see if this is
126  * NetScreen snoop output.
127  *
128  * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
129  * if we get an I/O error, "*err" will be set to a non-zero value and
130  * "*err_info" is set to null or an additional error string.
131  */
132 static gboolean netscreen_check_file_type(wtap *wth, int *err, gchar **err_info)
133 {
134         char    buf[NETSCREEN_LINE_LENGTH];
135         guint   reclen, line;
136
137         buf[NETSCREEN_LINE_LENGTH-1] = '\0';
138
139         for (line = 0; line < NETSCREEN_HEADER_LINES_TO_CHECK; line++) {
140                 if (file_gets(buf, NETSCREEN_LINE_LENGTH, wth->fh) == NULL) {
141                         /* EOF or error. */
142                         *err = file_error(wth->fh, err_info);
143                         return FALSE;
144                 }
145
146                 reclen = (guint) strlen(buf);
147                 if (reclen < strlen(NETSCREEN_HDR_MAGIC_STR1) ||
148                         reclen < strlen(NETSCREEN_HDR_MAGIC_STR2)) {
149                         continue;
150                 }
151
152                 if (strstr(buf, NETSCREEN_HDR_MAGIC_STR1) ||
153                     strstr(buf, NETSCREEN_HDR_MAGIC_STR2)) {
154                         return TRUE;
155                 }
156         }
157         *err = 0;
158         return FALSE;
159 }
160
161
162 wtap_open_return_val netscreen_open(wtap *wth, int *err, gchar **err_info)
163 {
164
165         /* Look for a NetScreen snoop header line */
166         if (!netscreen_check_file_type(wth, err, err_info)) {
167                 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
168                         return WTAP_OPEN_ERROR;
169                 return WTAP_OPEN_NOT_MINE;
170         }
171
172         if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1)        /* rewind */
173                 return WTAP_OPEN_ERROR;
174
175         wth->file_encap = WTAP_ENCAP_UNKNOWN;
176         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_NETSCREEN;
177         wth->snapshot_length = 0; /* not known */
178         wth->subtype_read = netscreen_read;
179         wth->subtype_seek_read = netscreen_seek_read;
180         wth->file_tsprec = WTAP_TSPREC_DSEC;
181
182         return WTAP_OPEN_MINE;
183 }
184
185 /* Find the next packet and parse it; called from wtap_read(). */
186 static gboolean netscreen_read(wtap *wth, int *err, gchar **err_info,
187     gint64 *data_offset)
188 {
189         gint64          offset;
190         char            line[NETSCREEN_LINE_LENGTH];
191
192         /* Find the next packet */
193         offset = netscreen_seek_next_packet(wth, err, err_info, line);
194         if (offset < 0)
195                 return FALSE;
196
197         /* Parse the header and convert the ASCII hex dump to binary data */
198         if (!parse_netscreen_packet(wth->fh, &wth->phdr,
199             wth->frame_buffer, line, err, err_info))
200                 return FALSE;
201
202         /*
203          * If the per-file encapsulation isn't known, set it to this
204          * packet's encapsulation.
205          *
206          * If it *is* known, and it isn't this packet's encapsulation,
207          * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
208          * have a single encapsulation for all packets in the file.
209          */
210         if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
211                 wth->file_encap = wth->phdr.pkt_encap;
212         else {
213                 if (wth->file_encap != wth->phdr.pkt_encap)
214                         wth->file_encap = WTAP_ENCAP_PER_PACKET;
215         }
216
217         *data_offset = offset;
218         return TRUE;
219 }
220
221 /* Used to read packets in random-access fashion */
222 static gboolean
223 netscreen_seek_read(wtap *wth, gint64 seek_off,
224         struct wtap_pkthdr *phdr, Buffer *buf,
225         int *err, gchar **err_info)
226 {
227         char            line[NETSCREEN_LINE_LENGTH];
228
229         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
230                 return FALSE;
231         }
232
233         if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) {
234                 *err = file_error(wth->random_fh, err_info);
235                 if (*err == 0) {
236                         *err = WTAP_ERR_SHORT_READ;
237                 }
238                 return FALSE;
239         }
240
241         return parse_netscreen_packet(wth->random_fh, phdr, buf, line,
242             err, err_info);
243 }
244
245 /* Parses a packet record header. There are a few possible formats:
246  *
247  * XXX list extra formats here!
248 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
249               192.168.1.1 -> 192.168.1.10/6
250               vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
251               tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
252               00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00     .`.h.Y.....2..E.
253               00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8     .T.k..@.c.......
254               01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18     ..........[..(P.
255               1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09     ..y!...v.d......
256               31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61     1..(.X....=e...a
257               2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9     ,!...`..5.......
258               1d 0b                                               ..
259
260
261  */
262 static gboolean
263 parse_netscreen_packet(FILE_T fh, struct wtap_pkthdr *phdr, Buffer* buf,
264     char *line, int *err, gchar **err_info)
265 {
266         int             pkt_len;
267         int             sec;
268         int             dsec;
269         char            cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
270         char            direction[2];
271         char            cap_src[13];
272         char            cap_dst[13];
273         guint8          *pd;
274         gchar           *p;
275         int             n, i = 0;
276         int             offset = 0;
277         gchar           dststr[13];
278
279         phdr->rec_type = REC_TYPE_PACKET;
280         phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
281
282         if (sscanf(line, "%9d.%9d: %15[a-z0-9/:.-](%1[io]) len=%9d:%12s->%12s/",
283                    &sec, &dsec, cap_int, direction, &pkt_len, cap_src, cap_dst) < 5) {
284                 *err = WTAP_ERR_BAD_FILE;
285                 *err_info = g_strdup("netscreen: Can't parse packet-header");
286                 return -1;
287         }
288         if (pkt_len < 0) {
289                 *err = WTAP_ERR_BAD_FILE;
290                 *err_info = g_strdup("netscreen: packet header has a negative packet length");
291                 return FALSE;
292         }
293         if (pkt_len > WTAP_MAX_PACKET_SIZE) {
294                 /*
295                  * Probably a corrupt capture file; don't blow up trying
296                  * to allocate space for an immensely-large packet.
297                  */
298                 *err = WTAP_ERR_BAD_FILE;
299                 *err_info = g_strdup_printf("netscreen: File has %u-byte packet, bigger than maximum of %u",
300                     pkt_len, WTAP_MAX_PACKET_SIZE);
301                 return FALSE;
302         }
303
304         /*
305          * If direction[0] is 'o', the direction is NETSCREEN_EGRESS,
306          * otherwise it's NETSCREEN_INGRESS.
307          */
308
309         phdr->ts.secs  = sec;
310         phdr->ts.nsecs = dsec * 100000000;
311         phdr->len = pkt_len;
312
313         /* Make sure we have enough room for the packet */
314         ws_buffer_assure_space(buf, pkt_len);
315         pd = ws_buffer_start_ptr(buf);
316
317         while(1) {
318
319                 /* The last packet is not delimited by an empty line, but by EOF
320                  * So accept EOF as a valid delimiter too
321                  */
322                 if (file_gets(line, NETSCREEN_LINE_LENGTH, fh) == NULL) {
323                         break;
324                 }
325
326                 /*
327                  * Skip blanks.
328                  * The number of blanks is not fixed - for wireless
329                  * interfaces, there may be 14 extra spaces before
330                  * the hex data.
331                  */
332                 for (p = &line[0]; g_ascii_isspace(*p); p++)
333                         ;
334                 /* packets are delimited with empty lines */
335                 if (*p == '\0') {
336                         break;
337                 }
338
339                 n = parse_single_hex_dump_line(p, pd, offset);
340
341                 /* the smallest packet has a length of 6 bytes, if
342                  * the first hex-data is less then check whether
343                  * it is a info-line and act accordingly
344                  */
345                 if (offset == 0 && n < 6) {
346                         if (info_line(line)) {
347                                 if (++i <= NETSCREEN_MAX_INFOLINES) {
348                                         continue;
349                                 }
350                         } else {
351                                 *err = WTAP_ERR_BAD_FILE;
352                                 *err_info = g_strdup("netscreen: cannot parse hex-data");
353                                 return FALSE;
354                         }
355                 }
356
357                 /* If there is no more data and the line was not empty,
358                  * then there must be an error in the file
359                  */
360                 if (n == -1) {
361                         *err = WTAP_ERR_BAD_FILE;
362                         *err_info = g_strdup("netscreen: cannot parse hex-data");
363                         return FALSE;
364                 }
365
366                 /* Adjust the offset to the data that was just added to the buffer */
367                 offset += n;
368
369                 /* If there was more hex-data than was announced in the len=x
370                  * header, then then there must be an error in the file
371                  */
372                 if (offset > pkt_len) {
373                         *err = WTAP_ERR_BAD_FILE;
374                         *err_info = g_strdup("netscreen: too much hex-data");
375                         return FALSE;
376                 }
377         }
378
379         /*
380          * Determine the encapsulation type, based on the
381          * first 4 characters of the interface name
382          *
383          * XXX  convert this to a 'case' structure when adding more
384          *      (non-ethernet) interfacetypes
385          */
386         if (strncmp(cap_int, "adsl", 4) == 0) {
387                 /* The ADSL interface can be bridged with or without
388                  * PPP encapsulation. Check whether the first six bytes
389                  * of the hex data are the same as the destination mac
390                  * address in the header. If they are, assume ethernet
391                  * LinkLayer or else PPP
392                  */
393                 g_snprintf(dststr, 13, "%02x%02x%02x%02x%02x%02x",
394                    pd[0], pd[1], pd[2], pd[3], pd[4], pd[5]);
395                 if (strncmp(dststr, cap_dst, 12) == 0)
396                         phdr->pkt_encap = WTAP_ENCAP_ETHERNET;
397                 else
398                         phdr->pkt_encap = WTAP_ENCAP_PPP;
399                 }
400         else if (strncmp(cap_int, "seri", 4) == 0)
401                 phdr->pkt_encap = WTAP_ENCAP_PPP;
402         else
403                 phdr->pkt_encap = WTAP_ENCAP_ETHERNET;
404
405         phdr->caplen = offset;
406
407         return TRUE;
408 }
409
410 /* Take a string representing one line from a hex dump, with leading white
411  * space removed, and converts the text to binary data. We place the bytes
412  * in the buffer at the specified offset.
413  *
414  * Returns number of bytes successfully read, -1 if bad.  */
415 static int
416 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
417 {
418         int num_items_scanned;
419         guint8 character;
420         guint8 byte;
421
422
423         for (num_items_scanned = 0; num_items_scanned < 16; num_items_scanned++) {
424                 character = *rec++;
425                 if (character >= '0' && character <= '9')
426                         byte = character - '0' + 0;
427                 else if (character >= 'A' && character <= 'F')
428                         byte = character - 'A' + 0xA;
429                 else if (character >= 'a' && character <= 'f')
430                         byte = character - 'a' + 0xa;
431                 else if (character == ' ' || character == '\r' || character == '\n' || character == '\0') {
432                         /* Nothing more to parse */
433                         break;
434                 } else
435                         return -1; /* not a hex digit, space before ASCII dump, or EOL */
436                 byte <<= 4;
437                 character = *rec++ & 0xFF;
438                 if (character >= '0' && character <= '9')
439                         byte += character - '0' + 0;
440                 else if (character >= 'A' && character <= 'F')
441                         byte += character - 'A' + 0xA;
442                 else if (character >= 'a' && character <= 'f')
443                         byte += character - 'a' + 0xa;
444                 else
445                         return -1; /* not a hex digit */
446                 buf[byte_offset + num_items_scanned] = byte;
447                 character = *rec++ & 0xFF;
448                 if (character == '\0' || character == '\r' || character == '\n') {
449                         /* Nothing more to parse */
450                         break;
451                 } else if (character != ' ') {
452                         /* not space before ASCII dump */
453                         return -1;
454                 }
455         }
456         if (num_items_scanned == 0)
457                 return -1;
458
459         return num_items_scanned;
460 }
461
462 /*
463  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
464  *
465  * Local variables:
466  * c-basic-offset: 8
467  * tab-width: 8
468  * indent-tabs-mode: t
469  * End:
470  *
471  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
472  * :indentSize=8:tabSize=8:noTabs=false:
473  */