From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- wiretap/dct3trace.c | 442 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 442 insertions(+) create mode 100644 wiretap/dct3trace.c (limited to 'wiretap/dct3trace.c') 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 + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" +#include "wtap-int.h" +#include "dct3trace.h" +#include "file_wrappers.h" + +#include +#include +#include + +#include + +/* + Example downlink data: + + + + + + + + + + Example uplink data (no raw L1): + + + + + + + + + + */ + + +/* Magic text to check */ +static const char dct3trace_magic_line1[] = ""; +static const char dct3trace_magic_line2[] = ""; +static const char dct3trace_magic_record_start[] = "= '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 */ + *err = 0; + return FALSE; + } + else if( memcmp(dct3trace_magic_record_end, line, strlen(dct3trace_magic_record_end)) == 0 ) + { + /* Return on end of record */ + 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 */ + 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 */ + 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: + */ -- cgit v1.2.3