/* iseries.c
- *
- * $Id$
*
* Wiretap Library
* Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com>
/*
* This module will read the contents of the iSeries (OS/400) Communication trace
- * Both ASCII & Unicode formatted traces are supported.
+ * 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
*
* currently the following options are a requirement for this module:
*
* 1. Object protocol = ETHERNET (Default)
- * 2. ASCII or UNICODE file formats.
+ * 2. ASCII or Unicode file formats.
*
* The above can be acheived by passing option ASCII(*YES) with the trace command
*
#include "config.h"
#include "wtap-int.h"
-#include "buffer.h"
#include "iseries.h"
#include "file_wrappers.h"
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
#include <errno.h>
#include <wsutil/str_util.h>
-#define ISERIES_HDR_MAGIC_STR "COMMUNICATIONS TRACE"
-#define ISERIES_HDR_MAGIC_LEN 20
#define ISERIES_LINE_LENGTH 270
#define ISERIES_HDR_LINES_TO_CHECK 100
#define ISERIES_PKT_LINES_TO_CHECK 4
-#define ISERIES_MAX_PACKET_LEN 16384
#define ISERIES_MAX_TRACE_LEN 99999999
-#define ISERIES_PKT_ALLOC_SIZE (pkt_len*2)+1
#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 */
static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
size_t len);
-int
+/*
+ * 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)
{
- int bytes_read;
gint offset;
char magic[ISERIES_LINE_LENGTH];
- char unicodemagic[] =
- { '\x43', '\x00', '\x4F', '\x00', '\x4D',
- '\x00', '\x4D', '\x00', '\x55', '\x00', '\x4E', '\x00', '\x49', '\x00',
- '\x43', '\x00', '\x41'
- };
/*
* Check that file starts with a valid iSeries COMMS TRACE header
* by scanning for it in the first line
*/
- errno = WTAP_ERR_CANT_READ;
- bytes_read = file_read (&magic, sizeof magic, wth->fh);
- if (bytes_read != sizeof magic)
+ if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info))
{
- *err = file_error (wth->fh, err_info);
- if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
- return -1;
- return 0;
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
}
/*
- * Check if this is a UNICODE formatted file by scanning for the magic string
+ * Check if this is a little-endian UCS-2 Unicode formatted file by scanning
+ * for the magic string
*/
offset=0;
- while ((unsigned)offset < (ISERIES_LINE_LENGTH - (sizeof unicodemagic)))
+ while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2)))
{
- if (memcmp (magic + offset, unicodemagic, sizeof unicodemagic) == 0) {
+ 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 0;
+ return WTAP_OPEN_ERROR;
}
/*
* Do some basic sanity checking to ensure we can handle the
if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
{
if (*err == 0)
- return 0;
+ return WTAP_OPEN_NOT_MINE;
else
- return -1;
+ return WTAP_OPEN_ERROR;
}
wth->file_encap = WTAP_ENCAP_ETHERNET;
wth->snapshot_length = 0;
wth->subtype_read = iseries_read;
wth->subtype_seek_read = iseries_seek_read;
- wth->tsprecision = WTAP_FILE_TSPREC_USEC;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
{
- return 0;
+ return WTAP_OPEN_ERROR;
}
- return 1;
+ return WTAP_OPEN_MINE;
}
offset += 1;
}
* Check if this is a ASCII formatted file by scanning for the magic string
*/
offset=0;
- while (offset < (ISERIES_LINE_LENGTH - ISERIES_HDR_MAGIC_LEN))
+ while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii))
{
- if (memcmp (magic + offset, ISERIES_HDR_MAGIC_STR, ISERIES_HDR_MAGIC_LEN) == 0)
+ 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 0;
+ return WTAP_OPEN_ERROR;
}
/*
* Do some basic sanity checking to ensure we can handle the
if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
{
if (*err == 0)
- return 0;
+ return WTAP_OPEN_NOT_MINE;
else
- return -1;
+ return WTAP_OPEN_ERROR;
}
wth->file_encap = WTAP_ENCAP_ETHERNET;
wth->snapshot_length = 0;
wth->subtype_read = iseries_read;
wth->subtype_seek_read = iseries_seek_read;
- wth->tsprecision = WTAP_FILE_TSPREC_USEC;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
{
- return 0;
+ return WTAP_OPEN_ERROR;
}
- return 1;
+ return WTAP_OPEN_MINE;
}
offset += 1;
}
/* Neither ASCII or UNICODE so not supported */
- return 0;
+ return WTAP_OPEN_NOT_MINE;
}
/*
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];
/* 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++)
{
+ 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;
- return FALSE;
+ 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)
- 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)
+ {
+ *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;
- }
+ /*
+ * 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;
+
+ if (is_iseries)
+ wth->priv = (void *) iseries;
+ else
+ g_free(iseries);
+
+ return is_iseries;
}
/*
{
goto done;
}
- if (!isxdigit(c) || islower(c))
+ if (!g_ascii_isxdigit(c) || g_ascii_islower(c))
{
/*
* Not a hex digit, or a lower-case hex digit.
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 - 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);
+ return FALSE;
+ }
pkt_len += 14;
break;
}
return FALSE;
}
+ phdr->rec_type = REC_TYPE_PACKET;
phdr->presence_flags = WTAP_HAS_CAP_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 */
+ /*
+ * 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
/*
* Skip leading white space.
*/
- for (offset = 0; isspace(data[offset]); offset++)
+ for (offset = 0; g_ascii_isspace(data[offset]); offset++)
;
/*
strncmp(data + 22, "Option Hdr: ", 14) == 0)
{
ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
- ISERIES_PKT_ALLOC_SIZE - 1,
+ pkt_len*2,
data + 22 + 14, err,
err_info);
if (ascii_offset == -1)
if (strncmp(data + 9, "Data . . . . . : ", 18) == 0)
{
ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
- ISERIES_PKT_ALLOC_SIZE - 1,
+ pkt_len*2,
data + 9 + 18, err,
err_info);
if (ascii_offset == -1)
if (offset == 36 || offset == 27)
{
ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
- ISERIES_PKT_ALLOC_SIZE - 1,
+ pkt_len*2,
data + offset, err,
err_info);
if (ascii_offset == -1)
}
}
}
- ascii_buf[ascii_offset] = '\0';
/*
* Make the captured length be the amount of bytes we've read (which
* XXX - this can happen for IPv6 packets if the next header isn't the
* last header.
*/
- phdr->caplen = ((guint32) strlen (ascii_buf))/2;
+ phdr->caplen = ((guint32) ascii_offset)/2;
/* Make sure we have enough room for the packet. */
- buffer_assure_space (buf, ISERIES_MAX_PACKET_LEN);
+ ws_buffer_assure_space (buf, phdr->caplen);
/* Convert ascii data to binary and return in the frame buffer */
- iseries_parse_hex_string (ascii_buf, buffer_start_ptr (buf), strlen (ascii_buf));
+ iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), ascii_offset);
/* free buffer allocs and return */
*err = 0;