/* iseries.c * * Wiretap Library * Copyright (c) 2011 by Martin Warnes * * 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 #include #include #include #include #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: */