summaryrefslogtreecommitdiffstats
path: root/wiretap/dct3trace.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /wiretap/dct3trace.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'wiretap/dct3trace.c')
-rw-r--r--wiretap/dct3trace.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/wiretap/dct3trace.c b/wiretap/dct3trace.c
new file mode 100644
index 00000000..1476247d
--- /dev/null
+++ b/wiretap/dct3trace.c
@@ -0,0 +1,442 @@
+/* dct3trace.c
+ * Routines for reading signalling traces generated by Gammu (www.gammu.org)
+ * from Nokia DCT3 phones in Netmonitor mode.
+ *
+ * gammu --nokiadebug nhm5_587.txt v18-19
+ *
+ * Duncan Salerno <duncan.salerno@googlemail.com>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "dct3trace.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <wsutil/strtoi.h>
+
+/*
+ Example downlink data:
+
+<?xml version="1.0"?>
+<dump>
+<l1 direction="down" logicalchannel="96" physicalchannel="19" sequence="268116" error="0" timeshift="2992" bsic="22" data="31063F100DD0297A53E1020103C802398E0B2B2B2B2B2B" >
+<l2 data="063F100DD0297A53E1020103" rest="C802398E0B2B2B2B2B2B" >
+</l2>
+</l1>
+</dump>
+
+ Example uplink data (no raw L1):
+
+<?xml version="1.0"?>
+<dump>
+<l1 direction="up" logicalchannel="112" >
+<l2 type="U" subtype="Unknown" p="0" data="061500400000000000000000000000000000" >
+</l2>
+</l1>
+</dump>
+
+ */
+
+
+/* Magic text to check */
+static const char dct3trace_magic_line1[] = "<?xml version=\"1.0\"?>";
+static const char dct3trace_magic_line2[] = "<dump>";
+static const char dct3trace_magic_record_start[] = "<l1 ";
+static const char dct3trace_magic_record_end[] = "</l1>";
+static const char dct3trace_magic_l2_start[] = "<l2 ";
+#if 0 /* Not used ?? */
+static const char dct3trace_magic_l2_end[] = "</l2>";
+#endif
+static const char dct3trace_magic_end[] = "</dump>";
+
+#define MAX_PACKET_LEN 23
+
+static gboolean dct3trace_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+static gboolean dct3trace_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static int dct3trace_file_type_subtype = -1;
+
+void register_dct3trace(void);
+
+/*
+ * Following 3 functions taken from gsmdecode-0.7bis, with permission:
+ *
+ * https://web.archive.org/web/20091218112927/http://wiki.thc.org/gsm
+ */
+
+static int
+hc2b(unsigned char hex)
+{
+ hex = g_ascii_tolower(hex);
+ if ((hex >= '0') && (hex <= '9'))
+ return hex - '0';
+ if ((hex >= 'a') && (hex <= 'f'))
+ return hex - 'a' + 10;
+ return -1;
+}
+
+static int
+hex2bin(guint8 *out, guint8 *out_end, char *in)
+{
+ guint8 *out_start = out;
+ int is_low = 0;
+ int c;
+
+ while (*in != '\0')
+ {
+ c = hc2b(*(unsigned char *)in);
+ if (c < 0)
+ {
+ in++;
+ continue;
+ }
+ if (out == out_end)
+ {
+ /* Too much data */
+ return -1;
+ }
+ if (is_low == 0)
+ {
+ *out = c << 4;
+ is_low = 1;
+ } else {
+ *out |= (c & 0x0f);
+ is_low = 0;
+ out++;
+ }
+ in++;
+ }
+
+ return (int)(out - out_start);
+}
+
+static gboolean
+xml_get_int(int *val, const char *str, const char *pattern, int *err, gchar **err_info)
+{
+ const char *ptr, *endptr;
+ char *start, *end;
+ char buf[32];
+
+ ptr = strstr(str, pattern);
+ if (ptr == NULL) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: %s not found", pattern);
+ return FALSE;
+ }
+ /*
+ * XXX - should we just skip past the pattern and check for ="?
+ */
+ start = strchr(ptr, '"');
+ if (start == NULL) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: opening quote for %s not found", pattern);
+ return FALSE;
+ }
+ start++;
+ /*
+ * XXX - should we just use ws_strtoi32() and check whether
+ * the character following the number is a "?
+ */
+ end = strchr(start, '"');
+ if (end == NULL) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: closing quote for %s not found", pattern);
+ return FALSE;
+ }
+ if (end - start > 31) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: %s value is too long", pattern);
+ return FALSE;
+ }
+
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ /*
+ * XXX - should we allow negative numbers in all cases? Or are
+ * there cases where the number is unsigned?
+ */
+ if (!ws_strtoi32(buf, &endptr, val)) {
+ *err = WTAP_ERR_BAD_FILE;
+ if (errno == ERANGE) {
+ if (*val < 0)
+ *err_info = ws_strdup_printf("dct3trace: %s value is too small, minimum is %d", pattern, *val);
+ else
+ *err_info = ws_strdup_printf("dct3trace: %s value is too large, maximum is %d", pattern, *val);
+ } else
+ *err_info = ws_strdup_printf("dct3trace: %s value \"%s\" not a number", pattern, buf);
+ return FALSE;
+ }
+ if (*endptr != '\0') {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: %s value \"%s\" not a number", pattern, buf);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+wtap_open_return_val dct3trace_open(wtap *wth, int *err, gchar **err_info)
+{
+ char line1[64], line2[64];
+
+ /* Look for Gammu DCT3 trace header */
+ if (file_gets(line1, sizeof(line1), wth->fh) == NULL ||
+ file_gets(line2, sizeof(line2), 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;
+ }
+
+ /* Don't compare line endings */
+ if( strncmp(dct3trace_magic_line1, line1, strlen(dct3trace_magic_line1)) != 0 ||
+ strncmp(dct3trace_magic_line2, line2, strlen(dct3trace_magic_line2)) != 0)
+ {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->file_encap = WTAP_ENCAP_GSM_UM;
+ wth->file_type_subtype = dct3trace_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = dct3trace_read;
+ wth->subtype_seek_read = dct3trace_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+
+ /*
+ * 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;
+}
+
+
+static gboolean dct3trace_get_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ char line[1024];
+ guint8 databuf[MAX_PACKET_LEN], *bufp;
+ gboolean have_data = FALSE;
+ int len = 0;
+
+ bufp = &databuf[0];
+ while (file_gets(line, sizeof(line), fh) != NULL)
+ {
+ if( memcmp(dct3trace_magic_end, line, strlen(dct3trace_magic_end)) == 0 )
+ {
+ /* Return on end of file </dump> */
+ *err = 0;
+ return FALSE;
+ }
+ else if( memcmp(dct3trace_magic_record_end, line, strlen(dct3trace_magic_record_end)) == 0 )
+ {
+ /* Return on end of record </l1> */
+ if( have_data )
+ {
+ /* We've got a full packet! */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = 0; /* no time stamp, no separate "on the wire" length */
+ rec->ts.secs = 0;
+ rec->ts.nsecs = 0;
+ rec->rec_header.packet_header.caplen = len;
+ rec->rec_header.packet_header.len = len;
+
+ *err = 0;
+
+ /* Make sure we have enough room for the packet */
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
+ memcpy( ws_buffer_start_ptr(buf), databuf, rec->rec_header.packet_header.caplen );
+
+ return TRUE;
+ }
+ else
+ {
+ /* If not got any data return error */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dct3trace: record without data");
+ return FALSE;
+ }
+ }
+ else if( memcmp(dct3trace_magic_record_start, line, strlen(dct3trace_magic_record_start)) == 0 )
+ {
+ /* Parse L1 header <l1 ...>*/
+ int channel, tmp;
+ char *ptr;
+
+ rec->rec_header.packet_header.pseudo_header.gsm_um.uplink = !strstr(line, "direction=\"down\"");
+ if (!xml_get_int(&channel, line, "logicalchannel", err, err_info))
+ return FALSE;
+
+ /* Parse downlink only fields */
+ if( !rec->rec_header.packet_header.pseudo_header.gsm_um.uplink )
+ {
+ if (!xml_get_int(&tmp, line, "physicalchannel", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.arfcn = tmp;
+ if (!xml_get_int(&tmp, line, "sequence", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.tdma_frame = tmp;
+ if (!xml_get_int(&tmp, line, "bsic", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.bsic = tmp;
+ if (!xml_get_int(&tmp, line, "error", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.error = tmp;
+ if (!xml_get_int(&tmp, line, "timeshift", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.timeshift = tmp;
+ }
+
+ switch( channel )
+ {
+ case 128: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SDCCH; break;
+ case 112: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SACCH; break;
+ case 176: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_FACCH; break;
+ case 96: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_CCCH; break;
+ case 80: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_BCCH; break;
+ default: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_UNKNOWN; break;
+ }
+
+ /* Read data (if have it) into databuf */
+ ptr = strstr(line, "data=\"");
+ if( ptr )
+ {
+ have_data = TRUE; /* If has data... */
+ len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], ptr+6);
+ if (len == -1)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: record length %d too long", rec->rec_header.packet_header.caplen);
+ return FALSE;
+ }
+ }
+ }
+ else if( !have_data && memcmp(dct3trace_magic_l2_start, line, strlen(dct3trace_magic_l2_start)) == 0 )
+ {
+ /* For uplink packets we might not get the raw L1, so have to recreate it from the L2 */
+ /* Parse L2 header if didn't get data from L1 <l2 ...> */
+ int data_len;
+ char *ptr = strstr(line, "data=\"");
+
+ if( !ptr )
+ {
+ continue;
+ }
+
+ have_data = TRUE;
+
+ /*
+ * We know we have no data already, so we know
+ * we have enough room for the header.
+ */
+ if( rec->rec_header.packet_header.pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_SACCH || rec->rec_header.packet_header.pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_FACCH || rec->rec_header.packet_header.pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_SDCCH )
+ {
+ /* Add LAPDm B header */
+ memset(bufp, 0x1, 2);
+ len = 3;
+ }
+ else
+ {
+ /* Add LAPDm Bbis header */
+ len = 1;
+ }
+ bufp += len;
+
+ data_len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], ptr+6);
+ if (data_len == -1)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: record length %d too long", rec->rec_header.packet_header.caplen);
+ return FALSE;
+ }
+ len += data_len;
+
+ /* Add LAPDm length byte */
+ *(bufp - 1) = data_len << 2 | 0x1;
+ }
+ }
+
+ *err = file_error(fh, err_info);
+ if (*err == 0)
+ {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+}
+
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean dct3trace_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return dct3trace_get_packet(wth->fh, rec, buf, err, err_info);
+}
+
+
+/* Used to read packets in random-access fashion */
+static gboolean dct3trace_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;
+ }
+
+ return dct3trace_get_packet(wth->random_fh, rec, buf, err, err_info);
+}
+
+static const struct supported_block_type dct3trace_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 dct3trace_info = {
+ "Gammu DCT3 trace", "dct3trace", "xml", NULL,
+ FALSE, BLOCKS_SUPPORTED(dct3trace_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_dct3trace(void)
+{
+ dct3trace_file_type_subtype = wtap_register_file_type_subtype(&dct3trace_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("DCT3TRACE",
+ dct3trace_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:
+ */