Revert "UNUSED enc_key_id_{equal,hash}"
[wireshark-sm.git] / wiretap / iseries.c
blobaa78744e8caaa1fd97bf0c6200b4c4ba7e91dfea
1 /* iseries.c
3 * Wiretap Library
4 * Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com>
6 * Based on toshiba.c and vms.c
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
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.
15 * iSeries Comms traces consist of a header page and a subsequent number of packet records
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:
20 * 1. Object protocol = ETHERNET (Default)
21 * 2. ASCII or Unicode file formats.
23 * The above can be achieved by passing option ASCII(*YES) with the trace command
27 /* iSeries header page
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
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.
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
66 * http://www-912.ibm.com/s_dir/SLKBase.nsf/1ac66549a21402188625680b0002037e/e05fb0515bc3449686256ce600512c37?OpenDocument
68 * and
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
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:".
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.
81 * Information from the packet header line, higher-level headers and, if
82 * available, data lines are extracted by the module for displaying.
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*.*. *
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 is no longer output in groups of 16 hex
105 * digits.
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 *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
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.
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;**
139 #include "config.h"
140 #include "iseries.h"
141 #include "wtap-int.h"
142 #include "file_wrappers.h"
144 #include <stdlib.h>
145 #include <string.h>
147 #include <wsutil/str_util.h>
148 #include <wsutil/strtoi.h>
149 #include <wsutil/ws_assert.h>
151 #define ISERIES_LINE_LENGTH 270
152 #define ISERIES_HDR_LINES_TO_CHECK 100
153 #define ISERIES_PKT_LINES_TO_CHECK 4
154 #define ISERIES_MAX_TRACE_LEN 99999999
155 #define ISERIES_FORMAT_ASCII 1
156 #define ISERIES_FORMAT_UNICODE 2
159 * Magic strings - "COMMUNICATIONS TRACE", in ASCII and little-endian UCS-2.
161 static const char iseries_hdr_magic_ascii[] = {
162 'C', 'O', 'M', 'M',
163 'U', 'N', 'I', 'C',
164 'A', 'T', 'I', 'O',
165 'N', 'S', ' ', 'T',
166 'R', 'A', 'C', 'E'
168 static const char iseries_hdr_magic_le_ucs_2[] = {
169 'C', 0x0, 'O', 0x0, 'M', 0x0, 'M', 0x0,
170 'U', 0x0, 'N', 0x0, 'I', 0x0, 'C', 0x0,
171 'A', 0x0, 'T', 0x0, 'I', 0x0, 'O', 0x0,
172 'N', 0x0, 'S', 0x0, ' ', 0x0, 'T', 0x0,
173 'R', 0x0, 'A', 0x0, 'C', 0x0, 'E', 0x0
176 typedef struct {
177 bool have_date; /* true if we found a capture start date */
178 int year, month, day; /* The start date */
179 int format; /* Trace format type */
180 } iseries_t;
182 static bool iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf,
183 int *err, char ** err_info, int64_t *data_offset);
184 static bool iseries_seek_read (wtap * wth, int64_t seek_off,
185 wtap_rec *rec,
186 Buffer * buf, int *err, char ** err_info);
187 static bool iseries_check_file_type (wtap * wth, int *err, char **err_info,
188 int format);
189 static int64_t iseries_seek_next_packet (wtap * wth, int *err, char **err_info);
190 static bool iseries_parse_packet (wtap * wth, FILE_T fh,
191 wtap_rec *rec,
192 Buffer * buf, int *err, char ** err_info);
193 static int iseries_UNICODE_to_ASCII (uint8_t * buf, unsigned bytes);
194 static bool iseries_parse_hex_string (const char * ascii, uint8_t * buf,
195 size_t len);
197 static int iseries_file_type_subtype = -1;
198 static int iseries_unicode_file_type_subtype = -1;
200 void register_iseries(void);
203 * XXX - it would probably be cleaner to use a UCS-2 flavor of file_gets(),
204 * rather than file_gets(), if we're reading a UCS-2 file.
206 wtap_open_return_val
207 iseries_open (wtap * wth, int *err, char ** err_info)
209 int offset;
210 char magic[ISERIES_LINE_LENGTH];
213 * Check that file starts with a valid iSeries COMMS TRACE header
214 * by scanning for it in the first line
216 if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info))
218 if (*err != WTAP_ERR_SHORT_READ)
219 return WTAP_OPEN_ERROR;
220 return WTAP_OPEN_NOT_MINE;
224 * Check if this is a little-endian UCS-2 Unicode formatted file by scanning
225 * for the magic string
227 offset=0;
228 while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2)))
230 if (memcmp (magic + offset, iseries_hdr_magic_le_ucs_2, sizeof iseries_hdr_magic_le_ucs_2) == 0) {
231 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
233 return WTAP_OPEN_ERROR;
236 * Do some basic sanity checking to ensure we can handle the
237 * contents of this trace
239 if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
241 if (*err == 0)
242 return WTAP_OPEN_NOT_MINE;
243 else
244 return WTAP_OPEN_ERROR;
247 wth->file_encap = WTAP_ENCAP_ETHERNET;
248 wth->file_type_subtype = iseries_unicode_file_type_subtype;
249 wth->snapshot_length = 0;
250 wth->subtype_read = iseries_read;
251 wth->subtype_seek_read = iseries_seek_read;
252 wth->file_tsprec = WTAP_TSPREC_USEC;
254 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
256 return WTAP_OPEN_ERROR;
260 * Add an IDB; we don't know how many interfaces were
261 * involved, so we just say one interface, about which
262 * we only know the link-layer type, snapshot length,
263 * and time stamp resolution.
265 wtap_add_generated_idb(wth);
267 return WTAP_OPEN_MINE;
269 offset += 1;
273 * Check if this is a ASCII formatted file by scanning for the magic string
275 offset=0;
276 while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii))
278 if (memcmp (magic + offset, iseries_hdr_magic_ascii, sizeof iseries_hdr_magic_ascii) == 0)
280 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
282 return WTAP_OPEN_ERROR;
285 * Do some basic sanity checking to ensure we can handle the
286 * contents of this trace
288 if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
290 if (*err == 0)
291 return WTAP_OPEN_NOT_MINE;
292 else
293 return WTAP_OPEN_ERROR;
296 wth->file_encap = WTAP_ENCAP_ETHERNET;
297 wth->file_type_subtype = iseries_file_type_subtype;
298 wth->snapshot_length = 0;
299 wth->subtype_read = iseries_read;
300 wth->subtype_seek_read = iseries_seek_read;
301 wth->file_tsprec = WTAP_TSPREC_USEC;
303 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
305 return WTAP_OPEN_ERROR;
309 * Add an IDB; we don't know how many interfaces were
310 * involved, so we just say one interface, about which
311 * we only know the link-layer type, snapshot length,
312 * and time stamp resolution.
314 wtap_add_generated_idb(wth);
316 return WTAP_OPEN_MINE;
318 offset += 1;
321 /* Neither ASCII or UNICODE so not supported */
322 return WTAP_OPEN_NOT_MINE;
326 * Do some basic sanity checking to ensure we can handle the
327 * contents of this trace by checking the header page for
328 * requisite requirements and additional information.
330 static bool
331 iseries_check_file_type (wtap * wth, int *err, char **err_info, int format)
333 bool is_iseries = false;
334 unsigned line;
335 int num_items_scanned;
336 char buf[ISERIES_LINE_LENGTH], protocol[9];
337 iseries_t *iseries;
339 /* Save trace format for passing between packets */
340 iseries = g_new(iseries_t, 1);
341 iseries->have_date = false;
342 iseries->format = format;
344 for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
346 memset(buf, 0x0, sizeof(buf));
347 if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
349 /* EOF or error. */
350 *err = file_error (wth->fh, err_info);
351 if (*err == WTAP_ERR_SHORT_READ)
352 *err = 0;
353 break;
357 * Check that we are dealing with an ETHERNET trace
359 if (iseries->format == ISERIES_FORMAT_UNICODE)
361 iseries_UNICODE_to_ASCII ((uint8_t *)buf, ISERIES_LINE_LENGTH);
363 ascii_strup_inplace (buf);
364 num_items_scanned = sscanf (buf,
365 "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s",
366 protocol);
367 if (num_items_scanned == 1)
369 if (memcmp (protocol, "ETHERNET", 8) == 0)
371 *err = 0;
372 is_iseries = true;
377 * The header is the only place where the date part of the timestamp is held, so
378 * extract it here and store for all packets to access
380 num_items_scanned = sscanf (buf,
381 "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d",
382 &iseries->month, &iseries->day,
383 &iseries->year);
384 if (num_items_scanned == 3)
386 iseries->have_date = true;
390 if (is_iseries)
391 wth->priv = (void *) iseries;
392 else
393 g_free(iseries);
395 return is_iseries;
399 * Find the next packet and parse it; called from wtap_read().
401 static bool
402 iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf, int *err,
403 char ** err_info, int64_t *data_offset)
405 int64_t offset;
408 * Locate the next packet
410 offset = iseries_seek_next_packet (wth, err, err_info);
411 if (offset < 0)
412 return false;
413 *data_offset = offset;
416 * Parse the packet and extract the various fields
418 return iseries_parse_packet (wth, wth->fh, rec, buf, err, err_info);
422 * Seeks to the beginning of the next packet, and returns the
423 * byte offset. Returns -1 on failure or EOF; on EOF, sets
424 * *err to 0, and, on failure, sets *err to the error and *err_info
425 * to null or an additional error string.
427 static int64_t
428 iseries_seek_next_packet (wtap * wth, int *err, char **err_info)
430 iseries_t *iseries = (iseries_t *)wth->priv;
431 char buf[ISERIES_LINE_LENGTH],type[5];
432 int line, num_items_scanned;
433 int64_t cur_off;
434 long buflen;
436 for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
438 if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
440 /* EOF or error. */
441 *err = file_error (wth->fh, err_info);
442 return -1;
444 /* Convert UNICODE to ASCII if required and determine */
445 /* the number of bytes to rewind to beginning of record. */
446 if (iseries->format == ISERIES_FORMAT_UNICODE)
448 /* buflen is #bytes to 1st 0x0A */
449 buflen = iseries_UNICODE_to_ASCII ((uint8_t *) buf, ISERIES_LINE_LENGTH);
451 else
453 /* Else buflen is just length of the ASCII string */
454 buflen = (long) strlen (buf);
456 ascii_strup_inplace (buf);
457 /* Check we have enough data in the line */
458 if (buflen < 78)
460 continue;
462 /* If packet header found return the offset */
463 num_items_scanned =
464 sscanf (buf+78,
465 "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
466 if (num_items_scanned == 1)
468 /* Rewind to beginning of line */
469 cur_off = file_tell (wth->fh);
470 if (cur_off == -1)
472 *err = file_error (wth->fh, err_info);
473 return -1;
475 if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
477 return -1;
479 return cur_off - buflen;
483 *err = WTAP_ERR_BAD_FILE;
484 *err_info =
485 ws_strdup_printf ("iseries: next packet header not found within %d lines",
486 ISERIES_MAX_TRACE_LEN);
487 return -1;
491 * Read packets in random-access fashion
493 static bool
494 iseries_seek_read (wtap * wth, int64_t seek_off, wtap_rec *rec,
495 Buffer * buf, int *err, char ** err_info)
498 /* seek to packet location */
499 if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
500 return false;
503 * Parse the packet and extract the various fields
505 return iseries_parse_packet (wth, wth->random_fh, rec, buf,
506 err, err_info);
509 static int
510 append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset,
511 char *data, int *err, char **err_info)
513 int in_offset, out_offset;
514 int c;
515 unsigned int i;
516 bool overflow = false;
518 in_offset = 0;
519 out_offset = ascii_offset;
520 for (;;)
523 * Process a block of up to 16 hex digits.
524 * The block is terminated early by an end-of-line indication (NUL,
525 * CR, or LF), by a space (which terminates the last block of the
526 * data we're processing), or by a "*", which introduces the ASCII representation
527 * of the data.
528 * All characters in the block must be upper-case hex digits;
529 * there might or might not be a space *after* a block, but, if so,
530 * that will be skipped over after the block is processed.
532 for (i = 0; i < 16; i++, in_offset++)
535 * If we see an end-of-line indication, or an early-end-of-block
536 * indication (space), we're done. (Only the last block ends
537 * early.)
539 c = data[in_offset] & 0xFF;
540 if (c == '\0' || c == ' ' || c == '*' || c == '\r' || c == '\n')
542 goto done;
544 if (!g_ascii_isxdigit(c) || g_ascii_islower(c))
547 * Not a hex digit, or a lower-case hex digit.
548 * Treat this as an indication that the line isn't a data
549 * line, so we just ignore it.
551 * XXX - do so only for continuation lines; treat non-hex-digit
552 * characters as errors for other lines?
554 return ascii_offset; /* pretend we appended nothing */
556 if (out_offset >= max_offset)
557 overflow = true;
558 else
560 ascii_buf[out_offset] = c;
561 out_offset++;
565 * Skip blanks, if any.
567 for (; (data[in_offset] & 0xFF) == ' '; in_offset++)
570 done:
572 * If we processed an *odd* number of hex digits, report an error.
574 if ((i % 2) != 0)
576 *err = WTAP_ERR_BAD_FILE;
577 *err_info = g_strdup("iseries: odd number of hex digits in a line");
578 return -1;
580 if (overflow)
582 *err = WTAP_ERR_BAD_FILE;
583 *err_info = g_strdup("iseries: more packet data than the packet length indicated");
584 return -1;
586 return out_offset;
589 /* return the multiplier for nanoseconds */
590 static uint32_t
591 csec_multiplier(uint32_t csec)
593 if (csec < 10) return 100000000;
594 if (csec < 100) return 10000000;
595 if (csec < 1000) return 1000000;
596 if (csec < 10000) return 100000;
597 if (csec < 100000) return 10000;
598 if (csec < 1000000) return 1000;
599 if (csec < 10000000) return 100;
600 if (csec < 100000000) return 10;
601 return 1;
604 /* Parses a packet. */
605 static bool
606 iseries_parse_packet (wtap * wth, FILE_T fh, wtap_rec *rec,
607 Buffer *buf, int *err, char **err_info)
609 iseries_t *iseries = (iseries_t *)wth->priv;
610 int64_t cur_off;
611 bool isValid, isCurrentPacket;
612 int num_items_scanned, line, pktline, buflen;
613 int pkt_len, pktnum, hr, min, sec;
614 char direction[2], destmac[13], srcmac[13], type[5];
615 uint32_t csec;
616 char data[ISERIES_LINE_LENGTH * 2];
617 int offset;
618 char *ascii_buf;
619 int ascii_offset;
620 struct tm tm;
623 * Check for packet headers in first 3 lines this should handle page breaks
624 * situations and the header lines output at each page throw and ensure we
625 * read both the captured and packet lengths.
627 isValid = false;
628 for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
630 if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
632 *err = file_error (fh, err_info);
633 return false;
635 /* Convert UNICODE data to ASCII */
636 if (iseries->format == ISERIES_FORMAT_UNICODE)
638 iseries_UNICODE_to_ASCII ((uint8_t *)data, ISERIES_LINE_LENGTH);
640 ascii_strup_inplace (data);
641 num_items_scanned =
642 sscanf (data,
643 "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9u%*[ \n\t]"
644 "%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s",
645 &pktnum, direction, &pkt_len, &hr, &min, &sec, &csec, destmac,
646 srcmac, type);
647 if (num_items_scanned == 10)
649 if (pktnum < 0)
651 *err = WTAP_ERR_BAD_FILE;
652 *err_info = g_strdup ("iseries: packet header has a negative packet number");
653 return false;
656 if (pkt_len < 0)
658 *err = WTAP_ERR_BAD_FILE;
659 *err_info = g_strdup ("iseries: packet header has a negative packet length");
660 return false;
663 if (hr < 0)
665 *err = WTAP_ERR_BAD_FILE;
666 *err_info = g_strdup ("iseries: packet header has a negative hour in the time stamp");
667 return false;
670 if (hr > 23)
672 *err = WTAP_ERR_BAD_FILE;
673 *err_info = g_strdup ("iseries: packet header has a hour in the time stamp greater than 23");
674 return false;
677 if (min < 0)
679 *err = WTAP_ERR_BAD_FILE;
680 *err_info = g_strdup ("iseries: packet header has a negative minute in the time stamp");
681 return false;
684 if (min > 59)
686 *err = WTAP_ERR_BAD_FILE;
687 *err_info = g_strdup ("iseries: packet header has a minute in the time stamp greater than 59");
688 return false;
691 if (sec < 0)
693 *err = WTAP_ERR_BAD_FILE;
694 *err_info = g_strdup ("iseries: packet header has a negative second in the time stamp");
695 return false;
699 * Yes, 60, even though the time-conversion routines on most OSes
700 * might not handle leap seconds.
702 if (sec > 60)
704 *err = WTAP_ERR_BAD_FILE;
705 *err_info = g_strdup ("iseries: packet header has a second in the time stamp greater than 60");
706 return false;
709 if (strlen(destmac) != 12)
711 *err = WTAP_ERR_BAD_FILE;
712 *err_info = g_strdup ("iseries: packet header has a destination MAC address shorter than 6 bytes");
713 return false;
716 if (strlen(srcmac) != 12)
718 *err = WTAP_ERR_BAD_FILE;
719 *err_info = g_strdup ("iseries: packet header has a source MAC address shorter than 6 bytes");
720 return false;
723 if (strlen(type) != 4)
725 *err = WTAP_ERR_BAD_FILE;
726 *err_info = g_strdup ("iseries: packet header has an Ethernet type/length field than 2 bytes");
727 return false;
730 /* OK! We found the packet header line */
731 isValid = true;
733 * XXX - The Capture length returned by the iSeries trace doesn't
734 * seem to include the Ethernet header, so we add its length here.
736 * Check the length first, just in case it's *so* big that, after
737 * adding the Ethernet header length, it overflows.
739 if ((unsigned)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD - 14)
742 * Probably a corrupt capture file; don't blow up trying
743 * to allocate space for an immensely-large packet, and
744 * don't think it's a really *small* packet because it
745 * overflowed. (Calculate the size as a 64-bit value in
746 * the error message, to avoid an overflow.)
748 *err = WTAP_ERR_BAD_FILE;
749 *err_info = ws_strdup_printf("iseries: File has %" PRIu64 "-byte packet, bigger than maximum of %u",
750 (uint64_t)pkt_len + 14,
751 WTAP_MAX_PACKET_SIZE_STANDARD);
752 return false;
754 pkt_len += 14;
755 break;
760 * If no packet header found we exit at this point and inform the user.
762 if (!isValid)
764 *err = WTAP_ERR_BAD_FILE;
765 *err_info = g_strdup ("iseries: packet header isn't valid");
766 return false;
769 rec->rec_type = REC_TYPE_PACKET;
770 rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
771 rec->presence_flags = WTAP_HAS_CAP_LEN;
774 * If we have Wiretap Header then populate it here
776 * Timer resolution on the iSeries is hardware dependent. We determine
777 * the resolution based on how many digits we see.
779 if (iseries->have_date)
781 rec->presence_flags |= WTAP_HAS_TS;
782 tm.tm_year = 100 + iseries->year;
783 tm.tm_mon = iseries->month - 1;
784 tm.tm_mday = iseries->day;
785 tm.tm_hour = hr;
786 tm.tm_min = min;
787 tm.tm_sec = sec;
788 tm.tm_isdst = -1;
789 rec->ts.secs = mktime (&tm);
790 rec->ts.nsecs = csec * csec_multiplier(csec);
793 rec->rec_header.packet_header.len = pkt_len;
794 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET;
795 rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
798 * Allocate a buffer big enough to hold the claimed packet length
799 * worth of byte values; each byte will be two hex digits, so the
800 * buffer's size should be twice the packet length.
802 * (There is no need to null-terminate the buffer.)
804 ascii_buf = (char *)g_malloc (pkt_len*2);
805 ascii_offset = 0;
808 * Copy in the Ethernet header.
810 * The three fields have already been checked to have the right length
811 * (6 bytes, hence 12 characters, of hex-dump destination and source
812 * addresses, and 2 bytes, hence 4 characters, of hex-dump type/length).
814 * pkt_len is guaranteed to be >= 14, so 2*pkt_len is guaranteed to be
815 * >= 28, so we don't need to do any bounds checking.
817 memcpy(&ascii_buf[0], destmac, 12);
818 ascii_offset += 12;
819 memcpy(&ascii_buf[12], srcmac, 12);
820 ascii_offset += 12;
821 memcpy(&ascii_buf[24], type, 4);
822 ascii_offset += 4;
825 * Start reading packet contents
827 isCurrentPacket = true;
829 /* loop through packet lines and breakout when the next packet header is read */
830 pktline = 0;
831 while (isCurrentPacket)
833 pktline++;
834 /* Read the next line */
835 if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
837 *err = file_error (fh, err_info);
838 if (*err == 0)
840 /* Hit the EOF without an error */
841 break;
843 goto errxit;
846 /* Convert UNICODE data to ASCII and determine line length */
847 if (iseries->format == ISERIES_FORMAT_UNICODE)
849 buflen = iseries_UNICODE_to_ASCII ((uint8_t *)data, ISERIES_LINE_LENGTH);
851 else
853 /* Else bytes to rewind is just length of ASCII string */
854 buflen = (int) strlen (data);
858 * Skip leading white space.
860 for (offset = 0; g_ascii_isspace(data[offset]); offset++)
864 * The higher-level header information starts at an offset of
865 * 22 characters. The header tags are 14 characters long.
867 * XXX - for IPv6, if the next header isn't the last header,
868 * the intermediate headers do *NOT* appear to be shown in
869 * the dump file *at all*, so the packet *cannot* be
870 * reconstructed!
872 if (offset == 22)
874 if (strncmp(data + 22, "IP Header : ", 14) == 0 ||
875 strncmp(data + 22, "IPv6 Header: ", 14) == 0 ||
876 strncmp(data + 22, "ARP Header : ", 14) == 0 ||
877 strncmp(data + 22, "TCP Header : ", 14) == 0 ||
878 strncmp(data + 22, "UDP Header : ", 14) == 0 ||
879 strncmp(data + 22, "ICMP Header: ", 14) == 0 ||
880 strncmp(data + 22, "ICMPv6 Hdr: ", 14) == 0 ||
881 strncmp(data + 22, "Option Hdr: ", 14) == 0)
883 ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
884 pkt_len*2,
885 data + 22 + 14, err,
886 err_info);
887 if (ascii_offset == -1)
889 /* Bad line. */
890 return false;
892 continue;
897 * Is this a data line?
899 * The "Data" starts at an offset of 8.
901 if (offset == 9)
903 if (strncmp(data + 9, "Data . . . . . : ", 18) == 0)
905 ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
906 pkt_len*2,
907 data + 9 + 18, err,
908 err_info);
909 if (ascii_offset == -1)
911 /* Bad line. */
912 return false;
914 continue;
919 * Is this a continuation of a previous header or data line?
920 * That's blanks followed by hex digits; first try the
921 * "no column separators" form.
923 * Continuations of header lines begin at an offset of 36;
924 * continuations of data lines begin at an offset of 27.
926 if (offset == 36 || offset == 27)
928 ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
929 pkt_len*2,
930 data + offset, err,
931 err_info);
932 if (ascii_offset == -1)
934 /* Bad line. */
935 return false;
937 continue;
941 * If we see the identifier for the next packet then rewind and set
942 * isCurrentPacket false
944 ascii_strup_inplace (data);
945 /* If packet header found return the offset */
946 num_items_scanned =
947 sscanf (data+78,
948 "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
949 if ((num_items_scanned == 1) && pktline > 1)
951 isCurrentPacket = false;
952 cur_off = file_tell( fh);
953 if (cur_off == -1)
955 /* Error. */
956 *err = file_error (fh, err_info);
957 goto errxit;
959 if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
961 /* XXX: need to set err_info ?? */
962 goto errxit;
968 * Make the captured length be the amount of bytes we've read (which
969 * is half the number of characters of hex dump we have).
971 * XXX - this can happen for IPv6 packets if the next header isn't the
972 * last header.
974 rec->rec_header.packet_header.caplen = ((uint32_t) ascii_offset)/2;
976 /* Make sure we have enough room for the packet. */
977 ws_buffer_assure_space (buf, rec->rec_header.packet_header.caplen);
978 /* Convert ascii data to binary and return in the frame buffer */
979 iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), ascii_offset);
981 /* free buffer allocs and return */
982 *err = 0;
983 g_free (ascii_buf);
984 return true;
986 errxit:
987 g_free (ascii_buf);
988 return false;
992 * Simple routine to convert an UNICODE buffer to ASCII
994 * XXX - This may be possible with iconv or similar
996 static int
997 iseries_UNICODE_to_ASCII (uint8_t * buf, unsigned bytes)
999 unsigned i;
1000 uint8_t *bufptr;
1002 bufptr = buf;
1004 for (i = 0; i < bytes; i++)
1006 switch (buf[i])
1008 case 0xFE:
1009 case 0xFF:
1010 case 0x00:
1011 break;
1012 default:
1013 *bufptr = buf[i];
1014 bufptr++;
1016 if (buf[i] == 0x0A)
1017 break;
1019 ws_assert(bufptr < buf + bytes);
1020 *bufptr = '\0';
1021 return i;
1025 * Simple routine to convert an ASCII hex string to binary data
1026 * Requires ASCII hex data and buffer to populate with binary data
1028 static bool
1029 iseries_parse_hex_string (const char * ascii, uint8_t * buf, size_t len)
1031 size_t i;
1032 int byte;
1033 int hexvalue;
1034 uint8_t bytevalue;
1036 byte = 0;
1037 for (i = 0; i < len; i++)
1039 hexvalue = g_ascii_xdigit_value(ascii[i]);
1040 i++;
1041 if (hexvalue == -1)
1042 return false; /* not a valid hex digit */
1043 bytevalue = (uint8_t)(hexvalue << 4);
1044 if (i >= len)
1045 return false; /* only one hex digit of the byte is present */
1046 hexvalue = g_ascii_xdigit_value(ascii[i]);
1047 if (hexvalue == -1)
1048 return false; /* not a valid hex digit */
1049 bytevalue |= (uint8_t) hexvalue;
1050 buf[byte] = bytevalue;
1051 byte++;
1053 return true;
1056 static const struct supported_block_type iseries_blocks_supported[] = {
1058 * We support packet blocks, with no comments or other options.
1060 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1063 static const struct file_type_subtype_info iseries_info = {
1064 "IBM iSeries comm. trace (ASCII)", "iseries_ascii", "txt", NULL,
1065 false, BLOCKS_SUPPORTED(iseries_blocks_supported),
1066 NULL, NULL, NULL
1069 static const struct supported_block_type iseries_unicode_blocks_supported[] = {
1071 * We support packet blocks, with no comments or other options.
1073 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1076 static const struct file_type_subtype_info iseries_unicode_info = {
1077 "IBM iSeries comm. trace (Unicode)", "iseries_unicode", "txt", NULL,
1078 false, BLOCKS_SUPPORTED(iseries_unicode_blocks_supported),
1079 NULL, NULL, NULL
1082 void register_iseries(void)
1084 iseries_file_type_subtype = wtap_register_file_type_subtype(&iseries_info);
1085 iseries_unicode_file_type_subtype = wtap_register_file_type_subtype(&iseries_unicode_info);
1088 * Register names for backwards compatibility with the
1089 * wtap_filetypes table in Lua.
1091 wtap_register_backwards_compatibility_lua_name("ISERIES",
1092 iseries_file_type_subtype);
1093 wtap_register_backwards_compatibility_lua_name("ISERIES_UNICODE",
1094 iseries_unicode_file_type_subtype);
1098 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1100 * Local variables:
1101 * c-basic-offset: 2
1102 * tab-width: 8
1103 * indent-tabs-mode: nil
1104 * End:
1106 * vi: set shiftwidth=2 tabstop=8 expandtab:
1107 * :indentSize=2:tabSize=8:noTabs=true: