diff options
Diffstat (limited to 'wiretap/iseries.c')
-rw-r--r-- | wiretap/iseries.c | 1108 |
1 files changed, 1108 insertions, 0 deletions
diff --git a/wiretap/iseries.c b/wiretap/iseries.c new file mode 100644 index 00000000..180ed01a --- /dev/null +++ b/wiretap/iseries.c @@ -0,0 +1,1108 @@ +/* iseries.c + * + * Wiretap Library + * Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com> + * + * Based on toshiba.c and vms.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * This module will read the contents of the iSeries (OS/400) Communication trace + * Both ASCII & Unicode (little-endian UCS-2) formatted traces are supported. + * + * iSeries Comms traces consist of a header page and a subsequent number of packet records + * + * The header page contains details on the options set during running of the trace, + * currently the following options are a requirement for this module: + * + * 1. Object protocol = ETHERNET (Default) + * 2. ASCII or Unicode file formats. + * + * The above can be achieved by passing option ASCII(*YES) with the trace command + * + */ + +/* iSeries header page + + COMMUNICATIONS TRACE Title: OS400 - OS400 trace 10/28/05 11:44:50 Page: 1 + Trace Description . . . . . : OS400 - OS400 trace + Configuration object . . . . : ETH0 + Type . . . . . . . . . . . . : 1 1=Line, 2=Network Interface + 3=Network server + Object protocol . . . . . . : ETHERNET + Start date/Time . . . . . . : 10/28/05 11:43:00.341 + End date/Time . . . . . . . : 10/28/05 11:44:22.148 + Bytes collected . . . . . . : 11999 + Buffer size . . . . . . . . : 2048 kilobytes + Data direction . . . . . . . : 3 1=Sent, 2=Received, 3=Both + Stop on buffer full . . . . : Y Y=Yes, N=No + Number of bytes to trace + Beginning bytes . . . . . : *MAX Value, *CALC, *MAX + Ending bytes . . . . . . : *CALC Value, *CALC + Controller name . . . . . . : *ALL *ALL, name + Data representation . . . . : 1 1=ASCII, 2=EBCDIC, 3=*CALC + Format SNA data only . . . . : N Y=Yes, N=No + Format RR, RNR commands . . : N Y=Yes, N=No + Format TCP/IP data only . . : Y Y=Yes, N=No + IP address . . . . . . . . : *ALL *ALL, address + IP address . . . . . . . . : *ALL *ALL, address + IP port . . . . . . . . . : *ALL *ALL, IP port + Format UI data only . . . . : N Y=Yes, N=No + Select Ethernet data . . . . : 3 1=802.3, 2=ETHV2, 3=Both + Format Broadcast data . . . : Y Y=Yes, N=No +*/ + +/* iSeries IPv4 formatted packet records consist of a packet header line + * identifying the packet number, direction, size, timestamp, + * source/destination MAC addresses and packet type. + * + * Thereafter there will be a formatted display of the headers above + * the link layer, such as ARP, IP, TCP, UDP, and ICMP (all but + * ICMP have either been seen in captures or on pages such as the ones + * at + * + * http://www-912.ibm.com/s_dir/SLKBase.nsf/1ac66549a21402188625680b0002037e/e05fb0515bc3449686256ce600512c37?OpenDocument + * + * and + * + * 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 + * + * so we cannot assume that "IP Header" or "TCP Header" will appear). The + * formatted display includes lines that show the contents of some of the + * fields in the header, as well as hex strings dumps of the headers + * themselves, with tags such as "IP Header :", "ARP Header :", + * "TCP Header :", "UDP Header :", and (presumably) "ICMP Header:". + * + * If the packet contains data this is displayed as 4 groups of 16 hex digits + * followed by an ASCII representation of the data line. + * + * Information from the packet header line, higher-level headers and, if + * available, data lines are extracted by the module for displaying. + * + * + Record Data Record Controller Destination Source Frame + Number S/R Length Timer Name MAC Address MAC Address Format + ------ --- ------ --------------- ---------- ------------ ------------ ------ + 8 S 145 11:43:59.82956 0006299C14AE 0006299C14FE ETHV2 Type: 0800 + Frame Type : IP DSCP: 0 ECN: 00-NECT Length: 145 Protocol: TCP Datagram ID: 388B + Src Addr: 10.20.144.150 Dest Addr: 10.20.144.151 Fragment Flags: DON'T,LAST + IP Header : 45000091388B40004006CC860A1490960A149097 + IP Options : NONE + TCP . . . : Src Port: 6006,Unassigned Dest Port: 35366,Unassigned + SEQ Number: 2666470699 ('9EEF1D2B'X) ACK Number: 2142147535 ('7FAE93CF'X) + Code Bits: ACK PSH Window: 32648 TCP Option: NO OP + TCP Header : 17768A269EEF1D2B7FAE93CF80187F885B5600000101080A0517E0F805166DE0 + Data . . . . . : 5443503200020010 0000004980000000 B800000080470103 01001E0000002000 *TCP2.......I*...*...*G........ .* + 002F010080000004 0300800700C00600 4002008000000304 00800000060FB067 *./..*.....*..*..@..*.....*....*G* + FC276228786B3EB0 EF34F5F1D27EF8DF 20926820E7B322AA 739F1FB20D **'B(XK>**4***.** *H **"*S*.*. * +*/ + +/* iSeries IPv6 formatted traces are similar to the IPv4 version above, + * except that the higher-level headers have "IPv6 Header:" and + * "ICMPv6 Hdr:", and data is no longer output in groups of 16 hex + * digits. + * + +Record Data Record Destination Source Frame +Number S/R Length Timer MAC Address MAC Address Format +------ --- ------ ------------ ------------ ------------ ------ + 218 S 1488 15:01:14.389 0011BC358680 00096B6BD918 ETHV2 Type: 86DD + IPv6 Data: Ver: 06 Traffic Class: 00 Flow Label: 000000 + Payload Length: 1448 Next Header: 06,TCP Hop Limit: 64 + Src Addr: fd00:0:0:20f2::122 + Dest Addr: fd00:0:0:20a0::155 + IPv6 Header: 6000000005A80640FD000000000020F20000000000000122FD000000000020A0 + 0000000000000155 + TCP . . . : Src Port: 21246,Unassigned Dest Port: 13601,Unassigned + SEQ Number: 2282300877 ('880925CD'X) ACK Number: 3259003715 ('C2407343'X) + Code Bits: ACK Window: 65535 TCP Option: NO OP + TCP Header : 52FE3521880925CDC24073438010FFFFCFBB00000101080A0E15127000237A08 + Data . . . . . : 54435032000200140000061880000000ECBEB867F0000000004CE640E6C1D9D5 *TCP2........*...***g*....L*@***** + C9D5C740E3C8C9E240C9E240E3C8C540E6C1D9D5C9D5C740C6C9C5D3C4404040 ****@****@**@***@*******@*****@@@* + 4040404040404040404040404040404040404040404040404040404040404040 *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* +*/ + +/* iSeries unformatted packet record consist of the same header record as + * the formatted trace but all other records are simply unformatted data + * containing higher-level headers and packet data combined. + * + Record Data Record Controller Destination Source Frame Number Number Poll/ + Number S/R Length Timer Name MAC Address MAC Address Format Command Sent Received Final DSAP SSAP + ------ --- ------ --------------- ---------- ------------ ------------ ------ ------- ------ -------- ----- ---- ---- + 1 R 64 12:19:29.97108 000629ECF48E 0006D78E23C2 ETHV2 Type: 0800 + Data . . . . . : 4500003C27954000 3A06CE3D9797440F 0A5964EAC4F50554 58C9915500000000 *E..<'*@.:.*=**D..YD***.TX**U....* + A00216D06A200000 020405B40402080A 1104B6C000000000 010303000B443BF1 **..*J .....*......**.........D;** +*/ + +#include "config.h" +#include "wtap-int.h" +#include "iseries.h" +#include "file_wrappers.h" + +#include <stdlib.h> +#include <string.h> + +#include <wsutil/str_util.h> +#include <wsutil/strtoi.h> +#include <wsutil/ws_assert.h> + +#define ISERIES_LINE_LENGTH 270 +#define ISERIES_HDR_LINES_TO_CHECK 100 +#define ISERIES_PKT_LINES_TO_CHECK 4 +#define ISERIES_MAX_TRACE_LEN 99999999 +#define ISERIES_FORMAT_ASCII 1 +#define ISERIES_FORMAT_UNICODE 2 + +/* + * Magic strings - "COMMUNICATIONS TRACE", in ASCII and little-endian UCS-2. + */ +static const char iseries_hdr_magic_ascii[] = { + 'C', 'O', 'M', 'M', + 'U', 'N', 'I', 'C', + 'A', 'T', 'I', 'O', + 'N', 'S', ' ', 'T', + 'R', 'A', 'C', 'E' +}; +static const char iseries_hdr_magic_le_ucs_2[] = { + 'C', 0x0, 'O', 0x0, 'M', 0x0, 'M', 0x0, + 'U', 0x0, 'N', 0x0, 'I', 0x0, 'C', 0x0, + 'A', 0x0, 'T', 0x0, 'I', 0x0, 'O', 0x0, + 'N', 0x0, 'S', 0x0, ' ', 0x0, 'T', 0x0, + 'R', 0x0, 'A', 0x0, 'C', 0x0, 'E', 0x0 +}; + +typedef struct { + gboolean have_date; /* TRUE if we found a capture start date */ + int year, month, day; /* The start date */ + int format; /* Trace format type */ +} iseries_t; + +static gboolean iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf, + int *err, gchar ** err_info, gint64 *data_offset); +static gboolean iseries_seek_read (wtap * wth, gint64 seek_off, + wtap_rec *rec, + Buffer * buf, int *err, gchar ** err_info); +static gboolean iseries_check_file_type (wtap * wth, int *err, gchar **err_info, + int format); +static gint64 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info); +static gboolean iseries_parse_packet (wtap * wth, FILE_T fh, + wtap_rec *rec, + Buffer * buf, int *err, gchar ** err_info); +static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes); +static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf, + size_t len); + +static int iseries_file_type_subtype = -1; +static int iseries_unicode_file_type_subtype = -1; + +void register_iseries(void); + +/* + * XXX - it would probably be cleaner to use a UCS-2 flavor of file_gets(), + * rather than file_gets(), if we're reading a UCS-2 file. + */ +wtap_open_return_val +iseries_open (wtap * wth, int *err, gchar ** err_info) +{ + gint offset; + char magic[ISERIES_LINE_LENGTH]; + + /* + * Check that file starts with a valid iSeries COMMS TRACE header + * by scanning for it in the first line + */ + if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info)) + { + if (*err != WTAP_ERR_SHORT_READ) + return WTAP_OPEN_ERROR; + return WTAP_OPEN_NOT_MINE; + } + + /* + * Check if this is a little-endian UCS-2 Unicode formatted file by scanning + * for the magic string + */ + offset=0; + while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2))) + { + if (memcmp (magic + offset, iseries_hdr_magic_le_ucs_2, sizeof iseries_hdr_magic_le_ucs_2) == 0) { + if (file_seek (wth->fh, 0, SEEK_SET, err) == -1) + { + return WTAP_OPEN_ERROR; + } + /* + * Do some basic sanity checking to ensure we can handle the + * contents of this trace + */ + if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE)) + { + if (*err == 0) + return WTAP_OPEN_NOT_MINE; + else + return WTAP_OPEN_ERROR; + } + + wth->file_encap = WTAP_ENCAP_ETHERNET; + wth->file_type_subtype = iseries_unicode_file_type_subtype; + wth->snapshot_length = 0; + wth->subtype_read = iseries_read; + wth->subtype_seek_read = iseries_seek_read; + wth->file_tsprec = WTAP_TSPREC_USEC; + + if (file_seek (wth->fh, 0, SEEK_SET, err) == -1) + { + return WTAP_OPEN_ERROR; + } + + /* + * Add an IDB; we don't know how many interfaces were + * involved, so we just say one interface, about which + * we only know the link-layer type, snapshot length, + * and time stamp resolution. + */ + wtap_add_generated_idb(wth); + + return WTAP_OPEN_MINE; + } + offset += 1; + } + + /* + * Check if this is a ASCII formatted file by scanning for the magic string + */ + offset=0; + while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii)) + { + if (memcmp (magic + offset, iseries_hdr_magic_ascii, sizeof iseries_hdr_magic_ascii) == 0) + { + if (file_seek (wth->fh, 0, SEEK_SET, err) == -1) + { + return WTAP_OPEN_ERROR; + } + /* + * Do some basic sanity checking to ensure we can handle the + * contents of this trace + */ + if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII)) + { + if (*err == 0) + return WTAP_OPEN_NOT_MINE; + else + return WTAP_OPEN_ERROR; + } + + wth->file_encap = WTAP_ENCAP_ETHERNET; + wth->file_type_subtype = iseries_file_type_subtype; + wth->snapshot_length = 0; + wth->subtype_read = iseries_read; + wth->subtype_seek_read = iseries_seek_read; + wth->file_tsprec = WTAP_TSPREC_USEC; + + if (file_seek (wth->fh, 0, SEEK_SET, err) == -1) + { + return WTAP_OPEN_ERROR; + } + + /* + * Add an IDB; we don't know how many interfaces were + * involved, so we just say one interface, about which + * we only know the link-layer type, snapshot length, + * and time stamp resolution. + */ + wtap_add_generated_idb(wth); + + return WTAP_OPEN_MINE; + } + offset += 1; + } + + /* Neither ASCII or UNICODE so not supported */ + return WTAP_OPEN_NOT_MINE; + } + +/* + * Do some basic sanity checking to ensure we can handle the + * contents of this trace by checking the header page for + * requisite requirements and additional information. + */ +static gboolean +iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format) +{ + gboolean is_iseries = FALSE; + guint line; + int num_items_scanned; + char buf[ISERIES_LINE_LENGTH], protocol[9]; + iseries_t *iseries; + + /* Save trace format for passing between packets */ + iseries = g_new(iseries_t, 1); + iseries->have_date = FALSE; + iseries->format = format; + + for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++) + { + memset(buf, 0x0, sizeof(buf)); + if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL) + { + /* EOF or error. */ + *err = file_error (wth->fh, err_info); + if (*err == WTAP_ERR_SHORT_READ) + *err = 0; + break; + } + + /* + * Check that we are dealing with an ETHERNET trace + */ + if (iseries->format == ISERIES_FORMAT_UNICODE) + { + iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH); + } + ascii_strup_inplace (buf); + num_items_scanned = sscanf (buf, + "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s", + protocol); + if (num_items_scanned == 1) + { + if (memcmp (protocol, "ETHERNET", 8) == 0) + { + *err = 0; + is_iseries = TRUE; + } + } + + /* + * The header is the only place where the date part of the timestamp is held, so + * extract it here and store for all packets to access + */ + num_items_scanned = sscanf (buf, + "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d", + &iseries->month, &iseries->day, + &iseries->year); + if (num_items_scanned == 3) + { + iseries->have_date = TRUE; + } + } + + if (is_iseries) + wth->priv = (void *) iseries; + else + g_free(iseries); + + return is_iseries; +} + +/* + * Find the next packet and parse it; called from wtap_read(). + */ +static gboolean +iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf, int *err, + gchar ** err_info, gint64 *data_offset) +{ + gint64 offset; + + /* + * Locate the next packet + */ + offset = iseries_seek_next_packet (wth, err, err_info); + if (offset < 0) + return FALSE; + *data_offset = offset; + + /* + * Parse the packet and extract the various fields + */ + return iseries_parse_packet (wth, wth->fh, rec, buf, err, err_info); +} + +/* + * Seeks to the beginning of the next packet, and returns the + * byte offset. Returns -1 on failure or EOF; on EOF, sets + * *err to 0, and, on failure, sets *err to the error and *err_info + * to null or an additional error string. + */ +static gint64 +iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info) +{ + iseries_t *iseries = (iseries_t *)wth->priv; + char buf[ISERIES_LINE_LENGTH],type[5]; + int line, num_items_scanned; + gint64 cur_off; + long buflen; + + for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++) + { + if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL) + { + /* EOF or error. */ + *err = file_error (wth->fh, err_info); + return -1; + } + /* Convert UNICODE to ASCII if required and determine */ + /* the number of bytes to rewind to beginning of record. */ + if (iseries->format == ISERIES_FORMAT_UNICODE) + { + /* buflen is #bytes to 1st 0x0A */ + buflen = iseries_UNICODE_to_ASCII ((guint8 *) buf, ISERIES_LINE_LENGTH); + } + else + { + /* Else buflen is just length of the ASCII string */ + buflen = (long) strlen (buf); + } + ascii_strup_inplace (buf); + /* Check we have enough data in the line */ + if (buflen < 78) + { + continue; + } + /* If packet header found return the offset */ + num_items_scanned = + sscanf (buf+78, + "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type); + if (num_items_scanned == 1) + { + /* Rewind to beginning of line */ + cur_off = file_tell (wth->fh); + if (cur_off == -1) + { + *err = file_error (wth->fh, err_info); + return -1; + } + if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1) + { + return -1; + } + return cur_off - buflen; + } + } + + *err = WTAP_ERR_BAD_FILE; + *err_info = + ws_strdup_printf ("iseries: next packet header not found within %d lines", + ISERIES_MAX_TRACE_LEN); + return -1; +} + +/* + * Read packets in random-access fashion + */ +static gboolean +iseries_seek_read (wtap * wth, gint64 seek_off, wtap_rec *rec, + Buffer * buf, int *err, gchar ** err_info) +{ + + /* seek to packet location */ + if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1) + return FALSE; + + /* + * Parse the packet and extract the various fields + */ + return iseries_parse_packet (wth, wth->random_fh, rec, buf, + err, err_info); +} + +static int +append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset, + char *data, int *err, gchar **err_info) +{ + int in_offset, out_offset; + int c; + unsigned int i; + gboolean overflow = FALSE; + + in_offset = 0; + out_offset = ascii_offset; + for (;;) + { + /* + * Process a block of up to 16 hex digits. + * The block is terminated early by an end-of-line indication (NUL, + * CR, or LF), by a space (which terminates the last block of the + * data we're processing), or by a "*", which introduces the ASCII representation + * of the data. + * All characters in the block must be upper-case hex digits; + * there might or might not be a space *after* a block, but, if so, + * that will be skipped over after the block is processed. + */ + for (i = 0; i < 16; i++, in_offset++) + { + /* + * If we see an end-of-line indication, or an early-end-of-block + * indication (space), we're done. (Only the last block ends + * early.) + */ + c = data[in_offset] & 0xFF; + if (c == '\0' || c == ' ' || c == '*' || c == '\r' || c == '\n') + { + goto done; + } + if (!g_ascii_isxdigit(c) || g_ascii_islower(c)) + { + /* + * Not a hex digit, or a lower-case hex digit. + * Treat this as an indication that the line isn't a data + * line, so we just ignore it. + * + * XXX - do so only for continuation lines; treat non-hex-digit + * characters as errors for other lines? + */ + return ascii_offset; /* pretend we appended nothing */ + } + if (out_offset >= max_offset) + overflow = TRUE; + else + { + ascii_buf[out_offset] = c; + out_offset++; + } + } + /* + * Skip blanks, if any. + */ + for (; (data[in_offset] & 0xFF) == ' '; in_offset++) + ; + } +done: + /* + * If we processed an *odd* number of hex digits, report an error. + */ + if ((i % 2) != 0) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("iseries: odd number of hex digits in a line"); + return -1; + } + if (overflow) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("iseries: more packet data than the packet length indicated"); + return -1; + } + return out_offset; +} + +/* return the multiplier for nanoseconds */ +static guint32 +csec_multiplier(guint32 csec) +{ + if (csec < 10) return 100000000; + if (csec < 100) return 10000000; + if (csec < 1000) return 1000000; + if (csec < 10000) return 100000; + if (csec < 100000) return 10000; + if (csec < 1000000) return 1000; + if (csec < 10000000) return 100; + if (csec < 100000000) return 10; + return 1; +} + +/* Parses a packet. */ +static gboolean +iseries_parse_packet (wtap * wth, FILE_T fh, wtap_rec *rec, + Buffer *buf, int *err, gchar **err_info) +{ + iseries_t *iseries = (iseries_t *)wth->priv; + gint64 cur_off; + gboolean isValid, isCurrentPacket; + int num_items_scanned, line, pktline, buflen; + int pkt_len, pktnum, hr, min, sec; + char direction[2], destmac[13], srcmac[13], type[5]; + guint32 csec; + char data[ISERIES_LINE_LENGTH * 2]; + int offset; + char *ascii_buf; + int ascii_offset; + struct tm tm; + + /* + * Check for packet headers in first 3 lines this should handle page breaks + * situations and the header lines output at each page throw and ensure we + * read both the captured and packet lengths. + */ + isValid = FALSE; + for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++) + { + if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL) + { + *err = file_error (fh, err_info); + return FALSE; + } + /* Convert UNICODE data to ASCII */ + if (iseries->format == ISERIES_FORMAT_UNICODE) + { + iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH); + } + ascii_strup_inplace (data); + num_items_scanned = + sscanf (data, + "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9u%*[ \n\t]" + "%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s", + &pktnum, direction, &pkt_len, &hr, &min, &sec, &csec, destmac, + srcmac, type); + if (num_items_scanned == 10) + { + if (pktnum < 0) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a negative packet number"); + return FALSE; + } + + if (pkt_len < 0) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a negative packet length"); + return FALSE; + } + + if (hr < 0) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a negative hour in the time stamp"); + return FALSE; + } + + if (hr > 23) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a hour in the time stamp greater than 23"); + return FALSE; + } + + if (min < 0) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a negative minute in the time stamp"); + return FALSE; + } + + if (min > 59) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a minute in the time stamp greater than 59"); + return FALSE; + } + + if (sec < 0) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a negative second in the time stamp"); + return FALSE; + } + + /* + * Yes, 60, even though the time-conversion routines on most OSes + * might not handle leap seconds. + */ + if (sec > 60) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a second in the time stamp greater than 60"); + return FALSE; + } + + if (strlen(destmac) != 12) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a destination MAC address shorter than 6 bytes"); + return FALSE; + } + + if (strlen(srcmac) != 12) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has a source MAC address shorter than 6 bytes"); + return FALSE; + } + + if (strlen(type) != 4) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header has an Ethernet type/length field than 2 bytes"); + return FALSE; + } + + /* OK! We found the packet header line */ + isValid = TRUE; + /* + * XXX - The Capture length returned by the iSeries trace doesn't + * seem to include the Ethernet header, so we add its length here. + * + * Check the length first, just in case it's *so* big that, after + * adding the Ethernet header length, it overflows. + */ + if ((guint)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD - 14) + { + /* + * Probably a corrupt capture file; don't blow up trying + * to allocate space for an immensely-large packet, and + * don't think it's a really *small* packet because it + * overflowed. (Calculate the size as a 64-bit value in + * the error message, to avoid an overflow.) + */ + *err = WTAP_ERR_BAD_FILE; + *err_info = ws_strdup_printf("iseries: File has %" PRIu64 "-byte packet, bigger than maximum of %u", + (guint64)pkt_len + 14, + WTAP_MAX_PACKET_SIZE_STANDARD); + return FALSE; + } + pkt_len += 14; + break; + } + } + + /* + * If no packet header found we exit at this point and inform the user. + */ + if (!isValid) + { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup ("iseries: packet header isn't valid"); + return FALSE; + } + + rec->rec_type = REC_TYPE_PACKET; + rec->block = wtap_block_create(WTAP_BLOCK_PACKET); + rec->presence_flags = WTAP_HAS_CAP_LEN; + + /* + * If we have Wiretap Header then populate it here + * + * Timer resolution on the iSeries is hardware dependent. We determine + * the resolution based on how many digits we see. + */ + if (iseries->have_date) + { + rec->presence_flags |= WTAP_HAS_TS; + tm.tm_year = 100 + iseries->year; + tm.tm_mon = iseries->month - 1; + tm.tm_mday = iseries->day; + tm.tm_hour = hr; + tm.tm_min = min; + tm.tm_sec = sec; + tm.tm_isdst = -1; + rec->ts.secs = mktime (&tm); + rec->ts.nsecs = csec * csec_multiplier(csec); + } + + rec->rec_header.packet_header.len = pkt_len; + rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET; + rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1; + + /* + * Allocate a buffer big enough to hold the claimed packet length + * worth of byte values; each byte will be two hex digits, so the + * buffer's size should be twice the packet length. + * + * (There is no need to null-terminate the buffer.) + */ + ascii_buf = (char *)g_malloc (pkt_len*2); + ascii_offset = 0; + + /* + * Copy in the Ethernet header. + * + * The three fields have already been checked to have the right length + * (6 bytes, hence 12 characters, of hex-dump destination and source + * addresses, and 2 bytes, hence 4 characters, of hex-dump type/length). + * + * pkt_len is guaranteed to be >= 14, so 2*pkt_len is guaranteed to be + * >= 28, so we don't need to do any bounds checking. + */ + memcpy(&ascii_buf[0], destmac, 12); + ascii_offset += 12; + memcpy(&ascii_buf[12], srcmac, 12); + ascii_offset += 12; + memcpy(&ascii_buf[24], type, 4); + ascii_offset += 4; + + /* + * Start reading packet contents + */ + isCurrentPacket = TRUE; + + /* loop through packet lines and breakout when the next packet header is read */ + pktline = 0; + while (isCurrentPacket) + { + pktline++; + /* Read the next line */ + if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL) + { + *err = file_error (fh, err_info); + if (*err == 0) + { + /* Hit the EOF without an error */ + break; + } + goto errxit; + } + + /* Convert UNICODE data to ASCII and determine line length */ + if (iseries->format == ISERIES_FORMAT_UNICODE) + { + buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH); + } + else + { + /* Else bytes to rewind is just length of ASCII string */ + buflen = (int) strlen (data); + } + + /* + * Skip leading white space. + */ + for (offset = 0; g_ascii_isspace(data[offset]); offset++) + ; + + /* + * The higher-level header information starts at an offset of + * 22 characters. The header tags are 14 characters long. + * + * XXX - for IPv6, if the next header isn't the last header, + * the intermediate headers do *NOT* appear to be shown in + * the dump file *at all*, so the packet *cannot* be + * reconstructed! + */ + if (offset == 22) + { + if (strncmp(data + 22, "IP Header : ", 14) == 0 || + strncmp(data + 22, "IPv6 Header: ", 14) == 0 || + strncmp(data + 22, "ARP Header : ", 14) == 0 || + strncmp(data + 22, "TCP Header : ", 14) == 0 || + strncmp(data + 22, "UDP Header : ", 14) == 0 || + strncmp(data + 22, "ICMP Header: ", 14) == 0 || + strncmp(data + 22, "ICMPv6 Hdr: ", 14) == 0 || + strncmp(data + 22, "Option Hdr: ", 14) == 0) + { + ascii_offset = append_hex_digits(ascii_buf, ascii_offset, + pkt_len*2, + data + 22 + 14, err, + err_info); + if (ascii_offset == -1) + { + /* Bad line. */ + return FALSE; + } + continue; + } + } + + /* + * Is this a data line? + * + * The "Data" starts at an offset of 8. + */ + if (offset == 9) + { + if (strncmp(data + 9, "Data . . . . . : ", 18) == 0) + { + ascii_offset = append_hex_digits(ascii_buf, ascii_offset, + pkt_len*2, + data + 9 + 18, err, + err_info); + if (ascii_offset == -1) + { + /* Bad line. */ + return FALSE; + } + continue; + } + } + + /* + * Is this a continuation of a previous header or data line? + * That's blanks followed by hex digits; first try the + * "no column separators" form. + * + * Continuations of header lines begin at an offset of 36; + * continuations of data lines begin at an offset of 27. + */ + if (offset == 36 || offset == 27) + { + ascii_offset = append_hex_digits(ascii_buf, ascii_offset, + pkt_len*2, + data + offset, err, + err_info); + if (ascii_offset == -1) + { + /* Bad line. */ + return FALSE; + } + continue; + } + + /* + * If we see the identifier for the next packet then rewind and set + * isCurrentPacket FALSE + */ + ascii_strup_inplace (data); + /* If packet header found return the offset */ + num_items_scanned = + sscanf (data+78, + "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type); + if ((num_items_scanned == 1) && pktline > 1) + { + isCurrentPacket = FALSE; + cur_off = file_tell( fh); + if (cur_off == -1) + { + /* Error. */ + *err = file_error (fh, err_info); + goto errxit; + } + if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1) + { + /* XXX: need to set err_info ?? */ + goto errxit; + } + } + } + + /* + * Make the captured length be the amount of bytes we've read (which + * is half the number of characters of hex dump we have). + * + * XXX - this can happen for IPv6 packets if the next header isn't the + * last header. + */ + rec->rec_header.packet_header.caplen = ((guint32) ascii_offset)/2; + + /* Make sure we have enough room for the packet. */ + ws_buffer_assure_space (buf, rec->rec_header.packet_header.caplen); + /* Convert ascii data to binary and return in the frame buffer */ + iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), ascii_offset); + + /* free buffer allocs and return */ + *err = 0; + g_free (ascii_buf); + return TRUE; + +errxit: + g_free (ascii_buf); + return FALSE; +} + +/* + * Simple routine to convert an UNICODE buffer to ASCII + * + * XXX - This may be possible with iconv or similar + */ +static int +iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes) +{ + guint i; + guint8 *bufptr; + + bufptr = buf; + + for (i = 0; i < bytes; i++) + { + switch (buf[i]) + { + case 0xFE: + case 0xFF: + case 0x00: + break; + default: + *bufptr = buf[i]; + bufptr++; + } + if (buf[i] == 0x0A) + break; + } + ws_assert(bufptr < buf + bytes); + *bufptr = '\0'; + return i; +} + +/* + * Simple routine to convert an ASCII hex string to binary data + * Requires ASCII hex data and buffer to populate with binary data + */ +static gboolean +iseries_parse_hex_string (const char * ascii, guint8 * buf, size_t len) +{ + size_t i; + int byte; + gint hexvalue; + guint8 bytevalue; + + byte = 0; + for (i = 0; i < len; i++) + { + hexvalue = g_ascii_xdigit_value(ascii[i]); + i++; + if (hexvalue == -1) + return FALSE; /* not a valid hex digit */ + bytevalue = (guint8)(hexvalue << 4); + if (i >= len) + return FALSE; /* only one hex digit of the byte is present */ + hexvalue = g_ascii_xdigit_value(ascii[i]); + if (hexvalue == -1) + return FALSE; /* not a valid hex digit */ + bytevalue |= (guint8) hexvalue; + buf[byte] = bytevalue; + byte++; + } + return TRUE; +} + +static const struct supported_block_type iseries_blocks_supported[] = { + /* + * We support packet blocks, with no comments or other options. + */ + { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } +}; + +static const struct file_type_subtype_info iseries_info = { + "IBM iSeries comm. trace (ASCII)", "iseries_ascii", "txt", NULL, + FALSE, BLOCKS_SUPPORTED(iseries_blocks_supported), + NULL, NULL, NULL +}; + +static const struct supported_block_type iseries_unicode_blocks_supported[] = { + /* + * We support packet blocks, with no comments or other options. + */ + { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } +}; + +static const struct file_type_subtype_info iseries_unicode_info = { + "IBM iSeries comm. trace (Unicode)", "iseries_unicode", "txt", NULL, + FALSE, BLOCKS_SUPPORTED(iseries_unicode_blocks_supported), + NULL, NULL, NULL +}; + +void register_iseries(void) +{ + iseries_file_type_subtype = wtap_register_file_type_subtype(&iseries_info); + iseries_unicode_file_type_subtype = wtap_register_file_type_subtype(&iseries_unicode_info); + + /* + * Register names for backwards compatibility with the + * wtap_filetypes table in Lua. + */ + wtap_register_backwards_compatibility_lua_name("ISERIES", + iseries_file_type_subtype); + wtap_register_backwards_compatibility_lua_name("ISERIES_UNICODE", + iseries_unicode_file_type_subtype); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=2 tabstop=8 expandtab: + * :indentSize=2:tabSize=8:noTabs=true: + */ |