aboutsummaryrefslogtreecommitdiffstats
path: root/wiretap
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2019-07-04 12:36:29 +0200
committerAnders Broman <a.broman58@gmail.com>2019-07-08 11:41:05 +0000
commitd8ad7a686384d0f1b348ed1b2f6d1e36733fbdba (patch)
tree87752172d30380b7b32ef89ea7c081c9bce45b9a /wiretap
parentbfeb46b1cf94f0b936c16c415258eee661317586 (diff)
wiretap: Add a phone log reader.
Change-Id: I0b290df4783616f1eb15e6ad35fbd6d2b4c3dbdd Reviewed-on: https://code.wireshark.org/review/33865 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'wiretap')
-rw-r--r--wiretap/CMakeLists.txt1
-rw-r--r--wiretap/file_access.c13
-rw-r--r--wiretap/log3gpp.c897
-rw-r--r--wiretap/log3gpp.h16
-rw-r--r--wiretap/wtap.c4
-rw-r--r--wiretap/wtap.h2
6 files changed, 931 insertions, 2 deletions
diff --git a/wiretap/CMakeLists.txt b/wiretap/CMakeLists.txt
index b29e85a7d9..dddb894b00 100644
--- a/wiretap/CMakeLists.txt
+++ b/wiretap/CMakeLists.txt
@@ -49,6 +49,7 @@ set(WIRETAP_NONGENERATED_FILES
k12.c
lanalyzer.c
libpcap.c
+ log3gpp.c
logcat.c
logcat_text.c
merge.c
diff --git a/wiretap/file_access.c b/wiretap/file_access.c
index 1c7231e943..b028e01096 100644
--- a/wiretap/file_access.c
+++ b/wiretap/file_access.c
@@ -76,6 +76,8 @@
#include "rfc7468.h"
#include "ruby_marshal.h"
#include "systemd_journal.h"
+#include "log3gpp.h"
+
/*
* Add an extension, and all compressed versions thereof if requested,
@@ -421,8 +423,10 @@ static const struct open_info open_info_base[] = {
{ "Ixia IxVeriWave .vwr Raw Capture", OPEN_INFO_HEURISTIC, vwr_open, "vwr", NULL, NULL },
{ "CAM Inspector file", OPEN_INFO_HEURISTIC, camins_open, "camins", NULL, NULL },
{ "JavaScript Object Notation", OPEN_INFO_HEURISTIC, json_open, "json", NULL, NULL },
- { "Ruby Marshal Object", OPEN_INFO_HEURISTIC, ruby_marshal_open, "", NULL, NULL },
- { "Systemd Journal", OPEN_INFO_HEURISTIC, systemd_journal_open, "log;jnl;journal", NULL, NULL }
+ { "Ruby Marshal Object", OPEN_INFO_HEURISTIC, ruby_marshal_open, "", NULL, NULL },
+ { "Systemd Journal", OPEN_INFO_HEURISTIC, systemd_journal_open, "log;jnl;journal", NULL, NULL },
+ { "3gpp phone log", OPEN_INFO_MAGIC, log3gpp_open, "log", NULL, NULL },
+
};
/* this is only used to build the dynamic array on load, do NOT use this
@@ -1652,6 +1656,11 @@ static const struct file_type_subtype_info dump_open_table_base[] = {
/* WTAP_FILE_TYPE_SUBTYPE_SYSTEMD_JOURNAL */
{ "systemd journal export", "systemd journal", NULL, NULL,
FALSE, FALSE, 0,
+ NULL, NULL, NULL },
+
+ /* WTAP_FILE_TYPE_SUBTYPE_LOG_3GPP */
+ { "3GPP Log", "3gpp_log", "*.log", NULL,
+ TRUE, FALSE, 0,
NULL, NULL, NULL }
};
diff --git a/wiretap/log3gpp.c b/wiretap/log3gpp.c
new file mode 100644
index 0000000000..06f58c1c61
--- /dev/null
+++ b/wiretap/log3gpp.c
@@ -0,0 +1,897 @@
+/* log3gpp.c
+ * Routines encapsulating/dumping 3gpp protocol logs.
+ * The purpose of this format is to be able to log the 3GPP protocol stack on a mobile phone.
+ * Copyright 2008, Vincent Helfre
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "log3gpp.h"
+
+#define MAX_FIRST_LINE_LENGTH 200
+#define MAX_TIMESTAMP_LINE_LENGTH 100
+#define MAX_LINE_LENGTH 65536
+#define MAX_TIMESTAMP_LEN 32
+#define MAX_SECONDS_CHARS 16
+#define MAX_SUBSECOND_DECIMALS 4
+#define MAX_PROTOCOL_NAME 64
+#define MAX_PROTOCOL_PAR_STRING 64
+
+/* 's' or 'r' of a packet as read from .out file */
+typedef enum packet_direction_t
+{
+ uplink,
+ downlink
+} packet_direction_t;
+
+typedef struct {
+ time_t start_secs;
+ guint32 start_usecs;
+} log3gpp_t;
+
+gint first_packet_offset;
+gchar firstline[MAX_FIRST_LINE_LENGTH];
+gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
+gint secondline_length = 0;
+
+/***********************************************************/
+/* Transient data used for parsing */
+
+/* 'Magic number' at start of 3gpp log files. */
+static const gchar log3gpp_magic[] = "3GPP protocols transcript";
+
+/* Protocol name of the packet that the packet was captured at */
+static gchar protocol_name[MAX_PROTOCOL_NAME+1];
+
+/* Optional string parameter giving info required for the protocol dissector */
+static gchar protocol_parameters[MAX_PROTOCOL_PAR_STRING+1];
+/************************************************************/
+/* Functions called from wiretap core */
+static gboolean log3gpp_read( wtap* wth, wtap_rec* rec, Buffer* buf,
+ int* err, gchar** err_info, gint64* data_offset);
+static gboolean log3gpp_seek_read(struct wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf,
+ int *err, gchar **err_info);
+
+/************************************************************/
+/* Private helper functions */
+static gboolean read_new_line(FILE_T fh, gint* length,
+ gchar* buf, size_t bufsize, int* err,
+ gchar** err_info);
+
+static gboolean parse_line(char* linebuff, gint line_length, gint *seconds, gint *useconds,
+ long *data_offset,
+ gint *data_chars,
+ packet_direction_t *direction,
+ gboolean *is_text_data);
+static int write_stub_header(guchar *frame_buffer, char *timestamp_string,
+ packet_direction_t direction);
+static guchar hex_from_char(gchar c);
+/*not used static gchar char_from_hex(guchar hex);*/
+
+static gboolean get_file_time_stamp(gchar* linebuff, time_t *secs, guint32 *usecs);
+
+
+/***************************************************************************/
+/* Free log3gpp-specific capture info from file that was open for reading */
+/***************************************************************************/
+void log3gpp_close(wtap* wth)
+{
+ log3gpp_t* log3gpp = (log3gpp_t*)wth->priv;
+ /* Also free this capture info */
+ g_free(log3gpp);
+ wth->priv = NULL;
+}
+
+static gboolean
+log3gpp_dump(wtap_dumper* wdh _U_, const wtap_rec* rec _U_,
+ const guchar* buf _U_, int* err _U_, gchar** err_info _U_)
+{
+ return TRUE;
+}
+
+
+/******************************************************/
+/* Close a file we've been writing to. */
+/******************************************************/
+static gboolean
+log3gpp_dump_finish(wtap_dumper* wdh _U_, int* err _U_)
+{
+ return TRUE;
+}
+
+/********************************************/
+/* Open file (for reading) */
+/********************************************/
+wtap_open_return_val
+log3gpp_open(wtap *wth, int *err, gchar **err_info _U_)
+{
+ time_t timestamp;
+ guint32 usecs;
+ log3gpp_t *log3gpp;
+ wtap_open_return_val retval;
+ /* Buffer to hold a single text line read from the file */
+ static gchar linebuff[MAX_LINE_LENGTH];
+ gint firstline_length = 0;
+
+ /* Clear errno before reading from the file */
+ errno = 0;
+
+ /********************************************************************/
+ /* First line needs to contain at least as many characters as magic */
+
+ /*g_warning("Open file"); */
+
+ if (!read_new_line(wth->fh, &firstline_length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
+ return WTAP_OPEN_ERROR;
+ }
+ else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+
+ if (((size_t)firstline_length < strlen(log3gpp_magic)) ||
+ firstline_length >= MAX_FIRST_LINE_LENGTH)
+ {
+ retval = WTAP_OPEN_NOT_MINE;
+ return retval;
+ }
+
+ /* This file is not for us if it doesn't match our signature */
+ if (memcmp(log3gpp_magic, linebuff, strlen(log3gpp_magic)) != 0)
+ {
+ retval = WTAP_OPEN_NOT_MINE;
+ return retval;
+ }
+
+ /***********************************************************/
+ /* Second line contains file timestamp */
+ if (!read_new_line(wth->fh, &secondline_length,
+ linebuff, sizeof linebuff, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
+ return WTAP_OPEN_ERROR;
+ }
+ else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+
+ first_packet_offset = firstline_length + secondline_length;
+
+ if ((secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
+ (!get_file_time_stamp(linebuff, &timestamp, &usecs)))
+ {
+ /* Give up if file time line wasn't valid */
+ retval = WTAP_OPEN_NOT_MINE;
+ return retval;
+ }
+
+ /* Allocate struct and fill in timestamp (netmon re used)*/
+ log3gpp = (log3gpp_t *)g_malloc(sizeof(log3gpp_t));
+ log3gpp->start_secs = timestamp;
+ log3gpp->start_usecs = usecs;
+ wth->priv = (void *)log3gpp;
+
+ /************************************************************/
+ /* File is for us. Fill in details so packets can be read */
+
+ /* Set our file type */
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOG_3GPP;
+
+ /* Use our own encapsulation to send all packets to our stub dissector */
+ wth->file_encap = WTAP_ENCAP_LOG_3GPP;
+
+ /* Callbacks for reading operations */
+ wth->subtype_read = log3gpp_read;
+ wth->subtype_seek_read = log3gpp_seek_read;
+ wth->subtype_close = log3gpp_close;
+
+ /* Choose microseconds (have 4 decimal places...) */
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ *err = errno;
+
+ retval = WTAP_OPEN_MINE;
+ return retval;
+}
+
+
+/**************************************************/
+/* Read packet function. */
+/* Look for and read the next usable packet */
+/* - return TRUE and details if found */
+/**************************************************/
+gboolean log3gpp_read(wtap* wth, wtap_rec* rec, Buffer* buf,
+ int* err, gchar** err_info, gint64* data_offset)
+{
+ gint64 offset = file_tell(wth->fh);
+ static gchar linebuff[MAX_LINE_LENGTH + 1];
+ long dollar_offset;
+ packet_direction_t direction;
+ gboolean is_text_data;
+ log3gpp_t *log3gpp = (log3gpp_t *)wth->priv;
+
+ /* Search for a line containing a usable packet */
+ while (1)
+ {
+ int line_length, seconds, useconds, data_chars;
+ gint64 this_offset = offset;
+
+ /* Are looking for first packet after 2nd line */
+ if (file_tell(wth->fh) == 0)
+ {
+ this_offset += (gint64)first_packet_offset +1+1;
+ }
+
+ /* Clear errno before reading from the file */
+ errno = 0;
+
+ /* Read a new line from file into linebuff */
+ if (!read_new_line(wth->fh, &line_length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ if (*err != 0) {
+ return FALSE; /* error */
+ }
+ /* No more lines can be read, so quit. */
+ break;
+ }
+
+ /* Try to parse the line as a frame record */
+ if (parse_line(linebuff, line_length, &seconds, &useconds,
+ &dollar_offset,
+ &data_chars,
+ &direction,
+ &is_text_data))
+ {
+ guchar *frame_buffer;
+ int n;
+ int stub_offset = 0;
+ char timestamp_string[MAX_TIMESTAMP_LEN+1];
+ /*not used gint64 *pkey = NULL;*/
+
+ g_snprintf(timestamp_string, 32, "%d.%04d", seconds, useconds/100);
+
+ /* All packets go to 3GPP protocol stub dissector */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LOG_3GPP;
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->presence_flags = WTAP_HAS_TS;
+
+ /* Set data_offset to the beginning of the line we're returning.
+ This will be the seek_off parameter when this frame is re-read.
+ */
+ *data_offset = this_offset;
+
+ /* Fill in timestamp (capture base + packet offset) */
+ rec->ts.secs = log3gpp->start_secs + seconds;
+ if ((log3gpp->start_usecs + useconds) >= 1000000)
+ {
+ rec->ts.secs++;
+ }
+ rec->ts.nsecs =
+ ((log3gpp->start_usecs + useconds) % 1000000) *1000;
+
+ if (!is_text_data)
+ {
+ /* Get buffer pointer ready */
+ ws_buffer_assure_space(buf,
+ strlen(timestamp_string)+1 + /* timestamp */
+ strlen(protocol_name)+1 + /* Protocol name */
+ 1 + /* direction */
+ (size_t)(data_chars/2));
+
+ frame_buffer = ws_buffer_start_ptr(buf);
+ /*********************/
+ /* Write stub header */
+ stub_offset = write_stub_header(frame_buffer, timestamp_string,
+ direction);
+
+ /* Binary data length is half bytestring length + stub header */
+ rec->rec_header.packet_header.len = data_chars/2 + stub_offset;
+ rec->rec_header.packet_header.caplen = data_chars/2 + stub_offset;
+ /********************************/
+ /* Copy packet data into buffer */
+ for (n=0; n <= data_chars; n+=2)
+ {
+ frame_buffer[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
+ hex_from_char(linebuff[dollar_offset+n+1]);
+ }
+ *err = errno = 0;
+ return TRUE;
+ }
+ else
+ {
+ /* Get buffer pointer ready */
+ ws_buffer_assure_space(buf,
+ strlen(timestamp_string)+1 + /* timestamp */
+ strlen(protocol_name)+1 + /* Protocol name */
+ 1 + /* direction */
+ data_chars);
+ frame_buffer = ws_buffer_start_ptr(buf);
+
+ /*********************/
+ /* Write stub header */
+ stub_offset = write_stub_header(frame_buffer, timestamp_string,
+ direction);
+
+ /* Binary data length is bytestring length + stub header */
+ rec->rec_header.packet_header.len = data_chars + stub_offset;
+ rec->rec_header.packet_header.caplen = data_chars + stub_offset;
+
+ /* do not convert the ascii char */
+ memcpy(&frame_buffer[stub_offset],&linebuff[dollar_offset],data_chars);
+ frame_buffer[stub_offset+data_chars-1]= '\0';
+ *err = errno = 0;
+ return TRUE;
+ }
+ }
+ }
+
+ /* No packet details to return... */
+ *err = errno;
+ return FALSE;
+}
+
+
+/**************************************************/
+/* Read & seek function. */
+/**************************************************/
+static gboolean
+log3gpp_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec _U_ , Buffer *buf,
+ int *err, gchar **err_info)
+{
+ long dollar_offset;
+ static gchar linebuff[MAX_LINE_LENGTH + 1];
+ packet_direction_t direction;
+ int seconds, useconds, data_chars;
+ gboolean is_text_data;
+ log3gpp_t* log3gpp = (log3gpp_t*)wth->priv;
+ int length = 0;
+ guchar *frame_buffer;
+
+ /* Reset errno */
+ *err = errno = 0;
+
+ /* Seek to beginning of packet */
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ {
+ return FALSE;
+ }
+
+ /* Re-read whole line (this really should succeed) */
+ if (!read_new_line(wth->random_fh, &length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ return FALSE;
+ }
+
+ /* Try to parse this line again (should succeed as re-reading...) */
+ if (parse_line(linebuff, length, &seconds, &useconds,
+ &dollar_offset,
+ &data_chars,
+ &direction,
+ &is_text_data))
+ {
+ int n;
+ int stub_offset = 0;
+ char timestamp_string[32];
+ g_snprintf(timestamp_string, 32, "%d.%04d", seconds, useconds/100);
+
+ /* Make sure all packets go to log3gpp dissector */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LOG_3GPP;
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->presence_flags = WTAP_HAS_TS;
+
+ /* Fill in timestamp (capture base + packet offset) */
+ rec->ts.secs = log3gpp->start_secs + seconds;
+ if ((log3gpp->start_usecs + useconds) >= 1000000)
+ {
+ rec->ts.secs++;
+ }
+ rec->ts.nsecs =
+ ((log3gpp->start_usecs + useconds) % 1000000) * 1000;
+
+ /*********************/
+ /* Write stub header */
+ ws_buffer_assure_space(buf,
+ strlen(timestamp_string)+1 + /* timestamp */
+ strlen(protocol_name)+1 + /* Protocol name */
+ 1 + /* direction */
+ data_chars);
+ frame_buffer = ws_buffer_start_ptr(buf);
+ stub_offset = write_stub_header(frame_buffer, timestamp_string,
+ direction);
+
+ if (!is_text_data)
+ {
+ /********************************/
+ /* Copy packet data into buffer */
+ for (n=0; n <= data_chars; n+=2)
+ {
+ frame_buffer[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
+ hex_from_char(linebuff[dollar_offset+n+1]);
+ }
+ *err = errno = 0;
+ return TRUE;
+ }
+ else
+ {
+ /* do not convert the ascii char */
+ memcpy(&frame_buffer[stub_offset],&linebuff[dollar_offset],data_chars);
+ frame_buffer[stub_offset+data_chars-1] = '\0';
+ *err = errno = 0;
+ return TRUE;
+ }
+ }
+
+ /* If get here, must have failed */
+ *err = errno;
+ *err_info = g_strdup_printf("prot 3gpp: seek_read failed to read/parse "
+ "line at position %" G_GINT64_MODIFIER "d",
+ seek_off);
+ return FALSE;
+}
+
+
+/***************************/
+/* Dump functions */
+/***************************/
+
+/*****************************************************/
+/* The file that we are writing to has been opened. */
+/* Set other dump callbacks. */
+/*****************************************************/
+gboolean log3gpp_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_, int *err _U_)
+{
+ /* Fill in other dump callbacks */
+ wdh->subtype_write = log3gpp_dump;
+ wdh->subtype_finish = log3gpp_dump_finish;
+
+ return TRUE;
+}
+
+/*****************************************/
+/* Write a single packet out to the file */
+/*****************************************/
+/*
+static gboolean do_fwrite(const void *data, size_t size, size_t count, FILE *stream, int *err_p)
+{
+ size_t nwritten;
+
+ nwritten = fwrite(data, size, count, stream);
+ if (nwritten != count) {
+ if (nwritten == 0 && ferror(stream))
+ {
+ *err_p = errno;
+ }
+ else
+ {
+ *err_p = WTAP_ERR_SHORT_WRITE;
+ }
+
+ return FALSE;
+ }
+ return TRUE;
+}*/
+
+
+
+/****************************/
+/* Private helper functions */
+/****************************/
+
+/**********************************************************************/
+/* Read a new line from the file, starting at offset. */
+/* - writes data to static var linebuff */
+/* - on return 'offset' will point to the next position to read from */
+/* - return TRUE if this read is successful */
+/**********************************************************************/
+static gboolean
+read_new_line(FILE_T fh, gint* length,
+ gchar* linebuff, size_t linebuffsize, int* err, gchar** err_info)
+{
+ /* Read in a line */
+ gint64 pos_before = file_tell(fh);
+
+ if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
+ /* No characters found, or error */
+ *err = file_error(fh, err_info);
+ return FALSE;
+ }
+
+ /* Set length (avoiding strlen()) and offset.. */
+ *length = (gint)(file_tell(fh) - pos_before);
+
+ /* ...but don't want to include newline in line length */
+ if (*length > 0 && linebuff[*length - 1] == '\n') {
+ linebuff[*length - 1] = '\0';
+ *length = *length - 1;
+ }
+ /* Nor do we want '\r' (as will be written when log is created on windows) */
+ if (*length > 0 && linebuff[*length - 1] == '\r') {
+ linebuff[*length - 1] = '\0';
+ *length = *length - 1;
+ }
+
+ return TRUE;
+}
+
+
+/**********************************************************************/
+/* Parse a line from buffer, by identifying: */
+/* - context, port and direction of packet */
+/* - timestamp */
+/* - data position and length */
+/* Return TRUE if this packet looks valid and can be displayed */
+/**********************************************************************/
+gboolean parse_line(gchar* linebuff, gint line_length, gint *seconds, gint *useconds,
+ long *data_offset, gint *data_chars,
+ packet_direction_t *direction,
+ gboolean *is_text_data)
+{
+ int n = 0;
+ /*not used int variant_digits = 0;*/
+ /*not used int variant = 1;*/
+ int protocol_chars = 0;
+ int prot_option_chars = 0;
+ char seconds_buff[MAX_SECONDS_CHARS+1];
+ int seconds_chars;
+ char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
+ int subsecond_decimals_chars;
+
+ /*********************************************************************/
+ /* Find and read the timestamp */
+ /*********************************************************************/
+ /* Now scan to the next digit, which should be the start of the timestamp */
+ for (; !g_ascii_isdigit((guchar)linebuff[n]) && (n < line_length); n++);
+ if (n >= line_length)
+ {
+ return FALSE;
+ }
+
+ /* Seconds */
+ for (seconds_chars = 0;
+ (linebuff[n] != '.') &&
+ (seconds_chars <= MAX_SECONDS_CHARS) &&
+ (n < line_length);
+ n++, seconds_chars++)
+ {
+ if (!g_ascii_isdigit((guchar)linebuff[n]))
+ {
+ /* Found a non-digit before decimal point. Fail */
+ return FALSE;
+ }
+ seconds_buff[seconds_chars] = linebuff[n];
+ }
+ if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length)
+ {
+ /* Didn't fit in buffer. Fail rather than use truncated */
+ return FALSE;
+ }
+
+ /* Convert found value into number */
+ seconds_buff[seconds_chars] = '\0';
+
+ /* Already know they are digits, so avoid expense of ws_strtoi32() */
+ int multiplier = 1;
+ *seconds = 0;
+ for (int d = seconds_chars - 1; d >= 0; d--) {
+ *seconds += ((seconds_buff[d] - '0') * multiplier);
+ multiplier *= 10;
+ }
+
+ /* The decimal point must follow the last of the seconds digits */
+ if (linebuff[n] != '.')
+ {
+ return FALSE;
+ }
+ /* Skip it */
+ n++;
+
+ /* Subsecond decimal digits (expect 4-digit accuracy) */
+ for (subsecond_decimals_chars = 0;
+ (linebuff[n] != ' ') &&
+ (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
+ (n < line_length);
+ n++, subsecond_decimals_chars++)
+ {
+ if (!g_ascii_isdigit((guchar)linebuff[n]))
+ {
+ return FALSE;
+ }
+ subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
+ }
+ if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length)
+ {
+ /* More numbers than expected - give up */
+ return FALSE;
+ }
+ /* Convert found value into microseconds */
+ subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
+ /* Already know they are digits, so avoid expense of ws_strtoi32() */
+ *useconds = ((subsecond_decimals_buff[0] - '0') * 100000) +
+ ((subsecond_decimals_buff[1] - '0') * 10000) +
+ ((subsecond_decimals_buff[2] - '0') * 1000) +
+ ((subsecond_decimals_buff[3] - '0') * 100);
+
+ /* Space character must follow end of timestamp */
+ if (linebuff[n] != ' ')
+ {
+ return FALSE;
+ }
+ n++;
+
+ /*********************************************************************/
+ /* Find and read protocol name */
+ /*********************************************************************/
+ /* Read context name until find ' ' */
+ for (protocol_chars = 0;
+ (linebuff[n] != ' ') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
+ n++, protocol_chars++)
+ {
+ if (!g_ascii_isalnum((guchar)linebuff[n]) && linebuff[n] != '_' && linebuff[n] != '.' && linebuff[n] != '-')
+ {
+ return FALSE;
+ }
+ protocol_name[protocol_chars] = linebuff[n];
+ }
+ if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length)
+ {
+ /* If doesn't fit, fail rather than truncate */
+ return FALSE;
+ }
+ protocol_name[protocol_chars] = '\0';
+
+ /* Space char must follow protocol name */
+ if (linebuff[n] != ' ')
+ {
+ return FALSE;
+ }
+ /* Skip it */
+ n++;
+
+ /* Scan ahead to the next space */
+ for (; (!g_ascii_isalnum((guchar)linebuff[n])) && (n < line_length); n++);
+ if (n >= line_length)
+ {
+ return FALSE;
+ }
+
+
+ if (strcmp(protocol_name,"TXT") == 0)
+ {
+ *direction = uplink;
+ *data_offset = n;
+ *data_chars = line_length - n;
+ *is_text_data = TRUE;
+ }
+ else
+ {
+ /* Next character gives direction of message (must be 'u' or 'd') */
+ if (linebuff[n] == 'u')
+ {
+ *direction = uplink;
+ }
+ else if (linebuff[n] == 'd')
+ {
+ *direction = downlink;
+ }
+ else
+ {
+ return FALSE;
+ }
+ n++;
+
+ /* Now skip ahead to find start of data (marked by '$') */
+ for (; (linebuff[n] != '$') && (n <= line_length) && (prot_option_chars <= MAX_PROTOCOL_PAR_STRING);
+ n++,prot_option_chars++)
+ {
+ protocol_parameters[prot_option_chars] = linebuff[n];
+ }
+ protocol_parameters[prot_option_chars] = '\0';
+ if (prot_option_chars == MAX_PROTOCOL_PAR_STRING || n >= line_length)
+ {
+ /* If doesn't fit, fail rather than truncate */
+ return FALSE;
+ }
+
+ /* Skip it */
+ n++;
+
+ /* Set offset to data start within line */
+ *data_offset = n;
+
+ /* Set number of chars that comprise the hex string protocol data */
+ *data_chars = line_length - n;
+
+ *is_text_data = FALSE;
+ }
+ return TRUE;
+}
+
+/*****************************************************************/
+/* Write the stub info to the data buffer while reading a packet */
+/*****************************************************************/
+int write_stub_header(guchar *frame_buffer, char *timestamp_string,
+ packet_direction_t direction)
+{
+ int stub_offset = 0;
+
+ /* Timestamp within file */
+ g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
+ stub_offset += (int)(strlen(timestamp_string) + 1);
+
+ /* Protocol name */
+ g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
+ stub_offset += (int)(strlen(protocol_name) + 1);
+
+ /* Direction */
+ frame_buffer[stub_offset] = direction;
+ stub_offset++;
+
+ /* Option string (might be string of length 0) */
+ g_strlcpy((char*)&frame_buffer[stub_offset], protocol_parameters,MAX_PROTOCOL_PAR_STRING+1);
+ stub_offset += (int)(strlen(protocol_parameters) + 1);
+ return stub_offset;
+}
+
+
+/********************************************************/
+/* Return hex nibble equivalent of hex string character */
+/********************************************************/
+guchar hex_from_char(gchar c)
+{
+ if ((c >= '0') && (c <= '9'))
+ {
+ return c - '0';
+ }
+
+ if ((c >= 'a') && (c <= 'f'))
+ {
+ return 0x0a + (c - 'a');
+ }
+
+ if ((c >= 'A') && (c <= 'F'))
+ {
+ return 0x0a + (c - 'A');
+ }
+ /* Not a valid hex string character */
+ return 0xff;
+}
+
+
+/********************************************************/
+/* Return character corresponding to hex nibble value */
+/********************************************************/
+/*gchar char_from_hex(guchar hex)
+{
+ static char hex_lookup[16] =
+ { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+
+ if (hex > 15)
+ {
+ return '?';
+ }
+
+ return hex_lookup[hex];
+}*/
+
+/************************************************************************/
+/* Parse year, month, day, hour, minute, seconds out of formatted line. */
+/* Set secs and usecs as output */
+/* Return FALSE if no valid time can be read */
+/************************************************************************/
+gboolean get_file_time_stamp(gchar* linebuff, time_t *secs, guint32 *usecs)
+{
+ int n;
+ struct tm tm;
+ #define MAX_MONTH_LETTERS 9
+ char month[MAX_MONTH_LETTERS+1];
+
+ int day, year, hour, minute, second;
+ int scan_found;
+
+ /* If line longer than expected, file is probably not correctly formatted */
+ if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH)
+ {
+ return FALSE;
+ }
+
+ /**************************************************************/
+ /* First is month. Read until get a space following the month */
+ for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++)
+ {
+ month[n] = linebuff[n];
+ }
+ month[n] = '\0';
+
+ if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
+ else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
+ else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
+ else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
+ else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
+ else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
+ else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
+ else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
+ else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
+ else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
+ else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
+ else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
+ else
+ {
+ /* Give up if not found a properly-formatted date */
+ return FALSE;
+ }
+ /* Skip space char */
+ n++;
+
+ /********************************************************/
+ /* Scan for remaining numerical fields */
+ scan_found = sscanf(linebuff+n, "%d, %d %d:%d:%d.%u",
+ &day, &year, &hour, &minute, &second, usecs);
+ if (scan_found != 6)
+ {
+ /* Give up if not all found */
+ return FALSE;
+ }
+
+ /******************************************************/
+ /* Fill in remaining fields and return it in a time_t */
+ tm.tm_year = year - 1900;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = minute;
+ tm.tm_sec = second;
+ tm.tm_isdst = -1; /* daylight saving time info not known */
+
+ /* Get seconds from this time */
+ *secs = mktime(&tm);
+
+ /* Multiply 4 digits given to get micro-seconds */
+ *usecs = *usecs * 100;
+
+ return TRUE;
+}
+
+#if 0
+/* Register with wtap */
+void wtap_register_phonelog(void) {
+ static struct file_type_subtype_info fi =
+ { "3GPP Log", "3gpp_log", "*.log", NULL, TRUE, FALSE, 0, NULL, NULL, NULL };
+
+ static struct open_info phonelog_oi =
+ { "3gpp_log", OPEN_INFO_MAGIC, log3gpp_open, "*.log", NULL, NULL };
+
+ wtap_register_open_info(&phonelog_oi, TRUE);
+
+ encap_3gpp_log = wtap_register_encap_type("3GPP Log","3gpp_log");
+ wf_3gpp_log = wtap_register_file_type_subtypes(&fi, WTAP_FILE_TYPE_SUBTYPE_UNKNOWN);
+}
+#endif
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/log3gpp.h b/wiretap/log3gpp.h
new file mode 100644
index 0000000000..82e635555f
--- /dev/null
+++ b/wiretap/log3gpp.h
@@ -0,0 +1,16 @@
+/* log_3gpp.h
+*
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+*/
+#ifndef __LOG_3GPP_H__
+#define __LOG_3GPP_H__
+
+#include <wtap.h>
+
+wtap_open_return_val log3gpp_open(wtap* wth, int* err, gchar** err_info);
+
+#endif /* __LOG_3GPP_H__ */
diff --git a/wiretap/wtap.c b/wiretap/wtap.c
index 4eb33454b9..3bb8934574 100644
--- a/wiretap/wtap.c
+++ b/wiretap/wtap.c
@@ -971,6 +971,10 @@ static struct encap_type_info encap_table_base[] = {
/* WTAP_ENCAP_IEEE802_15_4_TAP */
{ "wpan-tap", "IEEE 802.15.4 Wireless with TAP pseudo-header" },
+
+ /* WTAP_ENCAP_LOG_3GPP */
+ { "log_3GPP", "3GPP Phone Log" },
+
};
WS_DLL_LOCAL
diff --git a/wiretap/wtap.h b/wiretap/wtap.h
index 5443c53c61..6d8077542d 100644
--- a/wiretap/wtap.h
+++ b/wiretap/wtap.h
@@ -287,6 +287,7 @@ extern "C" {
#define WTAP_ENCAP_EBHSCR 204
#define WTAP_ENCAP_VPP 205
#define WTAP_ENCAP_IEEE802_15_4_TAP 206
+#define WTAP_ENCAP_LOG_3GPP 207
/* After adding new item here, please also add new item to encap_table_base array */
@@ -382,6 +383,7 @@ extern "C" {
#define WTAP_FILE_TYPE_SUBTYPE_RFC7468 82
#define WTAP_FILE_TYPE_SUBTYPE_RUBY_MARSHAL 83
#define WTAP_FILE_TYPE_SUBTYPE_SYSTEMD_JOURNAL 84
+#define WTAP_FILE_TYPE_SUBTYPE_LOG_3GPP 85
#define WTAP_NUM_FILE_TYPES_SUBTYPES wtap_get_num_file_types_subtypes()