/* * Do some basic sanity checking to ensure we can handle the * contents of this trace by checking the header page for * requisit requirements and additional information. */ static gboolean iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format) { guint line; int num_items_scanned; char buf[ISERIES_LINE_LENGTH], protocol[9]; iseries_t *iseries; /* Save trace format for passing between packets */ iseries = (iseries_t *) g_malloc (sizeof (iseries_t)); wth->priv = (void *) iseries; iseries->have_date = FALSE; iseries->format = format; for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++) { 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; return FALSE; } /* * 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) return FALSE; } /* * 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; } } *err = 0; return TRUE; }
/* * 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); /* 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 = g_strdup_printf ("iseries: next packet header not found within %d lines", ISERIES_MAX_TRACE_LEN); return -1; }
/* Parses a packet. */ static gboolean iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr, 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], csec[9+1]; 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.%9[0-9]%*[ \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) { /* 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. */ 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; } phdr->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) { phdr->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; phdr->ts.secs = mktime (&tm); switch (strlen(csec)) { case 0: phdr->ts.nsecs = 0; break; case 1: phdr->ts.nsecs = atoi(csec) * 100000000; break; case 2: phdr->ts.nsecs = atoi(csec) * 10000000; break; case 3: phdr->ts.nsecs = atoi(csec) * 1000000; break; case 4: phdr->ts.nsecs = atoi(csec) * 100000; break; case 5: phdr->ts.nsecs = atoi(csec) * 10000; break; case 6: phdr->ts.nsecs = atoi(csec) * 1000; break; case 7: phdr->ts.nsecs = atoi(csec) * 100; break; case 8: phdr->ts.nsecs = atoi(csec) * 10; break; case 9: phdr->ts.nsecs = atoi(csec); break; } } phdr->len = pkt_len; phdr->pkt_encap = WTAP_ENCAP_ETHERNET; phdr->pseudo_header.eth.fcs_len = -1; ascii_buf = (char *)g_malloc (ISERIES_PKT_ALLOC_SIZE); g_snprintf(ascii_buf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s", destmac, srcmac, type); ascii_offset = 14*2; /* 14-byte Ethernet header, 2 characters per byte */ /* * 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; 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, ISERIES_PKT_ALLOC_SIZE - 1, 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, ISERIES_PKT_ALLOC_SIZE - 1, 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, ISERIES_PKT_ALLOC_SIZE - 1, 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; } } } ascii_buf[ascii_offset] = '\0'; /* * 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. */ phdr->caplen = ((guint32) strlen (ascii_buf))/2; /* Make sure we have enough room for the packet. */ buffer_assure_space (buf, ISERIES_MAX_PACKET_LEN); /* Convert ascii data to binary and return in the frame buffer */ iseries_parse_hex_string (ascii_buf, buffer_start_ptr (buf), strlen (ascii_buf)); /* free buffer allocs and return */ *err = 0; g_free (ascii_buf); return TRUE; errxit: g_free (ascii_buf); return FALSE; }
/* 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 (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 = g_strdup_printf("iseries: File has %" G_GUINT64_FORMAT "-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->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; }