1 /* iseries.c
2 *
3 * Wiretap Library
4 * Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com>
5 *
6 * Based on toshiba.c and vms.c
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 /*
12 * This module will read the contents of the iSeries (OS/400) Communication trace
13 * Both ASCII & Unicode (little-endian UCS-2) formatted traces are supported.
14 *
15 * iSeries Comms traces consist of a header page and a subsequent number of packet records
16 *
17 * The header page contains details on the options set during running of the trace,
18 * currently the following options are a requirement for this module:
19 *
20 * 1. Object protocol = ETHERNET (Default)
21 * 2. ASCII or Unicode file formats.
22 *
23 * The above can be achieved by passing option ASCII(*YES) with the trace command
24 *
25 */
26
27 /* iSeries header page
28
29 COMMUNICATIONS TRACE Title: OS400 - OS400 trace 10/28/05 11:44:50 Page: 1
30 Trace Description . . . . . : OS400 - OS400 trace
31 Configuration object . . . . : ETH0
32 Type . . . . . . . . . . . . : 1 1=Line, 2=Network Interface
33 3=Network server
34 Object protocol . . . . . . : ETHERNET
35 Start date/Time . . . . . . : 10/28/05 11:43:00.341
36 End date/Time . . . . . . . : 10/28/05 11:44:22.148
37 Bytes collected . . . . . . : 11999
38 Buffer size . . . . . . . . : 2048 kilobytes
39 Data direction . . . . . . . : 3 1=Sent, 2=Received, 3=Both
40 Stop on buffer full . . . . : Y Y=Yes, N=No
41 Number of bytes to trace
42 Beginning bytes . . . . . : *MAX Value, *CALC, *MAX
43 Ending bytes . . . . . . : *CALC Value, *CALC
44 Controller name . . . . . . : *ALL *ALL, name
45 Data representation . . . . : 1 1=ASCII, 2=EBCDIC, 3=*CALC
46 Format SNA data only . . . . : N Y=Yes, N=No
47 Format RR, RNR commands . . : N Y=Yes, N=No
48 Format TCP/IP data only . . : Y Y=Yes, N=No
49 IP address . . . . . . . . : *ALL *ALL, address
50 IP address . . . . . . . . : *ALL *ALL, address
51 IP port . . . . . . . . . : *ALL *ALL, IP port
52 Format UI data only . . . . : N Y=Yes, N=No
53 Select Ethernet data . . . . : 3 1=802.3, 2=ETHV2, 3=Both
54 Format Broadcast data . . . : Y Y=Yes, N=No
55 */
56
57 /* iSeries IPv4 formatted packet records consist of a packet header line
58 * identifying the packet number, direction, size, timestamp,
59 * source/destination MAC addresses and packet type.
60 *
61 * Thereafter there will be a formatted display of the headers above
62 * the link layer, such as ARP, IP, TCP, UDP, and ICMP (all but
63 * ICMP have either been seen in captures or on pages such as the ones
64 * at
65 *
66 * http://www-912.ibm.com/s_dir/SLKBase.nsf/1ac66549a21402188625680b0002037e/e05fb0515bc3449686256ce600512c37?OpenDocument
67 *
68 * and
69 *
70 * http://publib.boulder.ibm.com/infocenter/javasdk/v5r0/index.jsp?topic=%2Fcom.ibm.java.doc.diagnostics.50%2Fdiag%2Fproblem_determination%2Fi5os_perf_io_commstrace.html
71 *
72 * so we cannot assume that "IP Header" or "TCP Header" will appear). The
73 * formatted display includes lines that show the contents of some of the
74 * fields in the header, as well as hex strings dumps of the headers
75 * themselves, with tags such as "IP Header :", "ARP Header :",
76 * "TCP Header :", "UDP Header :", and (presumably) "ICMP Header:".
77 *
78 * If the packet contains data this is displayed as 4 groups of 16 hex digits
79 * followed by an ASCII representation of the data line.
80 *
81 * Information from the packet header line, higher-level headers and, if
82 * available, data lines are extracted by the module for displaying.
83 *
84 *
85 Record Data Record Controller Destination Source Frame
86 Number S/R Length Timer Name MAC Address MAC Address Format
87 ------ --- ------ --------------- ---------- ------------ ------------ ------
88 8 S 145 11:43:59.82956 0006299C14AE 0006299C14FE ETHV2 Type: 0800
89 Frame Type : IP DSCP: 0 ECN: 00-NECT Length: 145 Protocol: TCP Datagram ID: 388B
90 Src Addr: 10.20.144.150 Dest Addr: 10.20.144.151 Fragment Flags: DON'T,LAST
91 IP Header : 45000091388B40004006CC860A1490960A149097
92 IP Options : NONE
93 TCP . . . : Src Port: 6006,Unassigned Dest Port: 35366,Unassigned
94 SEQ Number: 2666470699 ('9EEF1D2B'X) ACK Number: 2142147535 ('7FAE93CF'X)
95 Code Bits: ACK PSH Window: 32648 TCP Option: NO OP
96 TCP Header : 17768A269EEF1D2B7FAE93CF80187F885B5600000101080A0517E0F805166DE0
97 Data . . . . . : 5443503200020010 0000004980000000 B800000080470103 01001E0000002000 *TCP2.......I*...*...*G........ .*
98 002F010080000004 0300800700C00600 4002008000000304 00800000060FB067 *./..*.....*..*..@..*.....*....*G*
99 FC276228786B3EB0 EF34F5F1D27EF8DF 20926820E7B322AA 739F1FB20D **'B(XK>**4***.** *H **"*S*.*. *
100 */
101
102 /* iSeries IPv6 formatted traces are similar to the IPv4 version above,
103 * except that the higher-level headers have "IPv6 Header:" and
104 * "ICMPv6 Hdr:", and data data is no longer output in groups of 16 hex
105 * digits.
106 *
107
108 Record Data Record Destination Source Frame
109 Number S/R Length Timer MAC Address MAC Address Format
110 ------ --- ------ ------------ ------------ ------------ ------
111 218 S 1488 15:01:14.389 0011BC358680 00096B6BD918 ETHV2 Type: 86DD
112 IPv6 Data: Ver: 06 Traffic Class: 00 Flow Label: 000000
113 Payload Length: 1448 Next Header: 06,TCP Hop Limit: 64
114 Src Addr: fd00:0:0:20f2::122
115 Dest Addr: fd00:0:0:20a0::155
116 IPv6 Header: 6000000005A80640FD000000000020F20000000000000122FD000000000020A0
117 0000000000000155
118 TCP . . . : Src Port: 21246,Unassigned Dest Port: 13601,Unassigned
119 SEQ Number: 2282300877 ('880925CD'X) ACK Number: 3259003715 ('C2407343'X)
120 Code Bits: ACK Window: 65535 TCP Option: NO OP
121 TCP Header : 52FE3521880925CDC24073438010FFFFCFBB00000101080A0E15127000237A08
122 Data . . . . . : 54435032000200140000061880000000ECBEB867F0000000004CE640E6C1D9D5 *TCP2........*...***g*....L*@*****
123 C9D5C740E3C8C9E240C9E240E3C8C540E6C1D9D5C9D5C740C6C9C5D3C4404040 ****@****@**@***@*******@*****@@@*
124 4040404040404040404040404040404040404040404040404040404040404040 *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
125 */
126
127 /* iSeries unformatted packet record consist of the same header record as
128 * the formatted trace but all other records are simply unformatted data
129 * containing higher-level headers and packet data combined.
130 *
131 Record Data Record Controller Destination Source Frame Number Number Poll/
132 Number S/R Length Timer Name MAC Address MAC Address Format Command Sent Received Final DSAP SSAP
133 ------ --- ------ --------------- ---------- ------------ ------------ ------ ------- ------ -------- ----- ---- ----
134 1 R 64 12:19:29.97108 000629ECF48E 0006D78E23C2 ETHV2 Type: 0800
135 Data . . . . . : 4500003C27954000 3A06CE3D9797440F 0A5964EAC4F50554 58C9915500000000 *E..<'*@.:.*=**D..YD***.TX**U....*
136 A00216D06A200000 020405B40402080A 1104B6C000000000 010303000B443BF1 **..*J .....*......**.........D;**
137 */
138
139 #include "config.h"
140 #include "wtap-int.h"
141 #include "iseries.h"
142 #include "file_wrappers.h"
143
144 #include <stdlib.h>
145 #include <string.h>
146 #include <errno.h>
147
148 #include <wsutil/str_util.h>
149 #include <wsutil/strtoi.h>
150 #include <wsutil/ws_assert.h>
151
152 #define ISERIES_LINE_LENGTH 270
153 #define ISERIES_HDR_LINES_TO_CHECK 100
154 #define ISERIES_PKT_LINES_TO_CHECK 4
155 #define ISERIES_MAX_TRACE_LEN 99999999
156 #define ISERIES_FORMAT_ASCII 1
157 #define ISERIES_FORMAT_UNICODE 2
158
159 /*
160 * Magic strings - "COMMUNICATIONS TRACE", in ASCII and little-endian UCS-2.
161 */
162 static const char iseries_hdr_magic_ascii[] = {
163 'C', 'O', 'M', 'M',
164 'U', 'N', 'I', 'C',
165 'A', 'T', 'I', 'O',
166 'N', 'S', ' ', 'T',
167 'R', 'A', 'C', 'E'
168 };
169 static const char iseries_hdr_magic_le_ucs_2[] = {
170 'C', 0x0, 'O', 0x0, 'M', 0x0, 'M', 0x0,
171 'U', 0x0, 'N', 0x0, 'I', 0x0, 'C', 0x0,
172 'A', 0x0, 'T', 0x0, 'I', 0x0, 'O', 0x0,
173 'N', 0x0, 'S', 0x0, ' ', 0x0, 'T', 0x0,
174 'R', 0x0, 'A', 0x0, 'C', 0x0, 'E', 0x0
175 };
176
177 typedef struct {
178 gboolean have_date; /* TRUE if we found a capture start date */
179 int year, month, day; /* The start date */
180 int format; /* Trace format type */
181 } iseries_t;
182
183 static gboolean iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf,
184 int *err, gchar ** err_info, gint64 *data_offset);
185 static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
186 wtap_rec *rec,
187 Buffer * buf, int *err, gchar ** err_info);
188 static gboolean iseries_check_file_type (wtap * wth, int *err, gchar **err_info,
189 int format);
190 static gint64 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info);
191 static gboolean iseries_parse_packet (wtap * wth, FILE_T fh,
192 wtap_rec *rec,
193 Buffer * buf, int *err, gchar ** err_info);
194 static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
195 static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
196 size_t len);
197
198 static int iseries_file_type_subtype = -1;
199 static int iseries_unicode_file_type_subtype = -1;
200
201 void register_iseries(void);
202
203 /*
204 * XXX - it would probably be cleaner to use a UCS-2 flavor of file_gets(),
205 * rather than file_gets(), if we're reading a UCS-2 file.
206 */
207 wtap_open_return_val
iseries_open(wtap * wth,int * err,gchar ** err_info)208 iseries_open (wtap * wth, int *err, gchar ** err_info)
209 {
210 gint offset;
211 char magic[ISERIES_LINE_LENGTH];
212
213 /*
214 * Check that file starts with a valid iSeries COMMS TRACE header
215 * by scanning for it in the first line
216 */
217 if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info))
218 {
219 if (*err != WTAP_ERR_SHORT_READ)
220 return WTAP_OPEN_ERROR;
221 return WTAP_OPEN_NOT_MINE;
222 }
223
224 /*
225 * Check if this is a little-endian UCS-2 Unicode formatted file by scanning
226 * for the magic string
227 */
228 offset=0;
229 while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2)))
230 {
231 if (memcmp (magic + offset, iseries_hdr_magic_le_ucs_2, sizeof iseries_hdr_magic_le_ucs_2) == 0) {
232 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
233 {
234 return WTAP_OPEN_ERROR;
235 }
236 /*
237 * Do some basic sanity checking to ensure we can handle the
238 * contents of this trace
239 */
240 if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
241 {
242 if (*err == 0)
243 return WTAP_OPEN_NOT_MINE;
244 else
245 return WTAP_OPEN_ERROR;
246 }
247
248 wth->file_encap = WTAP_ENCAP_ETHERNET;
249 wth->file_type_subtype = iseries_unicode_file_type_subtype;
250 wth->snapshot_length = 0;
251 wth->subtype_read = iseries_read;
252 wth->subtype_seek_read = iseries_seek_read;
253 wth->file_tsprec = WTAP_TSPREC_USEC;
254
255 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
256 {
257 return WTAP_OPEN_ERROR;
258 }
259
260 /*
261 * Add an IDB; we don't know how many interfaces were
262 * involved, so we just say one interface, about which
263 * we only know the link-layer type, snapshot length,
264 * and time stamp resolution.
265 */
266 wtap_add_generated_idb(wth);
267
268 return WTAP_OPEN_MINE;
269 }
270 offset += 1;
271 }
272
273 /*
274 * Check if this is a ASCII formatted file by scanning for the magic string
275 */
276 offset=0;
277 while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii))
278 {
279 if (memcmp (magic + offset, iseries_hdr_magic_ascii, sizeof iseries_hdr_magic_ascii) == 0)
280 {
281 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
282 {
283 return WTAP_OPEN_ERROR;
284 }
285 /*
286 * Do some basic sanity checking to ensure we can handle the
287 * contents of this trace
288 */
289 if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
290 {
291 if (*err == 0)
292 return WTAP_OPEN_NOT_MINE;
293 else
294 return WTAP_OPEN_ERROR;
295 }
296
297 wth->file_encap = WTAP_ENCAP_ETHERNET;
298 wth->file_type_subtype = iseries_file_type_subtype;
299 wth->snapshot_length = 0;
300 wth->subtype_read = iseries_read;
301 wth->subtype_seek_read = iseries_seek_read;
302 wth->file_tsprec = WTAP_TSPREC_USEC;
303
304 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
305 {
306 return WTAP_OPEN_ERROR;
307 }
308
309 /*
310 * Add an IDB; we don't know how many interfaces were
311 * involved, so we just say one interface, about which
312 * we only know the link-layer type, snapshot length,
313 * and time stamp resolution.
314 */
315 wtap_add_generated_idb(wth);
316
317 return WTAP_OPEN_MINE;
318 }
319 offset += 1;
320 }
321
322 /* Neither ASCII or UNICODE so not supported */
323 return WTAP_OPEN_NOT_MINE;
324 }
325
326 /*
327 * Do some basic sanity checking to ensure we can handle the
328 * contents of this trace by checking the header page for
329 * requisite requirements and additional information.
330 */
331 static gboolean
iseries_check_file_type(wtap * wth,int * err,gchar ** err_info,int format)332 iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format)
333 {
334 gboolean is_iseries = FALSE;
335 guint line;
336 int num_items_scanned;
337 char buf[ISERIES_LINE_LENGTH], protocol[9];
338 iseries_t *iseries;
339
340 /* Save trace format for passing between packets */
341 iseries = g_new(iseries_t, 1);
342 iseries->have_date = FALSE;
343 iseries->format = format;
344
345 for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
346 {
347 memset(buf, 0x0, sizeof(buf));
348 if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
349 {
350 /* EOF or error. */
351 *err = file_error (wth->fh, err_info);
352 if (*err == WTAP_ERR_SHORT_READ)
353 *err = 0;
354 break;
355 }
356
357 /*
358 * Check that we are dealing with an ETHERNET trace
359 */
360 if (iseries->format == ISERIES_FORMAT_UNICODE)
361 {
362 iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
363 }
364 ascii_strup_inplace (buf);
365 num_items_scanned = sscanf (buf,
366 "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s",
367 protocol);
368 if (num_items_scanned == 1)
369 {
370 if (memcmp (protocol, "ETHERNET", 8) == 0)
371 {
372 *err = 0;
373 is_iseries = TRUE;
374 }
375 }
376
377 /*
378 * The header is the only place where the date part of the timestamp is held, so
379 * extract it here and store for all packets to access
380 */
381 num_items_scanned = sscanf (buf,
382 "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d",
383 &iseries->month, &iseries->day,
384 &iseries->year);
385 if (num_items_scanned == 3)
386 {
387 iseries->have_date = TRUE;
388 }
389 }
390
391 if (is_iseries)
392 wth->priv = (void *) iseries;
393 else
394 g_free(iseries);
395
396 return is_iseries;
397 }
398
399 /*
400 * Find the next packet and parse it; called from wtap_read().
401 */
402 static gboolean
iseries_read(wtap * wth,wtap_rec * rec,Buffer * buf,int * err,gchar ** err_info,gint64 * data_offset)403 iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf, int *err,
404 gchar ** err_info, gint64 *data_offset)
405 {
406 gint64 offset;
407
408 /*
409 * Locate the next packet
410 */
411 offset = iseries_seek_next_packet (wth, err, err_info);
412 if (offset < 0)
413 return FALSE;
414 *data_offset = offset;
415
416 /*
417 * Parse the packet and extract the various fields
418 */
419 return iseries_parse_packet (wth, wth->fh, rec, buf, err, err_info);
420 }
421
422 /*
423 * Seeks to the beginning of the next packet, and returns the
424 * byte offset. Returns -1 on failure or EOF; on EOF, sets
425 * *err to 0, and, on failure, sets *err to the error and *err_info
426 * to null or an additional error string.
427 */
428 static gint64
iseries_seek_next_packet(wtap * wth,int * err,gchar ** err_info)429 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info)
430 {
431 iseries_t *iseries = (iseries_t *)wth->priv;
432 char buf[ISERIES_LINE_LENGTH],type[5];
433 int line, num_items_scanned;
434 gint64 cur_off;
435 long buflen;
436
437 for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
438 {
439 if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
440 {
441 /* EOF or error. */
442 *err = file_error (wth->fh, err_info);
443 return -1;
444 }
445 /* Convert UNICODE to ASCII if required and determine */
446 /* the number of bytes to rewind to beginning of record. */
447 if (iseries->format == ISERIES_FORMAT_UNICODE)
448 {
449 /* buflen is #bytes to 1st 0x0A */
450 buflen = iseries_UNICODE_to_ASCII ((guint8 *) buf, ISERIES_LINE_LENGTH);
451 }
452 else
453 {
454 /* Else buflen is just length of the ASCII string */
455 buflen = (long) strlen (buf);
456 }
457 ascii_strup_inplace (buf);
458 /* Check we have enough data in the line */
459 if (buflen < 78)
460 {
461 continue;
462 }
463 /* If packet header found return the offset */
464 num_items_scanned =
465 sscanf (buf+78,
466 "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
467 if (num_items_scanned == 1)
468 {
469 /* Rewind to beginning of line */
470 cur_off = file_tell (wth->fh);
471 if (cur_off == -1)
472 {
473 *err = file_error (wth->fh, err_info);
474 return -1;
475 }
476 if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
477 {
478 return -1;
479 }
480 return cur_off - buflen;
481 }
482 }
483
484 *err = WTAP_ERR_BAD_FILE;
485 *err_info =
486 g_strdup_printf ("iseries: next packet header not found within %d lines",
487 ISERIES_MAX_TRACE_LEN);
488 return -1;
489 }
490
491 /*
492 * Read packets in random-access fashion
493 */
494 static gboolean
iseries_seek_read(wtap * wth,gint64 seek_off,wtap_rec * rec,Buffer * buf,int * err,gchar ** err_info)495 iseries_seek_read (wtap * wth, gint64 seek_off, wtap_rec *rec,
496 Buffer * buf, int *err, gchar ** err_info)
497 {
498
499 /* seek to packet location */
500 if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
501 return FALSE;
502
503 /*
504 * Parse the packet and extract the various fields
505 */
506 return iseries_parse_packet (wth, wth->random_fh, rec, buf,
507 err, err_info);
508 }
509
510 static int
append_hex_digits(char * ascii_buf,int ascii_offset,int max_offset,char * data,int * err,gchar ** err_info)511 append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset,
512 char *data, int *err, gchar **err_info)
513 {
514 int in_offset, out_offset;
515 int c;
516 unsigned int i;
517 gboolean overflow = FALSE;
518
519 in_offset = 0;
520 out_offset = ascii_offset;
521 for (;;)
522 {
523 /*
524 * Process a block of up to 16 hex digits.
525 * The block is terminated early by an end-of-line indication (NUL,
526 * CR, or LF), by a space (which terminates the last block of the
527 * data we're processing), or by a "*", which introduces the ASCII representation
528 * of the data.
529 * All characters in the block must be upper-case hex digits;
530 * there might or might not be a space *after* a block, but, if so,
531 * that will be skipped over after the block is processed.
532 */
533 for (i = 0; i < 16; i++, in_offset++)
534 {
535 /*
536 * If we see an end-of-line indication, or an early-end-of-block
537 * indication (space), we're done. (Only the last block ends
538 * early.)
539 */
540 c = data[in_offset] & 0xFF;
541 if (c == '\0' || c == ' ' || c == '*' || c == '\r' || c == '\n')
542 {
543 goto done;
544 }
545 if (!g_ascii_isxdigit(c) || g_ascii_islower(c))
546 {
547 /*
548 * Not a hex digit, or a lower-case hex digit.
549 * Treat this as an indication that the line isn't a data
550 * line, so we just ignore it.
551 *
552 * XXX - do so only for continuation lines; treat non-hex-digit
553 * characters as errors for other lines?
554 */
555 return ascii_offset; /* pretend we appended nothing */
556 }
557 if (out_offset >= max_offset)
558 overflow = TRUE;
559 else
560 {
561 ascii_buf[out_offset] = c;
562 out_offset++;
563 }
564 }
565 /*
566 * Skip blanks, if any.
567 */
568 for (; (data[in_offset] & 0xFF) == ' '; in_offset++)
569 ;
570 }
571 done:
572 /*
573 * If we processed an *odd* number of hex digits, report an error.
574 */
575 if ((i % 2) != 0)
576 {
577 *err = WTAP_ERR_BAD_FILE;
578 *err_info = g_strdup("iseries: odd number of hex digits in a line");
579 return -1;
580 }
581 if (overflow)
582 {
583 *err = WTAP_ERR_BAD_FILE;
584 *err_info = g_strdup("iseries: more packet data than the packet length indicated");
585 return -1;
586 }
587 return out_offset;
588 }
589
590 /* return the multiplier for nanoseconds */
591 static guint32
csec_multiplier(guint32 csec)592 csec_multiplier(guint32 csec)
593 {
594 if (csec < 10) return 100000000;
595 if (csec < 100) return 10000000;
596 if (csec < 1000) return 1000000;
597 if (csec < 10000) return 100000;
598 if (csec < 100000) return 10000;
599 if (csec < 1000000) return 1000;
600 if (csec < 10000000) return 100;
601 if (csec < 100000000) return 10;
602 return 1;
603 }
604
605 /* Parses a packet. */
606 static gboolean
iseries_parse_packet(wtap * wth,FILE_T fh,wtap_rec * rec,Buffer * buf,int * err,gchar ** err_info)607 iseries_parse_packet (wtap * wth, FILE_T fh, wtap_rec *rec,
608 Buffer *buf, int *err, gchar **err_info)
609 {
610 iseries_t *iseries = (iseries_t *)wth->priv;
611 gint64 cur_off;
612 gboolean isValid, isCurrentPacket;
613 int num_items_scanned, line, pktline, buflen;
614 int pkt_len, pktnum, hr, min, sec;
615 char direction[2], destmac[13], srcmac[13], type[5];
616 guint32 csec;
617 char data[ISERIES_LINE_LENGTH * 2];
618 int offset;
619 char *ascii_buf;
620 int ascii_offset;
621 struct tm tm;
622
623 /*
624 * Check for packet headers in first 3 lines this should handle page breaks
625 * situations and the header lines output at each page throw and ensure we
626 * read both the captured and packet lengths.
627 */
628 isValid = FALSE;
629 for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
630 {
631 if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
632 {
633 *err = file_error (fh, err_info);
634 return FALSE;
635 }
636 /* Convert UNICODE data to ASCII */
637 if (iseries->format == ISERIES_FORMAT_UNICODE)
638 {
639 iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
640 }
641 ascii_strup_inplace (data);
642 num_items_scanned =
643 sscanf (data,
644 "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9u%*[ \n\t]"
645 "%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s",
646 &pktnum, direction, &pkt_len, &hr, &min, &sec, &csec, destmac,
647 srcmac, type);
648 if (num_items_scanned == 10)
649 {
650 if (pktnum < 0)
651 {
652 *err = WTAP_ERR_BAD_FILE;
653 *err_info = g_strdup ("iseries: packet header has a negative packet number");
654 return FALSE;
655 }
656
657 if (pkt_len < 0)
658 {
659 *err = WTAP_ERR_BAD_FILE;
660 *err_info = g_strdup ("iseries: packet header has a negative packet length");
661 return FALSE;
662 }
663
664 if (hr < 0)
665 {
666 *err = WTAP_ERR_BAD_FILE;
667 *err_info = g_strdup ("iseries: packet header has a negative hour in the time stamp");
668 return FALSE;
669 }
670
671 if (hr > 23)
672 {
673 *err = WTAP_ERR_BAD_FILE;
674 *err_info = g_strdup ("iseries: packet header has a hour in the time stamp greater than 23");
675 return FALSE;
676 }
677
678 if (min < 0)
679 {
680 *err = WTAP_ERR_BAD_FILE;
681 *err_info = g_strdup ("iseries: packet header has a negative minute in the time stamp");
682 return FALSE;
683 }
684
685 if (min > 59)
686 {
687 *err = WTAP_ERR_BAD_FILE;
688 *err_info = g_strdup ("iseries: packet header has a minute in the time stamp greater than 59");
689 return FALSE;
690 }
691
692 if (sec < 0)
693 {
694 *err = WTAP_ERR_BAD_FILE;
695 *err_info = g_strdup ("iseries: packet header has a negative second in the time stamp");
696 return FALSE;
697 }
698
699 /*
700 * Yes, 60, even though the time-conversion routines on most OSes
701 * might not handle leap seconds.
702 */
703 if (sec > 60)
704 {
705 *err = WTAP_ERR_BAD_FILE;
706 *err_info = g_strdup ("iseries: packet header has a second in the time stamp greater than 60");
707 return FALSE;
708 }
709
710 if (strlen(destmac) != 12)
711 {
712 *err = WTAP_ERR_BAD_FILE;
713 *err_info = g_strdup ("iseries: packet header has a destination MAC address shorter than 6 bytes");
714 return FALSE;
715 }
716
717 if (strlen(srcmac) != 12)
718 {
719 *err = WTAP_ERR_BAD_FILE;
720 *err_info = g_strdup ("iseries: packet header has a source MAC address shorter than 6 bytes");
721 return FALSE;
722 }
723
724 if (strlen(type) != 4)
725 {
726 *err = WTAP_ERR_BAD_FILE;
727 *err_info = g_strdup ("iseries: packet header has an Ethernet type/length field than 2 bytes");
728 return FALSE;
729 }
730
731 /* OK! We found the packet header line */
732 isValid = TRUE;
733 /*
734 * XXX - The Capture length returned by the iSeries trace doesn't
735 * seem to include the Ethernet header, so we add its length here.
736 *
737 * Check the length first, just in case it's *so* big that, after
738 * adding the Ethernet header length, it overflows.
739 */
740 if ((guint)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD - 14)
741 {
742 /*
743 * Probably a corrupt capture file; don't blow up trying
744 * to allocate space for an immensely-large packet, and
745 * don't think it's a really *small* packet because it
746 * overflowed. (Calculate the size as a 64-bit value in
747 * the error message, to avoid an overflow.)
748 */
749 *err = WTAP_ERR_BAD_FILE;
750 *err_info = g_strdup_printf("iseries: File has %" G_GUINT64_FORMAT "-byte packet, bigger than maximum of %u",
751 (guint64)pkt_len + 14,
752 WTAP_MAX_PACKET_SIZE_STANDARD);
753 return FALSE;
754 }
755 pkt_len += 14;
756 break;
757 }
758 }
759
760 /*
761 * If no packet header found we exit at this point and inform the user.
762 */
763 if (!isValid)
764 {
765 *err = WTAP_ERR_BAD_FILE;
766 *err_info = g_strdup ("iseries: packet header isn't valid");
767 return FALSE;
768 }
769
770 rec->rec_type = REC_TYPE_PACKET;
771 rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
772 rec->presence_flags = WTAP_HAS_CAP_LEN;
773
774 /*
775 * If we have Wiretap Header then populate it here
776 *
777 * Timer resolution on the iSeries is hardware dependent. We determine
778 * the resolution based on how many digits we see.
779 */
780 if (iseries->have_date)
781 {
782 rec->presence_flags |= WTAP_HAS_TS;
783 tm.tm_year = 100 + iseries->year;
784 tm.tm_mon = iseries->month - 1;
785 tm.tm_mday = iseries->day;
786 tm.tm_hour = hr;
787 tm.tm_min = min;
788 tm.tm_sec = sec;
789 tm.tm_isdst = -1;
790 rec->ts.secs = mktime (&tm);
791 rec->ts.nsecs = csec * csec_multiplier(csec);
792 }
793
794 rec->rec_header.packet_header.len = pkt_len;
795 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET;
796 rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
797
798 /*
799 * Allocate a buffer big enough to hold the claimed packet length
800 * worth of byte values; each byte will be two hex digits, so the
801 * buffer's size should be twice the packet length.
802 *
803 * (There is no need to null-terminate the buffer.)
804 */
805 ascii_buf = (char *)g_malloc (pkt_len*2);
806 ascii_offset = 0;
807
808 /*
809 * Copy in the Ethernet header.
810 *
811 * The three fields have already been checked to have the right length
812 * (6 bytes, hence 12 characters, of hex-dump destination and source
813 * addresses, and 2 bytes, hence 4 characters, of hex-dump type/length).
814 *
815 * pkt_len is guaranteed to be >= 14, so 2*pkt_len is guaranteed to be
816 * >= 28, so we don't need to do any bounds checking.
817 */
818 memcpy(&ascii_buf[0], destmac, 12);
819 ascii_offset += 12;
820 memcpy(&ascii_buf[12], srcmac, 12);
821 ascii_offset += 12;
822 memcpy(&ascii_buf[24], type, 4);
823 ascii_offset += 4;
824
825 /*
826 * Start reading packet contents
827 */
828 isCurrentPacket = TRUE;
829
830 /* loop through packet lines and breakout when the next packet header is read */
831 pktline = 0;
832 while (isCurrentPacket)
833 {
834 pktline++;
835 /* Read the next line */
836 if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
837 {
838 *err = file_error (fh, err_info);
839 if (*err == 0)
840 {
841 /* Hit the EOF without an error */
842 break;
843 }
844 goto errxit;
845 }
846
847 /* Convert UNICODE data to ASCII and determine line length */
848 if (iseries->format == ISERIES_FORMAT_UNICODE)
849 {
850 buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
851 }
852 else
853 {
854 /* Else bytes to rewind is just length of ASCII string */
855 buflen = (int) strlen (data);
856 }
857
858 /*
859 * Skip leading white space.
860 */
861 for (offset = 0; g_ascii_isspace(data[offset]); offset++)
862 ;
863
864 /*
865 * The higher-level header information starts at an offset of
866 * 22 characters. The header tags are 14 characters long.
867 *
868 * XXX - for IPv6, if the next header isn't the last header,
869 * the intermediate headers do *NOT* appear to be shown in
870 * the dump file *at all*, so the packet *cannot* be
871 * reconstructed!
872 */
873 if (offset == 22)
874 {
875 if (strncmp(data + 22, "IP Header : ", 14) == 0 ||
876 strncmp(data + 22, "IPv6 Header: ", 14) == 0 ||
877 strncmp(data + 22, "ARP Header : ", 14) == 0 ||
878 strncmp(data + 22, "TCP Header : ", 14) == 0 ||
879 strncmp(data + 22, "UDP Header : ", 14) == 0 ||
880 strncmp(data + 22, "ICMP Header: ", 14) == 0 ||
881 strncmp(data + 22, "ICMPv6 Hdr: ", 14) == 0 ||
882 strncmp(data + 22, "Option Hdr: ", 14) == 0)
883 {
884 ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
885 pkt_len*2,
886 data + 22 + 14, err,
887 err_info);
888 if (ascii_offset == -1)
889 {
890 /* Bad line. */
891 return FALSE;
892 }
893 continue;
894 }
895 }
896
897 /*
898 * Is this a data line?
899 *
900 * The "Data" starts at an offset of 8.
901 */
902 if (offset == 9)
903 {
904 if (strncmp(data + 9, "Data . . . . . : ", 18) == 0)
905 {
906 ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
907 pkt_len*2,
908 data + 9 + 18, err,
909 err_info);
910 if (ascii_offset == -1)
911 {
912 /* Bad line. */
913 return FALSE;
914 }
915 continue;
916 }
917 }
918
919 /*
920 * Is this a continuation of a previous header or data line?
921 * That's blanks followed by hex digits; first try the
922 * "no column separators" form.
923 *
924 * Continuations of header lines begin at an offset of 36;
925 * continuations of data lines begin at an offset of 27.
926 */
927 if (offset == 36 || offset == 27)
928 {
929 ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
930 pkt_len*2,
931 data + offset, err,
932 err_info);
933 if (ascii_offset == -1)
934 {
935 /* Bad line. */
936 return FALSE;
937 }
938 continue;
939 }
940
941 /*
942 * If we see the identifier for the next packet then rewind and set
943 * isCurrentPacket FALSE
944 */
945 ascii_strup_inplace (data);
946 /* If packet header found return the offset */
947 num_items_scanned =
948 sscanf (data+78,
949 "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
950 if ((num_items_scanned == 1) && pktline > 1)
951 {
952 isCurrentPacket = FALSE;
953 cur_off = file_tell( fh);
954 if (cur_off == -1)
955 {
956 /* Error. */
957 *err = file_error (fh, err_info);
958 goto errxit;
959 }
960 if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
961 {
962 /* XXX: need to set err_info ?? */
963 goto errxit;
964 }
965 }
966 }
967
968 /*
969 * Make the captured length be the amount of bytes we've read (which
970 * is half the number of characters of hex dump we have).
971 *
972 * XXX - this can happen for IPv6 packets if the next header isn't the
973 * last header.
974 */
975 rec->rec_header.packet_header.caplen = ((guint32) ascii_offset)/2;
976
977 /* Make sure we have enough room for the packet. */
978 ws_buffer_assure_space (buf, rec->rec_header.packet_header.caplen);
979 /* Convert ascii data to binary and return in the frame buffer */
980 iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), ascii_offset);
981
982 /* free buffer allocs and return */
983 *err = 0;
984 g_free (ascii_buf);
985 return TRUE;
986
987 errxit:
988 g_free (ascii_buf);
989 return FALSE;
990 }
991
992 /*
993 * Simple routine to convert an UNICODE buffer to ASCII
994 *
995 * XXX - This may be possible with iconv or similar
996 */
997 static int
iseries_UNICODE_to_ASCII(guint8 * buf,guint bytes)998 iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
999 {
1000 guint i;
1001 guint8 *bufptr;
1002
1003 bufptr = buf;
1004
1005 for (i = 0; i < bytes; i++)
1006 {
1007 switch (buf[i])
1008 {
1009 case 0xFE:
1010 case 0xFF:
1011 case 0x00:
1012 break;
1013 default:
1014 *bufptr = buf[i];
1015 bufptr++;
1016 }
1017 if (buf[i] == 0x0A)
1018 break;
1019 }
1020 ws_assert(bufptr < buf + bytes);
1021 *bufptr = '\0';
1022 return i;
1023 }
1024
1025 /*
1026 * Simple routine to convert an ASCII hex string to binary data
1027 * Requires ASCII hex data and buffer to populate with binary data
1028 */
1029 static gboolean
iseries_parse_hex_string(const char * ascii,guint8 * buf,size_t len)1030 iseries_parse_hex_string (const char * ascii, guint8 * buf, size_t len)
1031 {
1032 size_t i;
1033 int byte;
1034 gint hexvalue;
1035 guint8 bytevalue;
1036
1037 byte = 0;
1038 for (i = 0; i < len; i++)
1039 {
1040 hexvalue = g_ascii_xdigit_value(ascii[i]);
1041 i++;
1042 if (hexvalue == -1)
1043 return FALSE; /* not a valid hex digit */
1044 bytevalue = (guint8)(hexvalue << 4);
1045 if (i >= len)
1046 return FALSE; /* only one hex digit of the byte is present */
1047 hexvalue = g_ascii_xdigit_value(ascii[i]);
1048 if (hexvalue == -1)
1049 return FALSE; /* not a valid hex digit */
1050 bytevalue |= (guint8) hexvalue;
1051 buf[byte] = bytevalue;
1052 byte++;
1053 }
1054 return TRUE;
1055 }
1056
1057 static const struct supported_block_type iseries_blocks_supported[] = {
1058 /*
1059 * We support packet blocks, with no comments or other options.
1060 */
1061 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1062 };
1063
1064 static const struct file_type_subtype_info iseries_info = {
1065 "IBM iSeries comm. trace (ASCII)", "iseries_ascii", "txt", NULL,
1066 FALSE, BLOCKS_SUPPORTED(iseries_blocks_supported),
1067 NULL, NULL, NULL
1068 };
1069
1070 static const struct supported_block_type iseries_unicode_blocks_supported[] = {
1071 /*
1072 * We support packet blocks, with no comments or other options.
1073 */
1074 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1075 };
1076
1077 static const struct file_type_subtype_info iseries_unicode_info = {
1078 "IBM iSeries comm. trace (Unicode)", "iseries_unicode", "txt", NULL,
1079 FALSE, BLOCKS_SUPPORTED(iseries_unicode_blocks_supported),
1080 NULL, NULL, NULL
1081 };
1082
register_iseries(void)1083 void register_iseries(void)
1084 {
1085 iseries_file_type_subtype = wtap_register_file_type_subtype(&iseries_info);
1086 iseries_unicode_file_type_subtype = wtap_register_file_type_subtype(&iseries_unicode_info);
1087
1088 /*
1089 * Register names for backwards compatibility with the
1090 * wtap_filetypes table in Lua.
1091 */
1092 wtap_register_backwards_compatibility_lua_name("ISERIES",
1093 iseries_file_type_subtype);
1094 wtap_register_backwards_compatibility_lua_name("ISERIES_UNICODE",
1095 iseries_unicode_file_type_subtype);
1096 }
1097
1098 /*
1099 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1100 *
1101 * Local variables:
1102 * c-basic-offset: 2
1103 * tab-width: 8
1104 * indent-tabs-mode: nil
1105 * End:
1106 *
1107 * vi: set shiftwidth=2 tabstop=8 expandtab:
1108 * :indentSize=2:tabSize=8:noTabs=true:
1109 */
1110