summaryrefslogtreecommitdiffstats
path: root/wiretap/daintree-sna.c
diff options
context:
space:
mode:
Diffstat (limited to 'wiretap/daintree-sna.c')
-rw-r--r--wiretap/daintree-sna.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/wiretap/daintree-sna.c b/wiretap/daintree-sna.c
new file mode 100644
index 00000000..67ae4444
--- /dev/null
+++ b/wiretap/daintree-sna.c
@@ -0,0 +1,293 @@
+/* daintree_sna.c
+ * Routines for opening .dcf capture files created by Daintree's
+ * Sensor Network Analyzer for 802.15.4 radios
+ * Copyright 2009, Exegin Technologies Limited <fff@exegin.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Started with packetlogger.c as a template, but little packetlogger code
+ * remains. Borrowed many snippets from dbs-etherwatch.c, the
+ * daintree_sna_process_hex_data function having the largest chunk.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module reads capture files saved by Daintree's Sensor Network Analyzer.
+ * Daintree captures are plain text files with a two line header,
+ * followed by packet records, one per line, with whitespace separated fields
+ * consisting of: packet number, time, bytes of capture data, capture data,
+ * unknown, unknown, signal strength?, unknown, etc, and terminated with CRLF.
+ */
+
+/* Example capture file:
+
+#Format=4
+# SNA v2.2.0.4 SUS:20090709 ACT:819705
+1 1233783799.326400 10 030809ffffffff07ffff 42 1 -69 25 2 0 1 32767
+2 1233783799.477440 5 02000bffff 110 1 -44 25 6 0 1 32767
+3 1233783799.809920 5 020013ffff 107 1 -45 25 43 0 1 3276
+
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "daintree-sna.h"
+
+typedef struct daintree_sna_header {
+ guint32 len;
+ guint64 ts;
+} daintree_sna_header_t;
+
+#define DAINTREE_SNA_HEADER_SIZE 2
+#define FCS_LENGTH 2
+
+static const char daintree_magic_text[] = "#Format=";
+
+#define DAINTREE_MAGIC_TEXT_SIZE (sizeof daintree_magic_text - 1)
+#define DAINTREE_MAX_LINE_SIZE 512
+#define READDATA_BUF_SIZE (DAINTREE_MAX_LINE_SIZE/2)
+#define READDATA_MAX_FIELD_SIZE "255" /* DAINTREE_MAX_LINE_SIZE/2 -1 */
+
+#define COMMENT_LINE daintree_magic_text[0]
+
+static gboolean daintree_sna_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+
+static gboolean daintree_sna_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static gboolean daintree_sna_read_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+static int daintree_sna_file_type_subtype = -1;
+
+void register_daintree_sna(void);
+
+/* Open a file and determine if it's a Daintree file */
+wtap_open_return_val daintree_sna_open(wtap *wth, int *err, gchar **err_info)
+{
+ char readLine[DAINTREE_MAX_LINE_SIZE];
+
+ /* get first line of file header */
+ if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh)==NULL) {
+ *err = file_error(wth->fh, err_info);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* check magic text */
+ if (strncmp(readLine, daintree_magic_text, DAINTREE_MAGIC_TEXT_SIZE) != 0)
+ return WTAP_OPEN_NOT_MINE; /* not daintree format */
+
+ /* read second header line */
+ if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh)==NULL) {
+ *err = file_error(wth->fh, err_info);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (readLine[0] != COMMENT_LINE)
+ return WTAP_OPEN_NOT_MINE; /* daintree files have a two line header */
+
+ /* set up the pointers to the handlers for this file type */
+ wth->subtype_read = daintree_sna_read;
+ wth->subtype_seek_read = daintree_sna_seek_read;
+
+ /* set up for file type */
+ wth->file_type_subtype = daintree_sna_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_IEEE802_15_4_NOFCS;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ wth->snapshot_length = 0; /* not available in header */
+
+ /*
+ * 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; /* it's a Daintree file */
+}
+
+/* Read the capture file sequentially
+ * Wireshark scans the file with sequential reads during preview and initial display. */
+static gboolean
+daintree_sna_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* parse that line and the following packet data */
+ return daintree_sna_read_packet(wth->fh, rec, buf, err, err_info);
+}
+
+/* Read the capture file randomly
+ * Wireshark opens the capture file for random access when displaying user-selected packets */
+static gboolean
+daintree_sna_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* parse that line and the following packet data */
+ return daintree_sna_read_packet(wth->random_fh, rec, buf, err,
+ err_info);
+}
+
+/* Read a header line, scan it, and fill in a struct wtap_rec.
+ * Then convert packet data from ASCII hex string to binary in place,
+ * sanity-check its length against what we assume is the packet length field,
+ * and copy it into a Buffer. */
+static gboolean
+daintree_sna_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ guint64 seconds;
+ int useconds;
+ char readLine[DAINTREE_MAX_LINE_SIZE];
+ char readData[READDATA_BUF_SIZE];
+ guchar *str = (guchar *)readData;
+ guint bytes;
+ guint8 *p;
+
+ /* we've only seen file header lines starting with '#', but
+ * if others appear in the file, they are tossed */
+ do {
+ if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ return FALSE; /* all done */
+ }
+ } while (readLine[0] == COMMENT_LINE);
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+
+ if (sscanf(readLine, "%*s %18" SCNu64 ".%9d %9u %" READDATA_MAX_FIELD_SIZE "s",
+ &seconds, &useconds, &rec->rec_header.packet_header.len, readData) != 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("daintree_sna: invalid read record");
+ return FALSE;
+ }
+
+ /* Daintree doesn't store the FCS, but pads end of packet with 0xffff, which we toss */
+ if (rec->rec_header.packet_header.len <= FCS_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("daintree_sna: packet length <= %u bytes, no frame data present",
+ FCS_LENGTH);
+ return FALSE;
+ }
+ rec->rec_header.packet_header.len -= FCS_LENGTH;
+
+ rec->ts.secs = (time_t) seconds;
+ rec->ts.nsecs = useconds * 1000; /* convert mS to nS */
+
+ /*
+ * READDATA_BUF_SIZE is < WTAP_MAX_PACKET_SIZE_STANDARD, and is the maximum
+ * number of bytes of packet data we can generate, so we don't
+ * need to check the packet length.
+ */
+ p = str; /* overlay source buffer */
+ bytes = 0;
+ /* convert hex string to guint8 */
+ while(*str) {
+ /* most significant nibble */
+ if (!g_ascii_isxdigit(*str)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("daintree_sna: non-hex digit in hex data");
+ return FALSE;
+ }
+ if(g_ascii_isdigit(*str)) {
+ *p = (*str - '0') << 4;
+ } else {
+ *p = ((g_ascii_tolower(*str) - 'a') + 10) << 4;
+ }
+ str++;
+
+ /* least significant nibble */
+ if (!g_ascii_isxdigit(*str)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("daintree_sna: non-hex digit in hex data");
+ return FALSE;
+ }
+ if(g_ascii_isdigit(*str)) {
+ *p += *str - '0';
+ } else {
+ *p += (g_ascii_tolower(*str) - 'a') + 10;
+ }
+ str++;
+
+ /* next byte in buffer */
+ p++;
+ bytes++;
+ }
+
+ /* Daintree doesn't store the FCS, but pads end of packet with 0xffff, which we toss */
+ if (bytes <= FCS_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("daintree_sna: Only %u bytes of packet data",
+ bytes);
+ return FALSE;
+ }
+ bytes -= FCS_LENGTH;
+ if (bytes > rec->rec_header.packet_header.len) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("daintree_sna: capture length (%u) > packet length (%u)",
+ bytes, rec->rec_header.packet_header.len);
+ return FALSE;
+ }
+
+ rec->rec_header.packet_header.caplen = bytes;
+
+ ws_buffer_assure_space(buf, bytes);
+ memcpy(ws_buffer_start_ptr(buf), readData, bytes);
+ return TRUE;
+}
+
+static const struct supported_block_type daintree_sna_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 daintree_sna_info = {
+ "Daintree SNA", "dsna", "dcf", NULL,
+ FALSE, BLOCKS_SUPPORTED(daintree_sna_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_daintree_sna(void)
+{
+ daintree_sna_file_type_subtype = wtap_register_file_type_subtype(&daintree_sna_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("DAINTREE_SNA",
+ daintree_sna_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */