diff --git a/epan/CMakeLists.txt b/epan/CMakeLists.txt index 876165f384..69670faa54 100644 --- a/epan/CMakeLists.txt +++ b/epan/CMakeLists.txt @@ -907,6 +907,7 @@ set(DISSECTOR_SRC dissectors/packet-lmi.c dissectors/packet-lmp.c dissectors/packet-logcat.c + dissectors/packet-logcat-text.c dissectors/packet-lon.c dissectors/packet-loop.c dissectors/packet-lpd.c diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common index 758fab6654..7c4a7925d8 100644 --- a/epan/dissectors/Makefile.common +++ b/epan/dissectors/Makefile.common @@ -833,6 +833,7 @@ DISSECTOR_SRC = \ packet-lmi.c \ packet-lmp.c \ packet-logcat.c \ + packet-logcat-text.c \ packet-lon.c \ packet-loop.c \ packet-lpd.c \ diff --git a/epan/dissectors/packet-logcat-text.c b/epan/dissectors/packet-logcat-text.c new file mode 100644 index 0000000000..cc1638735c --- /dev/null +++ b/epan/dissectors/packet-logcat-text.c @@ -0,0 +1,394 @@ +/* packet-logcat-text.c + * Routines for Android Logcat text formats + * + * Copyright 2014, Michal Orynicz for Tieto Corporation + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include "epan/packet.h" +#include "epan/expert.h" +#include "wiretap/wtap.h" + +#include + +/* Basically the same regexes are present in wiretap/logcat_text.c */ +#define SPECIAL_STRING "[-]+ (beginning of \\/.+)" +#define BRIEF_STRING "([IVDWEF])/(.*?)\\( *(\\d+)\\): (.*)" +#define TAG_STRING "([IVDWEF])/(.*?): (.*)" +#define TIME_STRING "(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) ([IVDWEF])/(.*?)\\( *(\\d+)\\): (.*)" +#define THREAD_STRING "([IVDWEF])\\( *(\\d+): *(\\d+)\\) (.*)" +#define PROCESS_STRING "([IVDWEF])\\( *(\\d+)\\) (.*)" +#define THREADTIME_STRING "(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+) +(\\d+) ([IVDWEF]) (.*?): (.*)" +#define LONG_STRING "\\[ (\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+): +(\\d+) ([IVDWEF])/(.+) ]\\R(.*)" + +extern const value_string priority_vals[]; + +static int proto_logcat_text = -1; + +static int hf_logcat_text_pid = -1; +static int hf_logcat_text_tid = -1; +static int hf_logcat_text_timestamp = -1; +static int hf_logcat_text_priority = -1; +static int hf_logcat_text_tag = -1; +static int hf_logcat_text_log = -1; + +static gint ett_logcat = -1; + +static expert_field ei_malformed_time = EI_INIT; +static expert_field ei_malformed_token = EI_INIT; + +static dissector_handle_t logcat_text_brief_handle; +static dissector_handle_t logcat_text_tag_handle; +static dissector_handle_t logcat_text_process_handle; +static dissector_handle_t logcat_text_time_handle; +static dissector_handle_t logcat_text_thread_handle; +static dissector_handle_t logcat_text_threadtime_handle; +static dissector_handle_t logcat_text_long_handle; + +static GRegex *special_regex = NULL; +static GRegex *brief_regex = NULL; +static GRegex *tag_regex = NULL; +static GRegex *time_regex = NULL; +static GRegex *process_regex = NULL; +static GRegex *thread_regex = NULL; +static GRegex *threadtime_regex = NULL; +static GRegex *long_regex = NULL; + +static const gchar dissector_name[] = "Logcat Text"; + +typedef int (*tGETTER) (const gchar *frame, const gchar *token, tvbuff_t *tvb, + proto_tree *maintree, gint start_offset, packet_info *pinfo); + +typedef struct { + GRegex *regex; + const tGETTER *getters; + guint no_of_getters; +} dissect_info_t; + +void proto_register_logcat_text(void); +void proto_reg_handoff_logcat_text(void); + +static int get_priority(const gchar *frame, const gchar *token, tvbuff_t *tvb, + proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) { + int prio; + gchar *p = g_strstr_len(frame + start_offset, -1, token); + int offset = p - frame; + + switch (*p) { + case 'I': + prio = 4; + break; + case 'V': + prio = 2; + break; + case 'D': + prio = 3; + break; + case 'W': + prio = 5; + break; + case 'E': + prio = 6; + break; + case 'F': + prio = 7; + break; + default: + prio = 0; + } + + proto_tree_add_uint(maintree, hf_logcat_text_priority, tvb, offset, 1, prio); + return offset + 1; +} + +static int get_tag(const gchar *frame, const gchar *token, tvbuff_t *tvb, + proto_tree *maintree, gint start_offset, packet_info *pinfo) { + gchar *p = g_strstr_len(frame + start_offset, -1, token); + int offset = p - frame; + guint8 *src_addr = wmem_strdup(wmem_packet_scope(), token); + gint tok_len = strlen(token); + + proto_tree_add_string(maintree, hf_logcat_text_tag, tvb, offset, tok_len, + token); + SET_ADDRESS(&pinfo->src, AT_STRINGZ, tok_len + 1, src_addr); + SET_ADDRESS(&pinfo->dst, AT_STRINGZ, sizeof(dissector_name), dissector_name); + return offset + tok_len; +} + +static int get_ptid(const gchar *frame, const gchar *token, tvbuff_t *tvb, + proto_tree *maintree, gint header_field, gint start_offset) { + gchar *p = g_strstr_len(frame + start_offset, -1, token); + int offset = p - frame; + + proto_tree_add_uint(maintree, header_field, tvb, offset, strlen(token), + g_ascii_strtoull(token, NULL, 10)); + return offset + strlen(token); +} + +static int get_pid(const gchar *frame, const gchar *token, tvbuff_t *tvb, + proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) { + return get_ptid(frame, token, tvb, maintree, hf_logcat_text_pid, start_offset); +} + +static int get_tid(const gchar *frame, const gchar *token, tvbuff_t *tvb, + proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) { + return get_ptid(frame, token, tvb, maintree, hf_logcat_text_tid, start_offset); +} + +static int get_log(const gchar *frame, const gchar *token, tvbuff_t *tvb, + proto_tree *maintree, gint start_offset, packet_info *pinfo) { + gchar *p = g_strstr_len(frame + start_offset, -1, token); + int offset = p - frame; + + proto_tree_add_string(maintree, hf_logcat_text_log, tvb, offset, + strlen(token), token); + col_add_str(pinfo->cinfo, COL_INFO, token); + return offset + strlen(token); +} + +static int get_time(const gchar *frame, const gchar *token, tvbuff_t *tvb, + proto_tree *maintree, gint start_offset, packet_info *pinfo) { + gint offset; + gchar *p; + gint day, month, hrs, min, sec, ms; + GDateTime *date; + gint64 seconds; + nstime_t ts; + + p = g_strstr_len(frame + start_offset, -1, token); + offset = p - frame; + + if (6 == sscanf(token, "%d-%d %d:%d:%d.%d", &month, &day, &hrs, &min, &sec, &ms)) { + date = g_date_time_new_local(1970, month, day, hrs, min, + (gdouble) sec + ((gdouble) ms) * 0.001); + seconds = g_date_time_to_unix(date); + ts.secs = (time_t) seconds; + ts.nsecs = (int) ms * 1e6; + proto_tree_add_time(maintree, hf_logcat_text_timestamp, tvb, offset, + strlen(token), &ts); + g_date_time_unref(date); + } else { + proto_tree_add_expert(maintree, pinfo, &ei_malformed_time, tvb, offset, -1); + } + return offset + strlen(token); +} + +static int dissect_logcat_text(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, + const dissect_info_t *dinfo) { + gchar **tokens; + guint i; + gchar *frame = tvb_get_string_enc(wmem_packet_scope(), tvb, 0, tvb_captured_length(tvb), + ENC_ASCII); + proto_item *mainitem = proto_tree_add_item(tree, proto_logcat_text, tvb, 0, -1, ENC_NA); + proto_tree *maintree = proto_item_add_subtree(mainitem, ett_logcat); + gint offset = 0; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, dissector_name); + + if (!g_regex_match(special_regex, frame, G_REGEX_MATCH_NOTEMPTY, NULL)) { + + tokens = g_regex_split(dinfo->regex, frame, G_REGEX_MATCH_NOTEMPTY); + if (NULL == tokens) return 0; + if (g_strv_length(tokens) != dinfo->no_of_getters + 2) { + proto_tree_add_expert(maintree, pinfo, &ei_malformed_token, tvb, offset, -1); + g_strfreev(tokens); + return 0; + } + + for (i = 0; i < dinfo->no_of_getters; ++i) { + offset = ((*dinfo->getters[i])(frame, tokens[i + 1], tvb, maintree, offset, pinfo)); + } + } else { + tokens = g_regex_split(special_regex, frame, G_REGEX_MATCH_NOTEMPTY); + if (NULL == tokens) return 0; + get_log(frame, tokens[1], tvb, maintree, 0, pinfo); + } + g_strfreev(tokens); + return offset; +} + +static int dissect_logcat_text_brief(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + void *data _U_) { + static const tGETTER getters[] = { get_priority, get_tag, get_pid, get_log }; + dissect_info_t dinfo = { brief_regex, getters, array_length(getters) }; + + return dissect_logcat_text(tvb, tree, pinfo, &dinfo); +} + +static int dissect_logcat_text_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + void *data _U_) { + static const tGETTER getters[] = { get_priority, get_tag, get_log }; + dissect_info_t dinfo = { tag_regex, getters, array_length(getters) }; + + return dissect_logcat_text(tvb, tree, pinfo, &dinfo); +} + +static int dissect_logcat_text_process(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + void *data _U_) { + static const tGETTER getters[] = { get_priority, get_pid, get_log }; + dissect_info_t dinfo = { process_regex, getters, array_length(getters) }; + + SET_ADDRESS(&pinfo->dst, AT_STRINGZ, 0, ""); + SET_ADDRESS(&pinfo->src, AT_STRINGZ, 0, ""); + + return dissect_logcat_text(tvb, tree, pinfo, &dinfo); +} + +static int dissect_logcat_text_time(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + void *data _U_) { + static const tGETTER getters[] = { get_time, get_priority, get_tag, get_pid, get_log }; + dissect_info_t dinfo = { time_regex, getters, array_length(getters) }; + + return dissect_logcat_text(tvb, tree, pinfo, &dinfo); +} + +static int dissect_logcat_text_thread(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + void *data _U_) { + static const tGETTER getters[] = { get_priority, get_pid, get_tid, get_log }; + dissect_info_t dinfo = { thread_regex, getters, array_length(getters) }; + + SET_ADDRESS(&pinfo->dst, AT_STRINGZ, 0, ""); + SET_ADDRESS(&pinfo->src, AT_STRINGZ, 0, ""); + + return dissect_logcat_text(tvb, tree, pinfo, &dinfo); +} + +static int dissect_logcat_text_threadtime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + void *data _U_) { + static const tGETTER getters[] = { get_time, get_pid, get_tid, get_priority, get_tag, get_log }; + dissect_info_t dinfo = { threadtime_regex, getters, array_length(getters) }; + + return dissect_logcat_text(tvb, tree, pinfo, &dinfo); +} + +static int dissect_logcat_text_long(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + void *data _U_) { + static const tGETTER getters[] = { get_time, get_pid, get_tid, get_priority, get_tag, get_log }; + dissect_info_t dinfo = { long_regex, getters, array_length(getters) }; + + return dissect_logcat_text(tvb, tree, pinfo, &dinfo); +} + +void proto_register_logcat_text(void) { + expert_module_t *expert_module; + static hf_register_info hf[] = { + { &hf_logcat_text_timestamp, + { "Timestamp", "logcat_text.timestamp", + FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x00, NULL, HFILL + } + }, + { &hf_logcat_text_tag, + { "Tag", "logcat_text.tag", + FT_STRING, STR_ASCII, NULL, 0x00, NULL, HFILL + } + }, + { &hf_logcat_text_log, + { "Log", "logcat_text.log", + FT_STRING, STR_ASCII, NULL, 0x00, NULL, HFILL + } + }, + { &hf_logcat_text_priority, + { "Priority", "logcat_text.priority", + FT_UINT8, BASE_DEC, VALS(priority_vals), 0x00, NULL, HFILL + } + }, + { &hf_logcat_text_pid, + { "PID", "logcat_text.pid", + FT_UINT32, BASE_DEC, NULL, 0x00, "Process ID", HFILL + } + }, + { &hf_logcat_text_tid, + { "TID", "logcat_text.tid", + FT_UINT32, BASE_DEC, NULL, 0x00, "Thread ID", HFILL + } + } + }; + + static ei_register_info ei[] = { + { &ei_malformed_time, { "logcat_text.malformed_time", PI_PROTOCOL, PI_ERROR, "Malformed time data", EXPFILL }}, + { &ei_malformed_token, { "logcat_text.malformed_token", PI_PROTOCOL, PI_ERROR, "Failed to decode one or more tokens", EXPFILL }}, + }; + + static gint *ett[] = { &ett_logcat}; + + proto_logcat_text = proto_register_protocol("Android Logcat Text", dissector_name, + "logcat_text"); + proto_register_field_array(proto_logcat_text, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + logcat_text_brief_handle = new_register_dissector("logcat_text_brief", + dissect_logcat_text_brief, proto_logcat_text); + logcat_text_tag_handle = new_register_dissector("logcat_text_tag", + dissect_logcat_text_tag, proto_logcat_text); + logcat_text_time_handle = new_register_dissector("logcat_text_time", + dissect_logcat_text_time, proto_logcat_text); + logcat_text_process_handle = new_register_dissector("logcat_text_process", + dissect_logcat_text_process, proto_logcat_text); + logcat_text_thread_handle = new_register_dissector("logcat_text_thread", + dissect_logcat_text_thread, proto_logcat_text); + logcat_text_threadtime_handle = new_register_dissector("logcat_text_threadtime", + dissect_logcat_text_threadtime, proto_logcat_text); + logcat_text_long_handle = new_register_dissector("logcat_text_long", + dissect_logcat_text_long, proto_logcat_text); + + special_regex = g_regex_new(SPECIAL_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL); + brief_regex = g_regex_new(BRIEF_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL); + tag_regex = g_regex_new(TAG_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL); + time_regex = g_regex_new(TIME_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL); + thread_regex = g_regex_new(THREAD_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL); + threadtime_regex = g_regex_new(THREADTIME_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL); + process_regex = g_regex_new(PROCESS_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL); + long_regex = g_regex_new(LONG_STRING, G_REGEX_MULTILINE, G_REGEX_MATCH_NOTEMPTY, NULL); + + expert_module = expert_register_protocol(proto_logcat_text); + expert_register_field_array(expert_module, ei, array_length(ei)); +} + +void proto_reg_handoff_logcat_text(void) { + dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_BRIEF, + logcat_text_brief_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_TAG, + logcat_text_tag_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_TIME, + logcat_text_time_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_THREAD, + logcat_text_thread_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_THREADTIME, + logcat_text_threadtime_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_PROCESS, + logcat_text_process_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_LONG, + logcat_text_long_handle); +} + +/* + * 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/epan/dissectors/packet-logcat.c b/epan/dissectors/packet-logcat.c index 4a5141a907..6515477716 100644 --- a/epan/dissectors/packet-logcat.c +++ b/epan/dissectors/packet-logcat.c @@ -57,7 +57,7 @@ static gint exported_pdu_tap = -1; static expert_field ei_invalid_payload_length = EI_INIT; -static const value_string priority_vals[] = { +const value_string priority_vals[] = { { 0x00, "Unknown" }, { 0x01, "Default" }, { 0x02, "Verbose" }, diff --git a/wiretap/CMakeLists.txt b/wiretap/CMakeLists.txt index 319e64a72f..b2687f1300 100644 --- a/wiretap/CMakeLists.txt +++ b/wiretap/CMakeLists.txt @@ -49,6 +49,7 @@ set(CLEAN_FILES lanalyzer.c libpcap.c logcat.c + logcat_text.c merge.c mpeg.c mime_file.c diff --git a/wiretap/Makefile.common b/wiretap/Makefile.common index e36949bbbd..abd2392cc7 100644 --- a/wiretap/Makefile.common +++ b/wiretap/Makefile.common @@ -53,6 +53,7 @@ NONGENERATED_C_FILES = \ mime_file.c \ k12.c \ lanalyzer.c \ + logcat_text.c \ logcat.c \ libpcap.c \ merge.c \ @@ -112,6 +113,7 @@ NONGENERATED_HEADER_FILES = \ lanalyzer.h \ libpcap.h \ logcat.h \ + logcat_text.h \ merge.h \ mpeg.h \ mp2t.h \ diff --git a/wiretap/file_access.c b/wiretap/file_access.c index 740108da33..a9c02d7341 100644 --- a/wiretap/file_access.c +++ b/wiretap/file_access.c @@ -65,6 +65,7 @@ #include "erf.h" #include "hcidump.h" #include "logcat.h" +#include "logcat_text.h" #include "network_instruments.h" #include "k12.h" #include "ber.h" @@ -362,6 +363,7 @@ static struct open_info open_info_base[] = { { "Commview", OPEN_INFO_HEURISTIC, commview_open, "ncf", NULL, NULL }, { "Nstrace", OPEN_INFO_HEURISTIC, nstrace_open, "txt", NULL, NULL }, { "Logcat ", OPEN_INFO_HEURISTIC, logcat_open, "logcat", NULL, NULL }, + { "Logcat Text", OPEN_INFO_HEURISTIC, logcat_text_open, "txt", NULL, NULL }, /* ASCII trace files from Telnet sessions. */ { "Ascend", OPEN_INFO_HEURISTIC, ascend_open, "txt", NULL, NULL }, { "Toshiba", OPEN_INFO_HEURISTIC, toshiba_open, "txt", NULL, NULL }, @@ -1441,25 +1443,25 @@ static const struct file_type_subtype_info dump_open_table_base[] = { logcat_dump_can_write_encap, logcat_binary_dump_open, NULL }, { "Android Logcat Brief text format", "logcat-brief", NULL, NULL, FALSE, FALSE, 0, - logcat_dump_can_write_encap, logcat_text_brief_dump_open, NULL }, + logcat_text_brief_dump_can_write_encap, logcat_text_brief_dump_open, NULL }, { "Android Logcat Process text format", "logcat-process", NULL, NULL, FALSE, FALSE, 0, - logcat_dump_can_write_encap, logcat_text_process_dump_open, NULL }, + logcat_text_process_dump_can_write_encap, logcat_text_process_dump_open, NULL }, { "Android Logcat Tag text format", "logcat-tag", NULL, NULL, FALSE, FALSE, 0, - logcat_dump_can_write_encap, logcat_text_tag_dump_open, NULL }, + logcat_text_tag_dump_can_write_encap, logcat_text_tag_dump_open, NULL }, + { "Android Logcat Thread text format", "logcat-thread", NULL, NULL, + FALSE, FALSE, 0, + logcat_text_thread_dump_can_write_encap, logcat_text_thread_dump_open, NULL }, { "Android Logcat Time text format", "logcat-time", NULL, NULL, FALSE, FALSE, 0, - logcat_dump_can_write_encap, logcat_text_time_dump_open, NULL }, - { "Android Logcat Thread text format", "logcat-thread", NULL, NULL, - FALSE, FALSE, 0, - logcat_dump_can_write_encap, logcat_text_thread_dump_open, NULL }, + logcat_text_time_dump_can_write_encap, logcat_text_time_dump_open, NULL }, { "Android Logcat Threadtime text format", "logcat-threadtime", NULL, NULL, FALSE, FALSE, 0, - logcat_dump_can_write_encap, logcat_text_threadtime_dump_open, NULL }, + logcat_text_threadtime_dump_can_write_encap, logcat_text_threadtime_dump_open, NULL }, { "Android Logcat Long text format", "logcat-long", NULL, NULL, FALSE, FALSE, 0, - logcat_dump_can_write_encap, logcat_text_long_dump_open, NULL } + logcat_text_long_dump_can_write_encap, logcat_text_long_dump_open, NULL } }; diff --git a/wiretap/logcat.c b/wiretap/logcat.c index 5ac6e383aa..ff80f99a39 100644 --- a/wiretap/logcat.c +++ b/wiretap/logcat.c @@ -28,53 +28,6 @@ #include "logcat.h" -enum dump_type_t { - DUMP_BINARY, - DUMP_BRIEF, - DUMP_PROCESS, - DUMP_TAG, - DUMP_TIME, - DUMP_THREAD, - DUMP_THREADTIME, - DUMP_LONG -}; - -struct dumper_t { - enum dump_type_t type; -}; - -/* The log format can be found on: - * https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h - * Log format is assumed to be little-endian (Android platform). - */ -/* maximum size of a message payload in a log entry */ -#define LOGGER_ENTRY_MAX_PAYLOAD 4076 - -struct logger_entry { - guint16 len; /* length of the payload */ - guint16 __pad; /* no matter what, we get 2 bytes of padding */ - gint32 pid; /* generating process's pid */ - gint32 tid; /* generating process's tid */ - gint32 sec; /* seconds since Epoch */ - gint32 nsec; /* nanoseconds */ - char msg[0]; /* the entry's payload */ -}; - -struct logger_entry_v2 { - guint16 len; /* length of the payload */ - guint16 hdr_size; /* sizeof(struct logger_entry_v2) */ - gint32 pid; /* generating process's pid */ - gint32 tid; /* generating process's tid */ - gint32 sec; /* seconds since Epoch */ - gint32 nsec; /* nanoseconds */ - union { - /* v1: not present */ - guint32 euid; /* v2: effective UID of logger */ - guint32 lid; /* v3: log id of the payload */ - }; - char msg[0]; /* the entry's payload */ -}; - /* Returns '?' for invalid priorities */ static gchar get_priority(const guint8 priority) { static gchar priorities[] = "??VDIWEFS"; @@ -85,51 +38,6 @@ static gchar get_priority(const guint8 priority) { return priorities[priority]; } -static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds, - gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag, - const gchar *log) -{ - gchar time_buffer[15]; - time_t datetime; - - datetime = (time_t) seconds; - - switch (dumper->type) { - case DUMP_BRIEF: - return g_strdup_printf("%c/%-8s(%5i): %s\n", - priority, tag, pid, log); - case DUMP_PROCESS: - /* NOTE: Last parameter should be "process name", not tag; - Unfortunately, we do not have process name */ - return g_strdup_printf("%c(%5i) %s (%s)\n", - priority, pid, log, ""); - case DUMP_TAG: - return g_strdup_printf("%c/%-8s: %s\n", - priority, tag, log); - case DUMP_THREAD: - return g_strdup_printf("%c(%5i:0x%02x) %s\n", - priority, pid, tid, log); - case DUMP_TIME: - strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", - gmtime(&datetime)); - return g_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n", - time_buffer, milliseconds, priority, tag, pid, log); - case DUMP_THREADTIME: - strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", - gmtime(&datetime)); - return g_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n", - time_buffer, milliseconds, pid, tid, priority, tag, log); - case DUMP_LONG: - strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", - gmtime(&datetime)); - return g_strdup_printf("[ %s.%03i %5i:0x%02x %c/%s ]\n%s\n\n", - time_buffer, milliseconds, pid, tid, priority, tag, log); - default: - return NULL; - } - -} - static gint detect_version(wtap *wth, int *err, gchar **err_info) { gint bytes_read; @@ -228,63 +136,7 @@ static gint detect_version(wtap *wth, int *err, gchar **err_info) return -1; } -static gint buffered_detect_version(const guint8 *pd) -{ - struct logger_entry *log_entry; - struct logger_entry_v2 *log_entry_v2; - gint version; - guint8 *msg_payload = NULL; - guint8 *msg_part; - guint8 *msg_end; - guint16 msg_len; - - log_entry_v2 = (struct logger_entry_v2 *) pd; - log_entry = (struct logger_entry *) pd; - - /* must contain at least priority and two nulls as separator */ - if (log_entry->len < 3) - return -1; - - /* payload length may not exceed the maximum payload size */ - if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD) - return -1; - - /* cannot rely on __pad being 0 for v1, use heuristics to find out what - * version is in use. First assume the smallest msg. */ - for (version = 1; version <= 2; ++version) { - if (version == 1) { - msg_payload = log_entry->msg; - } else if (version == 2) { - /* v2 is 4 bytes longer */ - msg_payload = log_entry_v2->msg; - if (log_entry_v2->hdr_size != sizeof(*log_entry_v2)) - continue; - } - - /* A v2 msg has a 32-bit userid instead of v1 priority */ - if (get_priority(msg_payload[0]) == '?') - continue; - - /* Is there a terminating '\0' for the tag? */ - msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1); - if (msg_part == NULL) - continue; - - /* if msg is '\0'-terminated, is it equal to the payload len? */ - ++msg_part; - msg_len = (guint16)(log_entry->len - (msg_part - msg_payload)); - msg_end = (guint8 *) memchr(msg_part, '\0', msg_len); - /* is the end of the buffer (-1) equal to the end of msg? */ - if (msg_end && (msg_payload + log_entry->len - 1 != msg_end)) - continue; - - return version; - } - - return -1; -} - -static gint exported_pdu_length(const guint8 *pd) { +gint logcat_exported_pdu_length(const guint8 *pd) { guint16 *tag; guint16 *tag_length; gint length = 0; @@ -457,7 +309,7 @@ static gboolean logcat_binary_dump(wtap_dumper *wdh, if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) { gint skipped_length; - skipped_length = exported_pdu_length(pd); + skipped_length = logcat_exported_pdu_length(pd); pd += skipped_length; caplen -= skipped_length; } @@ -489,216 +341,6 @@ gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err) return TRUE; } -static gboolean logcat_dump_text(wtap_dumper *wdh, - const struct wtap_pkthdr *phdr, - const guint8 *pd, int *err) -{ - gchar *buf; - gint length; - gchar priority; - const struct logger_entry *log_entry; - const struct logger_entry_v2 *log_entry_v2; - gint payload_length; - const gchar *tag; - gint32 pid; - gint32 tid; - gint32 seconds; - gint32 milliseconds; - const gchar *msg_begin; - gint msg_pre_skip; - gchar *log; - gchar *log_part; - gchar *log_next; - gint logcat_version; - const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv; - - /* We can only write packet records. */ - if (phdr->rec_type != REC_TYPE_PACKET) { - *err = WTAP_ERR_REC_TYPE_UNSUPPORTED; - return FALSE; - } - - /* Skip EXPORTED_PDU*/ - if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) { - gint skipped_length; - - skipped_length = exported_pdu_length(pd); - pd += skipped_length; - - logcat_version = buffered_detect_version(pd); - } else { - const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header; - - logcat_version = pseudo_header->logcat.version; - } - - log_entry = (struct logger_entry *) pd; - log_entry_v2 = (struct logger_entry_v2 *) pd; - - payload_length = GINT32_FROM_LE(log_entry->len); - pid = GINT32_FROM_LE(log_entry->pid); - tid = GINT32_FROM_LE(log_entry->tid); - seconds = GINT32_FROM_LE(log_entry->sec); - milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000; - - /* msg: \0\0 with N >= 0, last \0 can be missing */ - if (logcat_version == 1) { - priority = get_priority(log_entry->msg[0]); - tag = log_entry->msg + 1; - msg_pre_skip = 1 + (gint) strlen(tag) + 1; - msg_begin = log_entry->msg + msg_pre_skip; - } else if (logcat_version == 2) { - priority = get_priority(log_entry_v2->msg[0]); - tag = log_entry_v2->msg + 1; - msg_pre_skip = 1 + (gint) strlen(tag) + 1; - msg_begin = log_entry_v2->msg + msg_pre_skip; - } else { - *err = WTAP_ERR_UNSUPPORTED; - return FALSE; - } - - /* copy the message part. If a nul byte was missing, it will be added. */ - log = g_strndup(msg_begin, payload_length - msg_pre_skip); - - /* long format: display one header followed by the whole message (which may - * contain new lines). Other formats: include tag, etc. with each line */ - log_next = log; - do { - log_part = log_next; - if (dumper->type == DUMP_LONG) { - /* read until end, there is no next string */ - log_next = NULL; - } else { - /* read until next newline */ - log_next = strchr(log_part, '\n'); - if (log_next != NULL) { - *log_next = '\0'; - ++log_next; - /* ignore trailing newline */ - if (*log_next == '\0') { - log_next = NULL; - } - } - } - - buf = logcat_log(dumper, seconds, milliseconds, pid, tid, - priority, tag, log_part); - if (!buf) { - g_free(log); - return FALSE; - } - length = (guint32)strlen(buf); - - if (!wtap_dump_file_write(wdh, buf, length, err)) { - g_free(log); - return FALSE; - } - - wdh->bytes_dumped += length; - } while (log_next != NULL); - - g_free(log); - return TRUE; -} - -gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err _U_) -{ - struct dumper_t *dumper; - - dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t)); - dumper->type = DUMP_BRIEF; - - wdh->priv = dumper; - wdh->subtype_write = logcat_dump_text; - wdh->subtype_close = NULL; - - return TRUE; -} - -gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err _U_) -{ - struct dumper_t *dumper; - - dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t)); - dumper->type = DUMP_PROCESS; - - wdh->priv = dumper; - wdh->subtype_write = logcat_dump_text; - wdh->subtype_close = NULL; - - return TRUE; -} - -gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err _U_) -{ - struct dumper_t *dumper; - - dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t)); - dumper->type = DUMP_TAG; - - wdh->priv = dumper; - wdh->subtype_write = logcat_dump_text; - wdh->subtype_close = NULL; - - return TRUE; -} - -gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err _U_) -{ - struct dumper_t *dumper; - - dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t)); - dumper->type = DUMP_TIME; - - wdh->priv = dumper; - wdh->subtype_write = logcat_dump_text; - wdh->subtype_close = NULL; - - return TRUE; -} - -gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err _U_) -{ - struct dumper_t *dumper; - - dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t)); - dumper->type = DUMP_THREAD; - - wdh->priv = dumper; - wdh->subtype_write = logcat_dump_text; - wdh->subtype_close = NULL; - - return TRUE; -} - -gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err _U_) -{ - struct dumper_t *dumper; - - dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t)); - dumper->type = DUMP_THREADTIME; - - wdh->priv = dumper; - wdh->subtype_write = logcat_dump_text; - wdh->subtype_close = NULL; - - return TRUE; -} - -gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err _U_) -{ - struct dumper_t *dumper; - - dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t)); - dumper->type = DUMP_LONG; - - wdh->priv = dumper; - wdh->subtype_write = logcat_dump_text; - wdh->subtype_close = NULL; - - return TRUE; -} - /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * diff --git a/wiretap/logcat.h b/wiretap/logcat.h index 4167bc21fb..9b2ea5cc25 100644 --- a/wiretap/logcat.h +++ b/wiretap/logcat.h @@ -25,19 +25,45 @@ #include "wtap.h" +/* The log format can be found on: + * https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h + * Log format is assumed to be little-endian (Android platform). + */ +/* maximum size of a message payload in a log entry */ +#define LOGGER_ENTRY_MAX_PAYLOAD 4076 + +struct logger_entry { + guint16 len; /* length of the payload */ + guint16 __pad; /* no matter what, we get 2 bytes of padding */ + gint32 pid; /* generating process's pid */ + gint32 tid; /* generating process's tid */ + gint32 sec; /* seconds since Epoch */ + gint32 nsec; /* nanoseconds */ + char msg[0]; /* the entry's payload */ +}; + +struct logger_entry_v2 { + guint16 len; /* length of the payload */ + guint16 hdr_size; /* sizeof(struct logger_entry_v2) */ + gint32 pid; /* generating process's pid */ + gint32 tid; /* generating process's tid */ + gint32 sec; /* seconds since Epoch */ + gint32 nsec; /* nanoseconds */ + union { + /* v1: not present */ + guint32 euid; /* v2: effective UID of logger */ + guint32 lid; /* v3: log id of the payload */ + }; + char msg[0]; /* the entry's payload */ +}; + int logcat_open(wtap *wth, int *err, gchar **err_info); gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err); -gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err); -gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err); -gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err); -gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err); -gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err); -gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err); -gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err); int logcat_dump_can_write_encap(int encap); +gint logcat_exported_pdu_length(const guint8 *pd); #endif /* diff --git a/wiretap/logcat_text.c b/wiretap/logcat_text.c new file mode 100644 index 0000000000..a24a853f6d --- /dev/null +++ b/wiretap/logcat_text.c @@ -0,0 +1,593 @@ +/* logcat_text.c + * + * Copyright 2014, Michal Orynicz for Tieto Corporation + * Copyright 2014, Michal Labedzki for Tieto Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include + +#include "wtap-int.h" +#include "file_wrappers.h" +#include "wsutil/buffer.h" + +#include "logcat_text.h" +#include "logcat.h" + +/* Basically the same regexes are present in epan/packet-logcat-text.c */ +#define SPECIAL_STRING "[-]+ beginning of \\/" +#define BRIEF_STRING "[IVDWEF]/.*\\( *\\d*\\): .*" +#define TAG_STRING "[IVDWEF]/.*: .*" +#define TIME_STRING "\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3} [IVDWE]/.*\\( *\\d*\\): .*" +#define THREAD_STRING "[IVDWEF]\\( *\\d+: *\\d+\\) .*" +#define PROCESS_STRING "[IVDWEF]\\( *\\d+\\) .*" +#define THREADTIME_STRING "\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3} *\\d+ *\\d+ [IVDWEF] .+: +" +#define LONG_STRING "\\[ (\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+): +(\\d+) ([IVDWEF])/(.+) ]" + +struct dumper_t { + int type; +}; + +/* Returns '?' for invalid priorities */ +static gchar get_priority(const guint8 priority) { + static gchar priorities[] = "??VDIWEFS"; + + if (priority >= (guint8) sizeof(priorities)) + return '?'; + + return priorities[priority]; +} + +static gint buffered_detect_version(const guint8 *pd) +{ + struct logger_entry *log_entry; + struct logger_entry_v2 *log_entry_v2; + gint version; + guint8 *msg_payload = NULL; + guint8 *msg_part; + guint8 *msg_end; + guint16 msg_len; + + log_entry_v2 = (struct logger_entry_v2 *) pd; + log_entry = (struct logger_entry *) pd; + + /* must contain at least priority and two nulls as separator */ + if (log_entry->len < 3) + return -1; + + /* payload length may not exceed the maximum payload size */ + if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD) + return -1; + + /* cannot rely on __pad being 0 for v1, use heuristics to find out what + * version is in use. First assume the smallest msg. */ + for (version = 1; version <= 2; ++version) { + if (version == 1) { + msg_payload = log_entry->msg; + } else if (version == 2) { + /* v2 is 4 bytes longer */ + msg_payload = log_entry_v2->msg; + if (log_entry_v2->hdr_size != sizeof(*log_entry_v2)) + continue; + } + + /* A v2 msg has a 32-bit userid instead of v1 priority */ + if (get_priority(msg_payload[0]) == '?') + continue; + + /* Is there a terminating '\0' for the tag? */ + msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1); + if (msg_part == NULL) + continue; + + /* if msg is '\0'-terminated, is it equal to the payload len? */ + ++msg_part; + msg_len = (guint16)(log_entry->len - (msg_part - msg_payload)); + msg_end = (guint8 *) memchr(msg_part, '\0', msg_len); + /* is the end of the buffer (-1) equal to the end of msg? */ + if (msg_end && (msg_payload + log_entry->len - 1 != msg_end)) + continue; + + return version; + } + + return -1; +} + +static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds, + gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag, + const gchar *log) +{ + gchar time_buffer[15]; + time_t datetime; + + datetime = (time_t) seconds; + + switch (dumper->type) { + case WTAP_ENCAP_LOGCAT_BRIEF: + return g_strdup_printf("%c/%-8s(%5i): %s\n", + priority, tag, pid, log); + case WTAP_ENCAP_LOGCAT_PROCESS: + /* NOTE: Last parameter should be "process name", not tag; + Unfortunately, we do not have process name */ + return g_strdup_printf("%c(%5i) %s (%s)\n", + priority, pid, log, ""); + case WTAP_ENCAP_LOGCAT_TAG: + return g_strdup_printf("%c/%-8s: %s\n", + priority, tag, log); + case WTAP_ENCAP_LOGCAT_THREAD: + return g_strdup_printf("%c(%5i:%5i) %s\n", + priority, pid, tid, log); + case WTAP_ENCAP_LOGCAT_TIME: + strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", + gmtime(&datetime)); + return g_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n", + time_buffer, milliseconds, priority, tag, pid, log); + case WTAP_ENCAP_LOGCAT_THREADTIME: + strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", + gmtime(&datetime)); + return g_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n", + time_buffer, milliseconds, pid, tid, priority, tag, log); + case WTAP_ENCAP_LOGCAT_LONG: + strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", + gmtime(&datetime)); + return g_strdup_printf("[ %s.%03i %5i:%5i %c/%-8s ]\n%s\n\n", + time_buffer, milliseconds, pid, tid, priority, tag, log); + default: + return NULL; + } + +} + +static void get_time(gchar *string, struct wtap_pkthdr *phdr) { + gint day, month, hrs, min, sec, ms; + GDateTime *date = NULL; + gint64 seconds; + + if (6 == sscanf(string, "%d-%d %d:%d:%d.%d", &month, &day, &hrs, &min, &sec, &ms)) { + date = g_date_time_new_local(1970, month, day, hrs, min, + (gdouble) sec + ((gdouble) ms) * 0.001); + seconds = g_date_time_to_unix(date); + phdr->ts.secs = (time_t) seconds; + phdr->ts.nsecs = (int) ms * 1e6; + phdr->presence_flags = WTAP_HAS_TS; + g_date_time_unref(date); + } else { + phdr->presence_flags = 0; + phdr->ts.secs = (time_t) 0; + phdr->ts.nsecs = (int) 0; + } +} + +static gboolean logcat_text_read_packet(FILE_T fh, struct wtap_pkthdr *phdr, + Buffer *buf, gint file_type) { + gint8 *pd; + gchar cbuff[WTAP_MAX_PACKET_SIZE]; + gchar *ret = NULL; + + do { + ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE, fh); + } while (NULL != ret && 3 > strlen(cbuff) && !file_eof(fh)); + + if (NULL == ret || 3 > strlen(cbuff)) { + return FALSE; + } + + if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type && + !g_regex_match_simple(SPECIAL_STRING, cbuff, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY)) { + gint64 file_off = 0; + gchar lbuff[WTAP_MAX_PACKET_SIZE]; + int err; + gchar *ret2 = NULL; + + file_off = file_tell(fh); + ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE, fh); + while (NULL != ret2 && 2 < strlen(lbuff) && !file_eof(fh)) { + g_strlcat(cbuff,lbuff,WTAP_MAX_PACKET_SIZE); + file_off = file_tell(fh); + ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE, fh); + } + + if(NULL == ret2 || 2 < strlen(lbuff)) { + return FALSE; + } + + file_seek(fh,file_off,SEEK_SET,&err); + } + + phdr->rec_type = REC_TYPE_PACKET; + phdr->caplen = strlen(cbuff); + phdr->len = phdr->caplen; + + ws_buffer_assure_space(buf, phdr->caplen + 1); + pd = ws_buffer_start_ptr(buf); + if ((WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME == file_type + || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME == file_type + || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type) + && '-' != cbuff[0]) { /* the last part filters out the -- beginning of... lines */ + if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type) { + get_time(cbuff+2, phdr); + } else { + get_time(cbuff, phdr); + } + } else { + phdr->presence_flags = 0; + phdr->ts.secs = (time_t) 0; + phdr->ts.nsecs = (int) 0; + } + memcpy(pd, cbuff, phdr->caplen + 1); + return TRUE; +} + +static gboolean logcat_text_read(wtap *wth, int *err _U_ , gchar **err_info _U_, + gint64 *data_offset _U_) { + *data_offset = file_tell(wth->fh); + + return logcat_text_read_packet(wth->fh, &wth->phdr, wth->frame_buffer, + wth->file_type_subtype); +} + +static gboolean logcat_text_seek_read(wtap *wth, gint64 seek_off, + struct wtap_pkthdr *phdr, Buffer *buf, int *err _U_, gchar **err_info _U_) { + if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) + return FALSE; + + if (!logcat_text_read_packet(wth->random_fh, phdr, buf, + wth->file_type_subtype)) { + if (*err == 0) + *err = WTAP_ERR_SHORT_READ; + return FALSE; + } + return TRUE; +} + +int logcat_text_open(wtap *wth, int *err, gchar **err_info _U_) { + gchar cbuff[WTAP_MAX_PACKET_SIZE]; + gchar *ret = NULL; + + if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) + return -1; + + do { + ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE, wth->fh); + } while (NULL != ret && !file_eof(wth->fh) + && ((3 > strlen(cbuff)) + || g_regex_match_simple(SPECIAL_STRING, cbuff, G_REGEX_ANCHORED, + G_REGEX_MATCH_NOTEMPTY))); + + if (g_regex_match_simple(BRIEF_STRING, cbuff, G_REGEX_ANCHORED, + G_REGEX_MATCH_NOTEMPTY)) { + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_BRIEF; + wth->file_encap = WTAP_ENCAP_LOGCAT_BRIEF; + } else if (g_regex_match_simple(TAG_STRING, cbuff, G_REGEX_ANCHORED, + G_REGEX_MATCH_NOTEMPTY)) { + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TAG; + wth->file_encap = WTAP_ENCAP_LOGCAT_TAG; + } else if (g_regex_match_simple(PROCESS_STRING, cbuff, G_REGEX_ANCHORED, + G_REGEX_MATCH_NOTEMPTY)) { + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_PROCESS; + wth->file_encap = WTAP_ENCAP_LOGCAT_PROCESS; + } else if (g_regex_match_simple(TIME_STRING, cbuff, G_REGEX_ANCHORED, + G_REGEX_MATCH_NOTEMPTY)) { + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME; + wth->file_encap = WTAP_ENCAP_LOGCAT_TIME; + } else if (g_regex_match_simple(THREAD_STRING, cbuff, G_REGEX_ANCHORED, + G_REGEX_MATCH_NOTEMPTY)) { + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREAD; + wth->file_encap = WTAP_ENCAP_LOGCAT_THREAD; + } else if (g_regex_match_simple(THREADTIME_STRING, cbuff, G_REGEX_ANCHORED, + G_REGEX_MATCH_NOTEMPTY)) { + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME; + wth->file_encap = WTAP_ENCAP_LOGCAT_THREADTIME; + } else if (g_regex_match_simple(LONG_STRING, cbuff, G_REGEX_ANCHORED, + G_REGEX_MATCH_NOTEMPTY)) { + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG; + wth->file_encap = WTAP_ENCAP_LOGCAT_LONG; + } else { + return -1; + } + + if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) + return -1; + + wth->snapshot_length = 0; + + wth->subtype_read = logcat_text_read; + wth->subtype_seek_read = logcat_text_seek_read; + wth->tsprecision = WTAP_FILE_TSPREC_USEC; + return 1; +} + +int logcat_text_brief_dump_can_write_encap(int encap) { + if (encap == WTAP_ENCAP_PER_PACKET) + return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; + + switch (encap) { + case WTAP_ENCAP_LOGCAT: + case WTAP_ENCAP_LOGCAT_BRIEF: + return 0; + default: + return WTAP_ERR_UNSUPPORTED_ENCAP; + } +} + +int logcat_text_process_dump_can_write_encap(int encap) { + if (encap == WTAP_ENCAP_PER_PACKET) + return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; + + switch (encap) { + case WTAP_ENCAP_LOGCAT: + case WTAP_ENCAP_LOGCAT_PROCESS: + return 0; + default: + return WTAP_ERR_UNSUPPORTED_ENCAP; + } +} + +int logcat_text_tag_dump_can_write_encap(int encap) { + if (encap == WTAP_ENCAP_PER_PACKET) + return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; + + switch (encap) { + case WTAP_ENCAP_LOGCAT: + case WTAP_ENCAP_LOGCAT_TAG: + return 0; + default: + return WTAP_ERR_UNSUPPORTED_ENCAP; + } +} + +int logcat_text_time_dump_can_write_encap(int encap) { + if (encap == WTAP_ENCAP_PER_PACKET) + return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; + + switch (encap) { + case WTAP_ENCAP_LOGCAT: + case WTAP_ENCAP_LOGCAT_TIME: + return 0; + default: + return WTAP_ERR_UNSUPPORTED_ENCAP; + } +} + +int logcat_text_thread_dump_can_write_encap(int encap) { + if (encap == WTAP_ENCAP_PER_PACKET) + return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; + + switch (encap) { + case WTAP_ENCAP_LOGCAT: + case WTAP_ENCAP_LOGCAT_THREAD: + return 0; + default: + return WTAP_ERR_UNSUPPORTED_ENCAP; + } +} + +int logcat_text_threadtime_dump_can_write_encap(int encap) { + if (encap == WTAP_ENCAP_PER_PACKET) + return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; + + switch (encap) { + case WTAP_ENCAP_LOGCAT: + case WTAP_ENCAP_LOGCAT_THREADTIME: + return 0; + default: + return WTAP_ERR_UNSUPPORTED_ENCAP; + } +} + +int logcat_text_long_dump_can_write_encap(int encap) { + if (encap == WTAP_ENCAP_PER_PACKET) + return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; + + switch (encap) { + case WTAP_ENCAP_LOGCAT: + case WTAP_ENCAP_LOGCAT_LONG: + return 0; + default: + return WTAP_ERR_UNSUPPORTED_ENCAP; + } +} + +static gboolean logcat_text_dump_text(wtap_dumper *wdh, + const struct wtap_pkthdr *phdr, + const guint8 *pd, int *err) +{ + gchar *buf; + gint length; + gchar priority; + const struct logger_entry *log_entry; + const struct logger_entry_v2 *log_entry_v2; + gint payload_length; + const gchar *tag; + gint32 pid; + gint32 tid; + gint32 seconds; + gint32 milliseconds; + const gchar *msg_begin; + gint msg_pre_skip; + gchar *log; + gchar *log_part; + gchar *log_next; + gint logcat_version; + const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv; + + /* We can only write packet records. */ + if (phdr->rec_type != REC_TYPE_PACKET) { + *err = WTAP_ERR_REC_TYPE_UNSUPPORTED; + return FALSE; + } + + switch (wdh->encap) { + case WTAP_ENCAP_WIRESHARK_UPPER_PDU: + case WTAP_ENCAP_LOGCAT: + /* Skip EXPORTED_PDU*/ + if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) { + gint skipped_length; + + skipped_length = logcat_exported_pdu_length(pd); + pd += skipped_length; + + logcat_version = buffered_detect_version(pd); + } else { + const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header; + + logcat_version = pseudo_header->logcat.version; + } + + log_entry = (struct logger_entry *) pd; + log_entry_v2 = (struct logger_entry_v2 *) pd; + + payload_length = GINT32_FROM_LE(log_entry->len); + pid = GINT32_FROM_LE(log_entry->pid); + tid = GINT32_FROM_LE(log_entry->tid); + seconds = GINT32_FROM_LE(log_entry->sec); + milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000; + + /* msg: \0\0 with N >= 0, last \0 can be missing */ + if (logcat_version == 1) { + priority = get_priority(log_entry->msg[0]); + tag = log_entry->msg + 1; + msg_pre_skip = 1 + (gint) strlen(tag) + 1; + msg_begin = log_entry->msg + msg_pre_skip; + } else if (logcat_version == 2) { + priority = get_priority(log_entry_v2->msg[0]); + tag = log_entry_v2->msg + 1; + msg_pre_skip = 1 + (gint) strlen(tag) + 1; + msg_begin = log_entry_v2->msg + msg_pre_skip; + } else { + *err = WTAP_ERR_UNSUPPORTED; + return FALSE; + } + + /* copy the message part. If a nul byte was missing, it will be added. */ + log = g_strndup(msg_begin, payload_length - msg_pre_skip); + + /* long format: display one header followed by the whole message (which may + * contain new lines). Other formats: include tag, etc. with each line */ + log_next = log; + do { + log_part = log_next; + if (dumper->type == WTAP_ENCAP_LOGCAT_LONG) { + /* read until end, there is no next string */ + log_next = NULL; + } else { + /* read until next newline */ + log_next = strchr(log_part, '\n'); + if (log_next != NULL) { + *log_next = '\0'; + ++log_next; + /* ignore trailing newline */ + if (*log_next == '\0') { + log_next = NULL; + } + } + } + + buf = logcat_log(dumper, seconds, milliseconds, pid, tid, priority, tag, log_part); + if (!buf) { + g_free(log); + return FALSE; + } + length = (guint32) strlen(buf); + + if (!wtap_dump_file_write(wdh, buf, length, err)) { + g_free(log); + return FALSE; + } + + wdh->bytes_dumped += length; + } while (log_next != NULL ); + + g_free(log); + + break; + case WTAP_ENCAP_LOGCAT_BRIEF: + case WTAP_ENCAP_LOGCAT_TAG: + case WTAP_ENCAP_LOGCAT_PROCESS: + case WTAP_ENCAP_LOGCAT_TIME: + case WTAP_ENCAP_LOGCAT_THREAD: + case WTAP_ENCAP_LOGCAT_THREADTIME: + case WTAP_ENCAP_LOGCAT_LONG: + if (dumper->type == wdh->encap) { + if (!wtap_dump_file_write(wdh, (gchar*) pd, phdr->caplen, err)) { + return FALSE; + } + } else { + *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE; + return FALSE; + } + } + + return TRUE; +} + +static gboolean logcat_text_dump_open(wtap_dumper *wdh, guint dump_type, int *err _U_) { + struct dumper_t *dumper; + + dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t)); + dumper->type = dump_type; + + wdh->priv = dumper; + wdh->subtype_write = logcat_text_dump_text; + wdh->subtype_close = NULL; + + return TRUE; +} + +gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err) { + return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_BRIEF, err); +} + +gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err) { + return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_PROCESS, err); +} + +gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err) { + return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TAG, err); +} + +gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err) { + return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TIME, err); +} + +gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err) { + return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREAD, err); +} + +gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err) { + return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREADTIME, err); +} + +gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err) { + return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_LONG, err); +} + +/* + * 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/logcat_text.h b/wiretap/logcat_text.h new file mode 100644 index 0000000000..90f88ec0f1 --- /dev/null +++ b/wiretap/logcat_text.h @@ -0,0 +1,59 @@ +/* logcat_text.h + * + * Copyright 2014, Michal Orynicz for Tieto Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __LOGCAT_TEXT_H__ +#define __LOGCAT_TEXT_H__ + +#include + +#include "wtap.h" + +int logcat_text_open(wtap *wth, int *err, gchar **err_info); + +gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err); +gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err); +gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err); +gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err); +gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err); +gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err); +gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err); + +int logcat_text_brief_dump_can_write_encap(int encap); +int logcat_text_tag_dump_can_write_encap(int encap); +int logcat_text_process_dump_can_write_encap(int encap); +int logcat_text_thread_dump_can_write_encap(int encap); +int logcat_text_time_dump_can_write_encap(int encap); +int logcat_text_threadtime_dump_can_write_encap(int encap); +int logcat_text_long_dump_can_write_encap(int encap); + +#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: + */