summaryrefslogtreecommitdiffstats
path: root/plugins/epan/mate
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /plugins/epan/mate
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/epan/mate')
-rw-r--r--plugins/epan/mate/.editorconfig3
-rw-r--r--plugins/epan/mate/AUTHORS3
-rw-r--r--plugins/epan/mate/CMakeLists.txt85
-rw-r--r--plugins/epan/mate/examples/call.mate34
-rw-r--r--plugins/epan/mate/examples/mms.mate40
-rw-r--r--plugins/epan/mate/examples/pasv_ftp.mate18
-rw-r--r--plugins/epan/mate/examples/tcp.mate7
-rw-r--r--plugins/epan/mate/examples/web.mate27
-rw-r--r--plugins/epan/mate/mate.h373
-rw-r--r--plugins/epan/mate/mate_grammar.lemon729
-rw-r--r--plugins/epan/mate/mate_parser.l409
-rw-r--r--plugins/epan/mate/mate_runtime.c930
-rw-r--r--plugins/epan/mate/mate_setup.c667
-rw-r--r--plugins/epan/mate/mate_util.c1690
-rw-r--r--plugins/epan/mate/mate_util.h249
-rw-r--r--plugins/epan/mate/matelib/dns.mate6
-rw-r--r--plugins/epan/mate/matelib/h225_ras.mate9
-rw-r--r--plugins/epan/mate/matelib/isup.mate23
-rw-r--r--plugins/epan/mate/matelib/megaco.mate8
-rw-r--r--plugins/epan/mate/matelib/q931.mate6
-rw-r--r--plugins/epan/mate/matelib/radius.mate12
-rw-r--r--plugins/epan/mate/matelib/rtsp.mate10
-rw-r--r--plugins/epan/mate/matelib/sip.mate12
-rw-r--r--plugins/epan/mate/packet-mate.c453
24 files changed, 5803 insertions, 0 deletions
diff --git a/plugins/epan/mate/.editorconfig b/plugins/epan/mate/.editorconfig
new file mode 100644
index 00000000..2ee8bf0b
--- /dev/null
+++ b/plugins/epan/mate/.editorconfig
@@ -0,0 +1,3 @@
+[mate_grammar.lemon]
+indent_style = tab
+indent_size = tab
diff --git a/plugins/epan/mate/AUTHORS b/plugins/epan/mate/AUTHORS
new file mode 100644
index 00000000..e37df0dd
--- /dev/null
+++ b/plugins/epan/mate/AUTHORS
@@ -0,0 +1,3 @@
+Author:
+Luis E. Garcia Ontanon <luis.ontanon [AT] gmail.com>
+
diff --git a/plugins/epan/mate/CMakeLists.txt b/plugins/epan/mate/CMakeLists.txt
new file mode 100644
index 00000000..62b6c090
--- /dev/null
+++ b/plugins/epan/mate/CMakeLists.txt
@@ -0,0 +1,85 @@
+# CMakeLists.txt
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+include(WiresharkPlugin)
+
+# Plugin name and version info (major minor micro extra)
+set_module_info(mate 1 0 1 0)
+
+set(DISSECTOR_SRC
+ packet-mate.c
+)
+
+set(DISSECTOR_SUPPORT_SRC
+ mate_setup.c
+ mate_runtime.c
+ mate_util.c
+)
+
+add_lemon_files(LEMON_FILES GENERATED_FILES
+ mate_grammar.lemon
+)
+add_lex_files(LEX_FILES GENERATED_FILES
+ mate_parser.l
+)
+
+set(PLUGIN_FILES
+ plugin.c
+ ${DISSECTOR_SRC}
+ ${DISSECTOR_SUPPORT_SRC}
+ ${GENERATED_FILES}
+)
+
+set_source_files_properties(
+ ${DISSECTOR_SRC}
+ ${DISSECTOR_SUPPORT_SRC}
+ PROPERTIES
+ COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
+)
+
+register_plugin_files(plugin.c
+ plugin
+ ${DISSECTOR_SRC}
+ ${DISSECTOR_SUPPORT_SRC}
+)
+
+add_wireshark_plugin_library(mate epan)
+
+target_link_libraries(mate epan)
+
+install_plugin(mate epan)
+
+file(GLOB DISSECTOR_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.h")
+CHECKAPI(
+ NAME
+ mate
+ SWITCHES
+ --group dissectors-prohibited
+ --group dissectors-restricted
+ SOURCES
+ ${DISSECTOR_SRC}
+ ${DISSECTOR_SUPPORT_SRC}
+ ${DISSECTOR_HEADERS}
+# LEX files commented out due to use of malloc, free etc.
+# ${LEX_FILES}
+ ${LEMON_FILES}
+)
+
+#
+# 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:
+#
diff --git a/plugins/epan/mate/examples/call.mate b/plugins/epan/mate/examples/call.mate
new file mode 100644
index 00000000..0dbbb45e
--- /dev/null
+++ b/plugins/epan/mate/examples/call.mate
@@ -0,0 +1,34 @@
+# call.mate
+
+Action=Settings; DiscardPduData=TRUE; ShowGopTimes=FALSE; ShowPduTree=FALSE;
+
+Action=PduDef; Name=q931_pdu; Proto=q931; Stop=TRUE; Transport=tcp/ip; addr=ip.addr; call_ref=q931.call_ref; q931_msg=q931.message_type;
+Action=PduDef; Name=ras_pdu; Proto=h225.RasMessage; Transport=udp/ip; addr=ip.addr; ras_sn=h225.requestSeqNum; ras_msg=h225.RasMessage;
+Action=PduDef; Name=isup_pdu; Proto=isup; Transport=mtp3; m3pc=mtp3.dpc; m3pc=mtp3.opc; cic=isup.cic; isup_msg=isup.message_type;
+
+Action=PduExtra; For=q931_pdu; guid=h225.guid; calling=q931.calling_party_number.digits; q931_cause=q931.cause_value;
+Action=PduExtra; For=isup_pdu; calling=isup.calling; isup_cause=isup.cause_indicator;
+Action=PduExtra; For=ras_pdu; guid=h225.guid;
+
+Action=GopDef; Name=q931_leg; On=q931_pdu; addr; addr; call_ref;
+Action=GopStart; For=q931_leg; q931_msg=5;
+Action=GopStop; For=q931_leg; q931_msg=90;
+Action=GopExtra; For=q931_leg; calling; q931_cause; guid;
+
+Action=GopDef; Name=isup_leg; On=isup_pdu; ShowPduTree=TRUE; ShowGopTimes=TRUE; m3pc; m3pc; cic;
+Action=GopStart; For=isup_leg; isup_msg=1;
+Action=GopStop; For=isup_leg; isup_msg=16;
+Action=GopExtra; For=isup_leg; calling; isup_cause;
+
+Action=GopDef; Name=ras_leg; On=ras_pdu; addr; addr; ras_sn;
+Action=GopStart; For=ras_leg; ras_msg|0|3|6|9|12|15|18|21|26|30;
+Action=GopStop; For=ras_leg; ras_msg|1|2|4|5|7|8|10|11|13|14|16|17|19|20|22|24|27|28|29|31;
+Action=GopExtra; For=ras_leg; guid;
+
+Action=GogDef; Name=call; GogExpiration=0.75;
+Action=GogKey; For=call; On=isup_leg; calling;
+Action=GogKey; For=call; On=q931_leg; calling;
+Action=GogKey; For=call; On=q931_leg; guid;
+Action=GogKey; For=call; On=ras_leg; guid;
+Action=GogExtra; For=call; isup_cause;
+Action=GogExtra; For=call; q931_cause;
diff --git a/plugins/epan/mate/examples/mms.mate b/plugins/epan/mate/examples/mms.mate
new file mode 100644
index 00000000..7554b119
--- /dev/null
+++ b/plugins/epan/mate/examples/mms.mate
@@ -0,0 +1,40 @@
+# mms.mate
+
+# MMSE over HTTP
+Action=PduDef; Name=mmse_over_http_pdu; Proto=http; Transport=tcp/ip; Payload=mmse; addr=ip.addr; port=tcp.port; http_rq=http.request; content=http.content_type;
+Action=PduExtra; For=mmse_over_http_pdu; resp=http.response.code; method=http.request.method; host=http.host; content=http.content_type;
+Action=PduExtra; For=mmse_over_http_pdu; method=http.request.method; host=http.host;
+Action=PduExtra; For=mmse_over_http_pdu; trx=mmse.transaction_id; msg_type=mmse.message_type; notify_status=mmse.status; send_status=mmse.response_status;
+
+Action=Transform; Name=rm_client_from_http_resp1; Mode=Insert; Match=Strict; http_rq;
+Action=Transform; Name=rm_client_from_http_resp1; Mode=Insert; Match=Every; addr; .not_rq;
+
+Action=Transform; Name=rm_client_from_http_resp2; Mode=Replace; Match=Strict; not_rq; ue;
+
+Action=PduTransform; For=mmse_over_http_pdu; Name=rm_client_from_http_resp1;
+Action=PduTransform; For=mmse_over_http_pdu; Name=rm_client_from_http_resp2;
+
+Action=GopDef; Name=mmse_over_http; On=mmse_over_http_pdu; addr; addr; port; port;
+Action=GopStart; For=mmse_over_http; http_rq;
+Action=GopStop; For=mmse_over_http; http_rs;
+
+Action=GopExtra; For=mmse_over_http; host; ue; resp; notify_status; send_status; trx;
+
+# MMSE over WSP
+Action=PduDef; Name=mmse_over_wsp_pdu; Proto=wsp; Payload=mmse; Transport=ip; trx=mmse.transaction_id; msg_type=mmse.message_type; notify_status=mmse.status; send_status=mmse.response_status;
+
+Action=Transform; Name=mms_start; Match=Loose; .mms_start;
+
+Action=PduTransform; Name=mms_start; For=mmse_over_wsp_pdu;
+
+Action=GopDef; Name=mmse_over_wsp; On=mmse_over_wsp_pdu; trx;
+Action=GopStart; For=mmse_over_wsp; mms_start;
+Action=GopStop; For=mmse_over_wsp; never;
+
+Action=GopExtra; For=mmse_over_wsp; ue; notify_status; send_status;
+
+# the MMS GoG
+Action=GogDef; Name=mms; GogExpiration=60.0;
+Action=GogKey; For=mms; On=mmse_over_http; trx;
+Action=GogKey; For=mms; On=mmse_over_wsp; trx;
+Action=GogExtra; For=mms; ue; notify_status; send_status; resp; host; trx;
diff --git a/plugins/epan/mate/examples/pasv_ftp.mate b/plugins/epan/mate/examples/pasv_ftp.mate
new file mode 100644
index 00000000..24ef7ab4
--- /dev/null
+++ b/plugins/epan/mate/examples/pasv_ftp.mate
@@ -0,0 +1,18 @@
+# pasv_ftp.mate
+
+Action=PduDef; Name=ftp_pdu; Proto=ftp; Transport=tcp/ip; Stop=TRUE; ftp_addr=ip.addr; ftp_port=tcp.port; ftp_resp=ftp.response.code; ftp_req=ftp.request.command; server_addr=ftp.passive.ip; server_port=ftp.passive.port;
+
+Action=PduDef; Name=ftp_data_pdu; Proto=ftp-data; Transport=tcp/ip; server_addr=ip.src; server_port=tcp.srcport;
+
+Action=GopDef; Name=ftp_data; On=ftp_data_pdu; server_addr; server_port;
+Action=GopStart; For=ftp_data; server_addr;
+
+Action=GopDef; Name=ftp_ctl; On=ftp_pdu; ftp_addr; ftp_addr; ftp_port; ftp_port;
+Action=GopStart; For=ftp_ctl; ftp_resp=220;
+Action=GopStop; For=ftp_ctl; ftp_resp=221;
+Action=GopExtra; For=ftp_ctl; server_addr; server_port;
+
+Action=GogDef; Name=ftp_ses;
+Action=GogKey; For=ftp_ses; On=ftp_ctl; ftp_addr; ftp_addr; ftp_port; ftp_port;
+Action=GogKey; For=ftp_ses; On=ftp_data; server_addr; server_port;
+
diff --git a/plugins/epan/mate/examples/tcp.mate b/plugins/epan/mate/examples/tcp.mate
new file mode 100644
index 00000000..2abe3e88
--- /dev/null
+++ b/plugins/epan/mate/examples/tcp.mate
@@ -0,0 +1,7 @@
+# tcp.mate
+
+ Action=PduDef; Name=tcp_pdu; Proto=tcp; Transport=ip; addr=ip.addr; port=tcp.port; tcp_start=tcp.flags.syn; tcp_stop=tcp.flags.fin; tcp_stop=tcp.flags.reset;
+ Action=GopDef; Name=tcp_session; On=tcp_pdu; addr; addr; port; port;
+ Action=GopStart; For=tcp_session; tcp_start=1;
+ Action=GopStop; For=tcp_session; tcp_stop=1;
+
diff --git a/plugins/epan/mate/examples/web.mate b/plugins/epan/mate/examples/web.mate
new file mode 100644
index 00000000..fd00c651
--- /dev/null
+++ b/plugins/epan/mate/examples/web.mate
@@ -0,0 +1,27 @@
+# web.mate
+
+Action=PduDef; Name=dns_pdu; Proto=dns; Transport=ip; addr=ip.addr; dns_resp=dns.flags.response; host=dns.qry.name; client_addr=ip.src; dns_id=dns.id;
+Action=PduDef; Name=http_pdu; Proto=http; Transport=tcp/ip; addr=ip.addr; port=tcp.port; http_rq=http.request.method; http_rs=http.response; host=http.host; client_addr=ip.src;
+
+Action=GopDef; Name=dns_req; On=dns_pdu; addr; addr; dns_id;
+Action=GopStart; For=dns_req; dns_resp=0;
+Action=GopStop; For=dns_req; dns_resp=1;
+
+Action=GopDef; Name=http_req; On=http_pdu; addr; addr; port; port;
+Action=GopStart; For=http_req; http_rq;
+Action=GopStop; For=http_req; http_rs;
+
+Action=Transform; Name=rm_client_from_dns_resp; Mode=Replace; Match=Every; dns_resp=1; client_addr; .dns_resp=1;
+Action=PduTransform; For=dns_pdu; Name=rm_client_from_dns_resp;
+
+Action=Transform; Name=rm_client_from_http_resp; Mode=Replace; Match=Every; http_rs; client_addr; .http_rs=;
+Action=PduTransform; For=http_pdu; Name=rm_client_from_http_resp;
+
+Action=GopExtra; For=http_req; host; client_addr;
+Action=GopExtra; For=dns_req; host; client_addr;
+
+Action=GogDef; Name=http_use; GogExpiration=0.75;
+Action=GogKey; For=http_use; On=http_req; host; client_addr;
+Action=GogKey; For=http_use; On=dns_req; host;client_addr;
+
+Action=GogExtra; For=http_use; host; client_addr;
diff --git a/plugins/epan/mate/mate.h b/plugins/epan/mate/mate.h
new file mode 100644
index 00000000..79f46af8
--- /dev/null
+++ b/plugins/epan/mate/mate.h
@@ -0,0 +1,373 @@
+/* mate.h
+ * MATE -- Meta Analysis and Tracing Engine
+ *
+ * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#ifndef __MATE_H_
+#define __MATE_H_
+
+#define WS_LOG_DOMAIN "MATE"
+#include <wireshark.h>
+
+#include <gmodule.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <wsutil/report_message.h>
+#include <wsutil/wslog.h>
+
+#include <epan/packet.h>
+#include <epan/exceptions.h>
+#include <epan/strutil.h>
+#include <epan/prefs.h>
+#include <epan/proto.h>
+#include <epan/epan_dissect.h>
+#include <wsutil/filesystem.h>
+
+#include "mate_util.h"
+
+/* defaults */
+
+#define DEFAULT_GOG_EXPIRATION 2.0
+
+#ifdef _WIN32
+#define DIR_SEP '\\'
+#else
+#define DIR_SEP '/'
+#endif
+
+#define DEFAULT_MATE_LIB_PATH "matelib"
+
+#define MATE_ITEM_ID_SIZE 24
+
+#define VALUE_TOO ((void*)1)
+
+#define MateConfigError 65535
+
+typedef enum _gop_tree_mode_t {
+ GOP_NULL_TREE,
+ GOP_BASIC_TREE,
+ GOP_FULL_TREE
+} gop_tree_mode_t;
+
+typedef enum _gop_pdu_tree {
+ GOP_NO_TREE,
+ GOP_PDU_TREE,
+ GOP_FRAME_TREE,
+ GOP_BASIC_PDU_TREE
+} gop_pdu_tree_t;
+
+typedef enum _accept_mode_t {
+ ACCEPT_MODE,
+ REJECT_MODE
+} accept_mode_t;
+
+
+typedef struct _mate_cfg_pdu {
+ gchar* name;
+ guint last_id; /* keeps the last id given to an item of this kind */
+
+ GHashTable* items; /* all the items of this type */
+ GPtrArray* transforms; /* transformations to be applied */
+
+ int hfid;
+
+ int hfid_proto;
+ int hfid_pdu_rel_time;
+ int hfid_pdu_time_in_gop;
+
+ GHashTable* my_hfids; /* for creating register info */
+
+ gint ett;
+ gint ett_attr;
+
+ GHashTable* hfids_attr; /* k=hfid v=avp_name */
+
+ gboolean discard;
+ gboolean last_extracted;
+ gboolean drop_unassigned;
+
+ GPtrArray* transport_ranges; /* hfids of candidate transport ranges from which to extract attributes */
+ GPtrArray* payload_ranges; /* hfids of candidate payload ranges from which to extract attributes */
+
+ avpl_match_mode criterium_match_mode;
+ accept_mode_t criterium_accept_mode;
+ AVPL* criterium;
+} mate_cfg_pdu;
+
+
+typedef struct _mate_cfg_gop {
+ gchar* name;
+ guint last_id; /* keeps the last id given to an item of this kind */
+ GHashTable* items; /* all the items of this type */
+
+ GPtrArray* transforms; /* transformations to be applied */
+ gchar* on_pdu;
+
+ AVPL* key; /* key candidate avpl */
+ AVPL* start; /* start candidate avpl */
+ AVPL* stop; /* stop candidate avpl */
+ AVPL* extra; /* attributes to be added */
+
+ float expiration;
+ float idle_timeout;
+ float lifetime;
+
+ gboolean drop_unassigned;
+ gop_pdu_tree_t pdu_tree_mode;
+ gboolean show_times;
+
+ GHashTable* my_hfids; /* for creating register info */
+ int hfid;
+ int hfid_start_time;
+ int hfid_stop_time;
+ int hfid_last_time;
+ int hfid_gop_pdu;
+ int hfid_gop_num_pdus;
+
+ gint ett;
+ gint ett_attr;
+ gint ett_times;
+ gint ett_children;
+
+ GHashTable* gop_index;
+ GHashTable* gog_index;
+} mate_cfg_gop;
+
+
+typedef struct _mate_cfg_gog {
+ gchar* name;
+
+ GHashTable* items; /* all the items of this type */
+ guint last_id; /* keeps the last id given to an item of this kind */
+
+ GPtrArray* transforms; /* transformations to be applied */
+
+ LoAL* keys;
+ AVPL* extra; /* attributes to be added */
+
+ float expiration;
+ gop_tree_mode_t gop_tree_mode;
+ gboolean show_times;
+
+ GHashTable* my_hfids; /* for creating register info */
+ int hfid;
+ int hfid_gog_num_of_gops;
+ int hfid_gog_gop;
+ int hfid_gog_gopstart;
+ int hfid_gog_gopstop;
+ int hfid_start_time;
+ int hfid_stop_time;
+ int hfid_last_time;
+ gint ett;
+ gint ett_attr;
+ gint ett_times;
+ gint ett_children;
+ gint ett_gog_gop;
+} mate_cfg_gog;
+
+typedef struct _mate_config {
+ gchar* mate_config_file; /* name of the config file */
+
+ int hfid_mate;
+
+ GArray *wanted_hfids; /* hfids of protocols and fields MATE needs */
+ guint num_fields_wanted; /* number of fields MATE will look at */
+
+ FILE* dbg_facility; /* where to dump dbgprint output ws_message if null */
+
+ gchar* mate_lib_path; /* where to look for "Include" files first */
+
+ GHashTable* pducfgs; /* k=pducfg->name v=pducfg */
+ GHashTable* gopcfgs; /* k=gopcfg->name v=gopcfg */
+ GHashTable* gogcfgs; /* k=gogcfg->name v=gogcfg */
+ GHashTable* transfs; /* k=transform->name v=transform */
+
+ GPtrArray* pducfglist; /* pducfgs in order of "execution" */
+ GHashTable* gops_by_pduname; /* k=pducfg->name v=gopcfg */
+ GHashTable* gogs_by_gopname; /* k=gopname v=loal where avpl->name == matchedgop->name */
+
+ GArray* hfrs;
+ gint ett_root;
+ GArray* ett;
+
+ /* defaults */
+ struct _mate_cfg_defaults {
+ struct _pdu_defaults {
+ avpl_match_mode match_mode;
+ avpl_replace_mode replace_mode;
+ gboolean last_extracted;
+
+ gboolean drop_unassigned;
+ gboolean discard;
+ } pdu;
+
+ struct _gop_defaults {
+ float expiration;
+ float idle_timeout;
+ float lifetime;
+
+ gop_pdu_tree_t pdu_tree_mode;
+ gboolean show_times;
+ gboolean drop_unassigned;
+
+ } gop;
+
+ struct _gog_defaults {
+ float expiration;
+ gboolean show_times;
+ gop_tree_mode_t gop_tree_mode;
+ } gog;
+ } defaults;
+
+ /* what to dbgprint */
+ int dbg_lvl;
+ int dbg_pdu_lvl;
+ int dbg_gop_lvl;
+ int dbg_gog_lvl;
+
+ GPtrArray* config_stack;
+ GString* config_error;
+
+} mate_config;
+
+
+typedef struct _mate_config_frame {
+ gchar* filename;
+ guint linenum;
+} mate_config_frame;
+
+
+typedef struct _mate_runtime_data {
+ guint current_items; /* a count of items */
+ float now;
+ guint highest_analyzed_frame;
+
+ GHashTable* frames; /* k=frame.num v=pdus */
+
+} mate_runtime_data;
+
+typedef struct _mate_pdu mate_pdu;
+typedef struct _mate_gop mate_gop;
+typedef struct _mate_gog mate_gog;
+
+/* these are used to contain information regarding pdus, gops and gogs */
+struct _mate_pdu {
+ guint32 id; /* 1:1 -> saving a g_malloc */
+ mate_cfg_pdu* cfg; /* the type of this item */
+
+ AVPL* avpl;
+
+ guint32 frame; /* wich frame I belog to? */
+ mate_pdu* next_in_frame; /* points to the next pdu in this frame */
+ float rel_time; /* time since start of capture */
+
+ mate_gop* gop; /* the gop the pdu belongs to (if any) */
+ mate_pdu* next; /* next in gop */
+ float time_in_gop; /* time since gop start */
+
+ gboolean first; /* is this the first pdu in this frame? */
+ gboolean is_start; /* this is the start pdu for this gop */
+ gboolean is_stop; /* this is the stop pdu for this gop */
+ gboolean after_release; /* this pdu comes after the stop */
+
+};
+
+
+struct _mate_gop {
+ guint32 id;
+ mate_cfg_gop* cfg;
+
+ gchar* gop_key;
+ AVPL* avpl; /* the attributes of the pdu/gop/gog */
+ guint last_n;
+
+ mate_gog* gog; /* the gog of a gop */
+ mate_gop* next; /* next in gog; */
+
+ float expiration; /* when will it expire after release (all gops releases if gog)? */
+ float idle_expiration; /* when will it expire if no new pdus are assigned to it */
+ float time_to_die;
+ float time_to_timeout;
+
+ float start_time; /* time of start */
+ float release_time; /* when this gop/gog was released */
+ float last_time; /* the rel_time at which the last pdu has been added (to gop or gog's gop) */
+
+
+ int num_of_pdus; /* how many gops a gog has? */
+ int num_of_after_release_pdus; /* how many pdus have arrived since it's been released */
+ mate_pdu* pdus; /* pdus that belong to a gop (NULL in gog) */
+ mate_pdu* last_pdu; /* last pdu in pdu's list */
+
+ gboolean released; /* has this gop been released? */
+};
+
+
+struct _mate_gog {
+ guint32 id;
+ mate_cfg_gog* cfg;
+
+ AVPL* avpl; /* the attributes of the pdu/gop/gog */
+ guint last_n; /* the number of attributes the avpl had the last time we checked */
+
+ gboolean released; /* has this gop been released? */
+
+ float expiration; /* when will it expire after release (all gops releases if gog)? */
+ float idle_expiration; /* when will it expire if no new pdus are assigned to it */
+
+ /* on gop and gog: */
+ float start_time; /* time of start */
+ float release_time; /* when this gog was released */
+ float last_time; /* the rel_time at which the last pdu has been added */
+
+ mate_gop* gops; /* gops that belong to a gog (NULL in gop) */
+ mate_gop* last_gop; /* last gop in gop's list */
+
+ int num_of_gops; /* how many gops a gog has? */
+ int num_of_counting_gops; /* how many of them count for gog release */
+ int num_of_released_gops; /* how many of them have already been released */
+ GPtrArray* gog_keys; /* the keys under which this gog is stored in the gogs hash */
+};
+
+typedef union _mate_max_size {
+ mate_pdu pdu;
+ mate_gop gop;
+ mate_gog gog;
+} mate_max_size;
+
+/* from mate_runtime.c */
+extern void initialize_mate_runtime(mate_config* mc);
+extern mate_pdu* mate_get_pdus(guint32 framenum);
+extern void mate_analyze_frame(mate_config *mc, packet_info *pinfo, proto_tree* tree);
+
+/* from mate_setup.c */
+extern mate_config* mate_make_config(const gchar* filename, int mate_hfid);
+
+extern mate_cfg_pdu* new_pducfg(mate_config* mc, gchar* name);
+extern mate_cfg_gop* new_gopcfg(mate_config* mc, gchar* name);
+extern mate_cfg_gog* new_gogcfg(mate_config* mc, gchar* name);
+
+extern gboolean add_hfid(mate_config* mc, header_field_info* hfi, gchar* as, GHashTable* where);
+extern gchar* add_ranges(gchar* range, GPtrArray* range_ptr_arr);
+
+
+/* from mate_parser.l */
+extern gboolean mate_load_config(const gchar* filename, mate_config* mc);
+
+/* Constructor/Destructor prototypes for Lemon Parser */
+#define YYMALLOCARGTYPE gsize
+void *MateParserAlloc(void* (*)(YYMALLOCARGTYPE));
+void MateParserFree(void*, void (*)(void *));
+void MateParser(void*, int, gchar*, mate_config*);
+
+#endif
diff --git a/plugins/epan/mate/mate_grammar.lemon b/plugins/epan/mate/mate_grammar.lemon
new file mode 100644
index 00000000..57f291fe
--- /dev/null
+++ b/plugins/epan/mate/mate_grammar.lemon
@@ -0,0 +1,729 @@
+%include {
+
+/* mate_grammar.lemon
+ * MATE's configuration language grammar
+ *
+ * Copyright 2005, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * XXX - there's a Lemon bug where this grammar produces a parser that
+ * fails assertions; to work around it, we disable assert() failures.
+ */
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+#include "config.h"
+
+#include <errno.h>
+
+#include "mate.h"
+#include "mate_grammar.h"
+#include <wsutil/file_util.h>
+#include <wsutil/str_util.h>
+
+#define DUMMY void*
+
+typedef struct _extraction {
+ gchar* as;
+ header_field_info* hfi;
+ struct _extraction* next;
+ struct _extraction* last;
+} extraction_t;
+
+typedef struct _pdu_criteria_t {
+ AVPL* criterium_avpl;
+ avpl_match_mode criterium_match_mode;
+ accept_mode_t criterium_accept_mode;
+} pdu_criteria_t;
+
+typedef struct _gop_options {
+ gop_tree_mode_t pdu_tree_mode;
+ gboolean drop_unassigned;
+ gboolean show_times;
+ float expiration;
+ float idle_timeout;
+ float lifetime;
+ AVPL* start;
+ AVPL* stop;
+ AVPL* extras;
+} gop_options_t;
+
+typedef struct _gog_statements {
+ float expiration;
+ gop_tree_mode_t gop_tree_mode;
+ GPtrArray* transform_list;
+ AVPL* extras;
+ LoAL* current_gogkeys;
+} gog_statement_t;
+
+typedef struct _transf_match_t {
+ avpl_match_mode match_mode;
+ AVPL* avpl;
+} transf_match_t;
+
+typedef struct _transf_action_t {
+ avpl_replace_mode replace_mode;
+ AVPL* avpl;
+} transf_action_t;
+
+static void configuration_error(mate_config* mc, const gchar* fmt, ...) {
+ static gchar error_buffer[256];
+ const gchar* incl;
+ gint i;
+ mate_config_frame* current_frame;
+ va_list list;
+
+ va_start( list, fmt );
+ vsnprintf(error_buffer,sizeof(error_buffer),fmt,list);
+ va_end( list );
+
+ i = (gint) mc->config_stack->len;
+
+ while (i--) {
+
+ if (i>0) {
+ incl = "\n included from: ";
+ } else {
+ incl = " ";
+ }
+
+ current_frame = (mate_config_frame *)g_ptr_array_index(mc->config_stack,(guint)i);
+
+ g_string_append_printf(mc->config_error,"%s%s at line %u",incl, current_frame->filename, current_frame->linenum);
+ }
+
+ g_string_append_printf(mc->config_error,": %s\n",error_buffer);
+
+ THROW(MateConfigError);
+
+}
+
+static AVPL_Transf* new_transform_elem(AVPL* match, AVPL* replace, avpl_match_mode match_mode, avpl_replace_mode replace_mode) {
+ AVPL_Transf* t = (AVPL_Transf *)g_malloc(sizeof(AVPL_Transf));
+
+ t->name = NULL;
+ t->match = match;
+ t->replace = replace;
+ t->match_mode = match_mode;
+ t->replace_mode = replace_mode;
+
+ t->map = NULL;
+ t->next = NULL;
+
+ return t;
+}
+
+static gchar* recolonize(mate_config* mc, gchar* s) {
+ GString* str = g_string_new("");
+ gchar** vec;
+ gchar* r;
+ guint i,v;
+ gchar c;
+
+ vec = g_strsplit(s,":",0);
+
+ for (i = 0; vec[i]; i++) {
+ ascii_strdown_inplace(vec[i]);
+
+ v = 0;
+ switch ( strlen(vec[i]) ) {
+ case 2:
+ c = vec[i][1];
+ vec[i][1] = vec[i][0];
+ vec[i][0] = c;
+ if (vec[i][0] >= '0' && vec[i][0] <= '9') {
+ v += (vec[i][1] - '0' )*16;
+ } else {
+ v += (vec[i][1] - 'a' + 10)*16;
+ }
+ /* FALL THROUGH */
+ case 1:
+ if (vec[i][0] >= '0' && vec[i][0] <= '9') {
+ v += (vec[i][0] - '0' );
+ } else {
+ v += (vec[i][0] - 'a' + 10);
+ }
+ case 0:
+ break;
+ default:
+ configuration_error(mc,"bad token %s",s);
+ }
+
+ g_string_append_printf(str,":%.2X",v);
+ }
+
+ g_strfreev(vec);
+
+ g_string_erase(str,0,1);
+
+ r = str->str;
+
+ g_string_free(str,FALSE);
+
+ return r;
+}
+
+DIAG_OFF_LEMON()
+} /* end of %include */
+
+%code {
+DIAG_ON_LEMON()
+}
+
+%name MateParser
+
+%token_prefix TOKEN_
+
+%token_type { gchar* }
+%token_destructor {
+ (void) mc; /* Mark unused, similar to Q_UNUSED */
+ g_free($$);
+}
+
+%extra_argument { mate_config* mc }
+
+%syntax_error {
+ configuration_error(mc,"Syntax Error before %s",yyminor);
+}
+
+%parse_failure {
+ configuration_error(mc,"Parse Error");
+}
+
+%type transform_decl { AVPL_Transf* }
+%type transform_body { AVPL_Transf* }
+%type transform_statements { AVPL_Transf* }
+%type transform_statement { AVPL_Transf* }
+%type transform_match { transf_match_t* }
+%type transform_action { transf_action_t* }
+%type match_mode { avpl_match_mode }
+%type action_mode { avpl_replace_mode }
+
+%type gop_name { gchar* }
+%type time_value { float }
+%type pdu_name { gchar* }
+%type gop_tree_mode { gop_tree_mode_t }
+%type true_false { gboolean }
+
+%type criteria_statement { pdu_criteria_t* }
+%type accept_mode { accept_mode_t }
+%type pdu_drop_unassigned_statement { gboolean }
+%type discard_pdu_data_statement { gboolean }
+%type last_extracted_statement { gboolean }
+
+%type extraction_statement {extraction_t*}
+%type extraction_statements {extraction_t*}
+
+%type gop_options { gop_options_t* }
+
+%type gop_start_statement { AVPL* }
+%type gop_stop_statement { AVPL* }
+%type extra_statement { AVPL* }
+%type gop_drop_unassigned_statement { gboolean }
+%type show_goptree_statement { gop_tree_mode_t }
+%type show_times_statement { gboolean }
+%type gop_expiration_statement { float }
+%type idle_timeout_statement { float }
+%type lifetime_statement { float }
+
+%type gog_statements { gog_statement_t* }
+%type gog_expiration_statement { float }
+%type gog_goptree_statement { gop_tree_mode_t }
+%type gog_key_statements { LoAL* }
+%type gog_key_statement { AVPL* }
+%type transform_list_statement { GPtrArray* }
+%type transform { AVPL_Transf* }
+%type gop_tree_type { gop_tree_mode_t }
+
+%type payload_statement { GPtrArray* }
+%type proto_stack { GPtrArray* }
+%type field { header_field_info* }
+%type transform_list { GPtrArray* }
+%type avpl { AVPL* }
+%type avps { AVPL* }
+%type avp { AVP* }
+%type value { gchar* }
+%type avp_oneoff { gchar* }
+
+
+mate_config ::= decls.
+
+decls ::= decls decl.
+decls ::= .
+
+decl ::= pdu_decl.
+decl ::= gop_decl.
+decl ::= gog_decl.
+decl ::= transform_decl.
+decl ::= defaults_decl.
+decl ::= debug_decl.
+decl ::= DONE_KW SEMICOLON.
+
+/************* DEBUG
+*/
+
+debug_decl ::= DEBUG_KW OPEN_BRACE dbgfile_default dbglevel_default pdu_dbglevel_default gop_dbglevel_default gog_dbglevel_default CLOSE_BRACE SEMICOLON.
+
+dbgfile_default ::= FILENAME_KW QUOTED(Filename) SEMICOLON. { mc->dbg_facility = ws_fopen(Filename,"w"); if (mc->dbg_facility == NULL) report_open_failure(Filename,errno,TRUE); }
+dbgfile_default ::= FILENAME_KW NAME(Filename) SEMICOLON. { mc->dbg_facility = ws_fopen(Filename,"w"); if (mc->dbg_facility == NULL) report_open_failure(Filename,errno,TRUE); }
+dbgfile_default ::= .
+
+dbglevel_default ::= LEVEL_KW INTEGER(LevelString) SEMICOLON. { mc->dbg_lvl = (int) strtol(LevelString,NULL,10); }
+dbglevel_default ::= .
+
+pdu_dbglevel_default ::= PDU_KW LEVEL_KW INTEGER(LevelString) SEMICOLON. { mc->dbg_pdu_lvl = (int) strtol(LevelString,NULL,10); }
+pdu_dbglevel_default ::= .
+
+gop_dbglevel_default ::= GOP_KW LEVEL_KW INTEGER(LevelString) SEMICOLON. { mc->dbg_gop_lvl = (int) strtol(LevelString,NULL,10); }
+gop_dbglevel_default ::= .
+
+gog_dbglevel_default ::= GOG_KW LEVEL_KW INTEGER(LevelString) SEMICOLON. { mc->dbg_gog_lvl = (int) strtol(LevelString,NULL,10); }
+gog_dbglevel_default ::= .
+
+
+/************* DEFAULTS
+*/
+
+defaults_decl ::= DEFAULT_KW OPEN_BRACE pdu_defaults gop_defaults gog_defaults CLOSE_BRACE SEMICOLON.
+
+pdu_defaults ::= PDU_KW OPEN_BRACE pdu_last_extracted_default pdu_drop_unassigned_default pdu_discard_default CLOSE_BRACE SEMICOLON.
+pdu_defaults ::= .
+
+pdu_last_extracted_default ::= LAST_EXTRACTED_KW true_false(Flag) SEMICOLON. { mc->defaults.pdu.last_extracted = Flag; }
+pdu_last_extracted_default ::= .
+
+pdu_drop_unassigned_default ::= DROP_UNASSIGNED_KW true_false(Flag) SEMICOLON. { mc->defaults.pdu.drop_unassigned = Flag; }
+pdu_drop_unassigned_default ::= .
+
+pdu_discard_default ::= DISCARD_PDU_DATA_KW true_false(Flag) SEMICOLON. { mc->defaults.pdu.discard = Flag; }
+pdu_discard_default ::= .
+
+gop_defaults ::= GOP_KW OPEN_BRACE gop_expiration_default gop_idle_timeout_default gop_lifetime_default gop_drop_unassigned_default gop_tree_mode_default gop_show_times_default CLOSE_BRACE SEMICOLON.
+gop_defaults ::= .
+
+gop_expiration_default ::= EXPIRATION_KW time_value(B) SEMICOLON. { mc->defaults.gop.expiration = B; }
+gop_expiration_default ::= .
+
+gop_idle_timeout_default ::= IDLE_TIMEOUT_KW time_value(B) SEMICOLON. { mc->defaults.gop.idle_timeout = B; }
+gop_idle_timeout_default ::= .
+
+gop_lifetime_default ::= LIFETIME_KW time_value(B) SEMICOLON. { mc->defaults.gop.lifetime = B; }
+gop_lifetime_default ::= .
+
+gop_drop_unassigned_default ::= DROP_UNASSIGNED_KW true_false(B) SEMICOLON. { mc->defaults.gop.drop_unassigned = B; }
+gop_drop_unassigned_default ::= .
+
+gop_tree_mode_default ::= SHOW_TREE_KW gop_tree_mode(B) SEMICOLON. { mc->defaults.gop.pdu_tree_mode = (gop_pdu_tree_t)B; }
+gop_tree_mode_default ::= .
+
+gop_show_times_default ::= SHOW_TIMES_KW true_false(B) SEMICOLON. { mc->defaults.gop.show_times = B; }
+gop_show_times_default ::= .
+
+gog_defaults ::= GOG_KW OPEN_BRACE gog_expiration_default gop_tree_mode_default gog_goptree_default gog_show_times_default CLOSE_BRACE SEMICOLON.
+gog_defaults ::= .
+
+gog_expiration_default ::= EXPIRATION_KW time_value(B) SEMICOLON. { mc->defaults.gop.expiration = B; }
+gog_expiration_default ::= .
+
+gog_goptree_default ::= GOP_TREE_KW gop_tree_type(B) SEMICOLON. { mc->defaults.gog.gop_tree_mode = B; }
+gog_goptree_default ::= .
+
+gog_show_times_default ::= SHOW_TIMES_KW true_false(B) SEMICOLON. { mc->defaults.gog.show_times = B; }
+gog_show_times_default ::= .
+
+
+/******************************************* TRANSFORM
+*/
+
+transform_decl(A) ::= TRANSFORM_KW NAME(B) transform_body(C) SEMICOLON. {
+ AVPL_Transf* c;
+
+ if ( g_hash_table_lookup(mc->transfs,B) ) {
+ configuration_error(mc,"A transformation called '%s' exists already",B);
+ }
+
+ for ( c = C; c; c = c->next )
+ c->name = g_strdup(B);
+
+ g_hash_table_insert(mc->transfs,C->name,C);
+
+ A = NULL;
+}
+
+transform_body(A) ::= OPEN_BRACE transform_statements(B) CLOSE_BRACE. { A = B; }
+
+transform_statements(A) ::= transform_statements(C) transform_statement(B). {
+ AVPL_Transf* c;
+
+ for ( c = C; c->next; c = c->next ) ;
+ c->next = B;
+ A = C;
+}
+
+transform_statements(A) ::= transform_statement(B). { A = B; }
+
+transform_statement(A) ::= transform_match(Match) transform_action(Action) SEMICOLON. {
+ A = new_transform_elem(Match->avpl,Action->avpl,Match->match_mode,Action->replace_mode);
+}
+
+transform_match(A) ::= MATCH_KW match_mode(Mode) avpl(Avpl). {
+ A = (transf_match_t *)g_malloc(sizeof(transf_match_t));
+ A->match_mode = Mode;
+ A->avpl = Avpl;
+}
+
+transform_match(A) ::= . {
+ A = (transf_match_t *)g_malloc(sizeof(transf_match_t));
+ A->match_mode = AVPL_STRICT;
+ A->avpl = new_avpl("");
+
+}
+
+transform_action(A) ::= . {
+ A = (transf_action_t *)g_malloc(sizeof(transf_action_t));
+ A->replace_mode = AVPL_INSERT;
+ A->avpl = new_avpl("");
+}
+transform_action(A) ::= action_mode(Mode) avpl(Avpl). {
+ A = (transf_action_t *)g_malloc(sizeof(transf_action_t));
+ A->replace_mode = Mode;
+ A->avpl = Avpl;
+}
+
+match_mode(A) ::= . { A = AVPL_STRICT; }
+match_mode(A) ::= STRICT_KW. { A = AVPL_STRICT; }
+match_mode(A) ::= EVERY_KW. { A = AVPL_EVERY; }
+match_mode(A) ::= LOOSE_KW. { A = AVPL_LOOSE; }
+
+action_mode(A) ::= REPLACE_KW. { A = AVPL_REPLACE; }
+action_mode(A) ::= INSERT_KW. { A = AVPL_INSERT; }
+action_mode(A) ::= . { A = AVPL_INSERT; }
+
+/******************************************* PDU
+*/
+
+pdu_decl ::=
+ PDU_KW NAME(Name) PROTO_KW field(Field) TRANSPORT_KW proto_stack(Stack)
+ OPEN_BRACE
+ payload_statement(Payload)
+ extraction_statements(Extraction)
+ transform_list_statement(Transform)
+ criteria_statement(Criteria)
+ pdu_drop_unassigned_statement(DropUnassigned)
+ discard_pdu_data_statement(DistcardPduData)
+ last_extracted_statement(LastExtracted)
+ CLOSE_BRACE SEMICOLON.
+{
+
+ mate_cfg_pdu* cfg = new_pducfg(mc, Name);
+ extraction_t *extraction, *next_extraction;
+ GPtrArray* transport_stack = g_ptr_array_new();
+ int i;
+
+ if (! cfg ) configuration_error(mc,"could not create Pdu %s.",Name);
+
+ cfg->hfid_proto = Field->id;
+
+ cfg->last_extracted = LastExtracted;
+ cfg->discard = DistcardPduData;
+ cfg->drop_unassigned = DropUnassigned;
+
+ /*
+ * Add this protocol to our table of wanted hfids.
+ */
+ mc->wanted_hfids = g_array_append_val(mc->wanted_hfids, Field->id);
+
+ /* flip the transport_stack */
+ for (i = Stack->len - 1; Stack->len; i--) {
+ g_ptr_array_add(transport_stack,g_ptr_array_remove_index(Stack,i));
+ }
+
+ g_ptr_array_free(Stack, TRUE);
+
+ cfg->transport_ranges = transport_stack;
+ cfg->payload_ranges = Payload;
+
+ if (Criteria) {
+ cfg->criterium = Criteria->criterium_avpl;
+ cfg->criterium_match_mode = Criteria->criterium_match_mode;
+ cfg->criterium_accept_mode = Criteria->criterium_accept_mode;
+ }
+
+ cfg->transforms = Transform;
+
+ for (extraction = Extraction; extraction; extraction = next_extraction) {
+ next_extraction = extraction->next;
+
+ if ( ! add_hfid(mc, extraction->hfi, extraction->as, cfg->hfids_attr) ) {
+ configuration_error(mc,"MATE: failed to create extraction rule '%s'",extraction->as);
+ }
+
+ g_free(extraction);
+ }
+}
+
+payload_statement(A) ::= . { A = NULL; }
+payload_statement(A) ::= PAYLOAD_KW proto_stack(B) SEMICOLON. { A = B; }
+
+criteria_statement(A) ::= . { A = NULL; }
+criteria_statement(A) ::= CRITERIA_KW accept_mode(B) match_mode(C) avpl(D) SEMICOLON. {
+ A = g_new(pdu_criteria_t, 1);
+ A->criterium_avpl = D;
+ A->criterium_match_mode = C;
+ A->criterium_accept_mode = B;
+}
+
+accept_mode(A) ::= . { A = ACCEPT_MODE; }
+accept_mode(A) ::= ACCEPT_KW. { A = ACCEPT_MODE; }
+accept_mode(A) ::= REJECT_KW. { A = REJECT_MODE; }
+
+extraction_statements(A) ::= extraction_statements(B) extraction_statement(C). { A = B; A->last = A->last->next = C; }
+extraction_statements(A) ::= extraction_statement(B). { A = B; A->last = A; }
+
+extraction_statement(A) ::= EXTRACT_KW NAME(NAME) FROM_KW field(FIELD) SEMICOLON. {
+ A = g_new(extraction_t, 1);
+ A->as = NAME;
+ A->hfi = FIELD;
+ A->next = A->last = NULL;
+}
+
+
+pdu_drop_unassigned_statement(A) ::= DROP_UNASSIGNED_KW true_false(B) SEMICOLON. { A = B; }
+pdu_drop_unassigned_statement(A) ::= . { A = mc->defaults.pdu.drop_unassigned; }
+
+discard_pdu_data_statement(A) ::= DISCARD_PDU_DATA_KW true_false(B) SEMICOLON. { A = B; }
+discard_pdu_data_statement(A) ::= . { A = mc->defaults.pdu.discard; }
+
+last_extracted_statement(A) ::= LAST_PDU_KW true_false(B) SEMICOLON. { A = B; }
+last_extracted_statement(A) ::= . { A = mc->defaults.pdu.last_extracted; }
+
+proto_stack(A) ::= proto_stack(B) SLASH field(C). {
+ int* hfidp = g_new(int, 1);
+
+ *hfidp = C->id;
+ g_ptr_array_add(B,hfidp);
+ A = B;
+}
+
+proto_stack(A) ::= field(B). {
+ int* hfidp = g_new(int, 1);
+ *hfidp = B->id;
+
+ A = g_ptr_array_new();
+ g_ptr_array_add(A,hfidp);
+}
+
+field(A) ::= NAME(B). {
+ A = proto_registrar_get_byname(B);
+}
+
+/******************************************* GOP
+*/
+
+gop_decl(A) ::= GOP_KW NAME(Name) ON_KW pdu_name(PduName) MATCH_KW avpl(Key) OPEN_BRACE
+ gop_start_statement(Start)
+ gop_stop_statement(Stop)
+ extra_statement(Extra)
+ transform_list_statement(Transform)
+ gop_expiration_statement(Expiration)
+ idle_timeout_statement(IdleTimeout)
+ lifetime_statement(Lifetime)
+ gop_drop_unassigned_statement(DropUnassigned)
+ show_goptree_statement(TreeMode)
+ show_times_statement(ShowTimes)
+ CLOSE_BRACE SEMICOLON. {
+ mate_cfg_gop* cfg;
+
+ if (g_hash_table_lookup(mc->gopcfgs,Name)) configuration_error(mc,"A Gop Named '%s' exists already.",Name);
+ if (g_hash_table_lookup(mc->gops_by_pduname,PduName) ) configuration_error(mc,"Gop for Pdu '%s' exists already",PduName);
+
+ cfg = new_gopcfg(mc, Name);
+ g_hash_table_insert(mc->gops_by_pduname,PduName,cfg);
+ g_hash_table_insert(mc->gopcfgs,cfg->name,cfg);
+
+ cfg->on_pdu = PduName;
+ cfg->key = Key;
+ cfg->drop_unassigned = DropUnassigned;
+ cfg->show_times = ShowTimes;
+ cfg->pdu_tree_mode = (gop_pdu_tree_t)TreeMode;
+ cfg->expiration = Expiration;
+ cfg->idle_timeout = IdleTimeout;
+ cfg->lifetime = Lifetime;
+ cfg->start = Start;
+ cfg->stop = Stop;
+ cfg->transforms = Transform;
+
+ merge_avpl(cfg->extra,Extra,TRUE);
+ delete_avpl(Extra,TRUE);
+}
+
+gop_drop_unassigned_statement(A) ::= DROP_UNASSIGNED_KW true_false(B) SEMICOLON. { A = B; }
+gop_drop_unassigned_statement(A) ::= . { A = mc->defaults.gop.drop_unassigned; }
+
+gop_start_statement(A) ::= START_KW avpl(B) SEMICOLON. { A = B; }
+gop_start_statement(A) ::= . { A = NULL; }
+
+gop_stop_statement(A) ::= STOP_KW avpl(B) SEMICOLON. { A = B; }
+gop_stop_statement(A) ::= . { A = NULL; }
+
+show_goptree_statement(A) ::= SHOW_TREE_KW gop_tree_mode(B) SEMICOLON. { A = B; }
+show_goptree_statement(A) ::= . { A = (gop_tree_mode_t)mc->defaults.gop.pdu_tree_mode; }
+
+show_times_statement(A) ::= SHOW_TIMES_KW true_false(B) SEMICOLON. { A = B; }
+show_times_statement(A) ::= . { A = mc->defaults.gop.show_times; }
+
+gop_expiration_statement(A) ::= EXPIRATION_KW time_value(B) SEMICOLON. { A = B; }
+gop_expiration_statement(A) ::= . { A = mc->defaults.gop.lifetime; }
+
+idle_timeout_statement(A) ::= IDLE_TIMEOUT_KW time_value(B) SEMICOLON. { A = B; }
+idle_timeout_statement(A) ::= . { A = mc->defaults.gop.lifetime; }
+
+lifetime_statement(A) ::= LIFETIME_KW time_value(B) SEMICOLON. { A = B; }
+lifetime_statement(A) ::= . { A = mc->defaults.gop.lifetime; }
+
+gop_tree_mode(A) ::= NO_TREE_KW. { A = (gop_tree_mode_t)GOP_NO_TREE; }
+gop_tree_mode(A) ::= PDU_TREE_KW. { A = (gop_tree_mode_t)GOP_PDU_TREE; }
+gop_tree_mode(A) ::= FRAME_TREE_KW. { A = (gop_tree_mode_t)GOP_FRAME_TREE; }
+gop_tree_mode(A) ::= BASIC_TREE_KW. { A = (gop_tree_mode_t)GOP_BASIC_PDU_TREE; }
+
+true_false(A) ::= TRUE_KW. { A = TRUE; }
+true_false(A) ::= FALSE_KW. { A = FALSE; }
+
+pdu_name(A) ::= NAME(B). {
+ mate_cfg_pdu* c;
+ if (( c = (mate_cfg_pdu *)g_hash_table_lookup(mc->pducfgs,B) )) {
+ A = c->name;
+ } else {
+ configuration_error(mc,"No such Pdu: '%s'",B);
+ }
+}
+
+
+time_value(A) ::= FLOATING(B). {
+ A = (float) g_ascii_strtod(B,NULL);
+}
+
+time_value(A) ::= INTEGER(B). {
+ A = (float) g_ascii_strtod(B,NULL);
+}
+
+/************* GOG
+*/
+
+gog_decl ::= GOG_KW NAME(Name) OPEN_BRACE
+ gog_key_statements(Keys)
+ extra_statement(Extra)
+ transform_list_statement(Transforms)
+ gog_expiration_statement(Expiration)
+ gog_goptree_statement(Tree)
+ show_times_statement(ShowTimes)
+ CLOSE_BRACE SEMICOLON. {
+ mate_cfg_gog* cfg = NULL;
+
+ if ( g_hash_table_lookup(mc->gogcfgs,Name) ) {
+ configuration_error(mc,"Gog '%s' exists already ",Name);
+ }
+
+ cfg = new_gogcfg(mc, Name);
+
+ cfg->expiration = Expiration;
+ cfg->gop_tree_mode = Tree;
+ cfg->transforms = Transforms;
+ cfg->keys = Keys;
+ cfg->show_times = ShowTimes;
+
+ merge_avpl(cfg->extra,Extra,TRUE);
+ delete_avpl(Extra,TRUE);
+}
+
+gog_goptree_statement(A) ::= GOP_TREE_KW gop_tree_type(B) SEMICOLON. { A = B; }
+gog_goptree_statement(A) ::= . { A = mc->defaults.gog.gop_tree_mode; }
+
+gog_expiration_statement(A) ::= EXPIRATION_KW time_value(B) SEMICOLON. { A = B; }
+gog_expiration_statement(A) ::= . { A = mc->defaults.gog.expiration; }
+
+gop_tree_type(A) ::= NULL_TREE_KW. { A = GOP_NULL_TREE; }
+gop_tree_type(A) ::= FULL_TREE_KW. { A = GOP_FULL_TREE; }
+gop_tree_type(A) ::= BASIC_TREE_KW. { A = GOP_BASIC_TREE; }
+
+gog_key_statements(A) ::= gog_key_statements(B) gog_key_statement(C). {
+ loal_append(B,C);
+ A = B;
+}
+
+gog_key_statements(A) ::= gog_key_statement(B). {
+ A = new_loal("");
+ loal_append(A,B);
+}
+
+
+gog_key_statement(A) ::= MEMBER_KW gop_name(B) avpl(C) SEMICOLON. {
+ rename_avpl(C,B);
+ A = C;
+}
+
+gop_name(A) ::= NAME(B). {
+ mate_cfg_gop* c;
+ if (( c = (mate_cfg_gop *)g_hash_table_lookup(mc->gopcfgs,B) )) {
+ A = c->name;
+ } else {
+ configuration_error(mc,"No Gop called '%s' has been already declared",B);
+ }
+}
+/******************************************** GENERAL
+*/
+
+
+extra_statement(A) ::= EXTRA_KW avpl(B) SEMICOLON. { A = B; }
+extra_statement(A) ::= . { A = new_avpl(""); }
+
+transform_list_statement(A) ::= TRANSFORM_KW transform_list(B) SEMICOLON. { A = B; }
+transform_list_statement(A) ::= . { A = g_ptr_array_new(); }
+
+transform_list(A) ::= transform_list(B) COMMA transform(C). {
+ A = B;
+ g_ptr_array_add(B,C);
+}
+
+transform_list(A) ::= transform(B). {
+ A = g_ptr_array_new();
+ g_ptr_array_add(A,B);
+}
+
+transform(A) ::= NAME(B). {
+ AVPL_Transf* t;
+
+ if (( t = (AVPL_Transf *)g_hash_table_lookup(mc->transfs,B) )) {
+ A = t;
+ } else {
+ configuration_error(mc,"There's no such Transformation: %s",B);
+ }
+}
+
+avpl(A) ::= OPEN_PARENS avps(B) CLOSE_PARENS. { A = B; }
+avpl(A) ::= OPEN_PARENS CLOSE_PARENS. { A = new_avpl(""); }
+
+avps(A) ::= avps(B) COMMA avp(C). { A = B; if ( ! insert_avp(B,C) ) delete_avp(C); }
+avps(A) ::= avp(B). { A = new_avpl(""); if ( ! insert_avp(A,B) ) delete_avp(B); }
+
+avp(A) ::= NAME(B) AVP_OPERATOR(C) value(D). { A = new_avp(B,D,*C); }
+avp(A) ::= NAME(B). { A = new_avp(B,"",'?'); }
+avp(A) ::= NAME(B) OPEN_BRACE avp_oneoff(C) CLOSE_BRACE. { A = new_avp(B,C,'|'); }
+
+avp_oneoff(A) ::= avp_oneoff(B) PIPE value(C). { A = ws_strdup_printf("%s|%s",B,C); }
+avp_oneoff(A) ::= value(B). { A = g_strdup(B); }
+
+value(A) ::= QUOTED(B). { A = g_strdup(B); }
+value(A) ::= NAME(B). { A = g_strdup(B); }
+value(A) ::= FLOATING(B). { A = g_strdup(B); }
+value(A) ::= INTEGER(B). { A = g_strdup(B); }
+value(A) ::= DOTED_IP(B). { A = g_strdup(B); }
+value(A) ::= COLONIZED(B). { A = recolonize(mc,B); }
+
diff --git a/plugins/epan/mate/mate_parser.l b/plugins/epan/mate/mate_parser.l
new file mode 100644
index 00000000..6e423abc
--- /dev/null
+++ b/plugins/epan/mate/mate_parser.l
@@ -0,0 +1,409 @@
+%top {
+/* Include this before everything else, for various large-file definitions */
+#include "config.h"
+/* Include this before everything else, as it declares functions used here. */
+#include "mate.h"
+}
+
+/*
+ * We want a reentrant scanner.
+ */
+%option reentrant
+
+/*
+ * We don't use input, so don't generate code for it.
+ */
+%option noinput
+
+/*
+ * We don't use unput, so don't generate code for it.
+ */
+%option nounput
+
+/*
+ * We don't read interactively from the terminal.
+ */
+%option never-interactive
+
+/*
+ * We want to stop processing when we get to the end of the input.
+ */
+%option noyywrap
+
+/*
+ * The type for the state we keep for a scanner.
+ */
+%option extra-type="Mate_scanner_state_t *"
+
+/*
+ * We have to override the memory allocators so that we don't get
+ * "unused argument" warnings from the yyscanner argument (which
+ * we don't use, as we have a global memory allocator).
+ *
+ * We provide, as macros, our own versions of the routines generated by Flex,
+ * which just call malloc()/realloc()/free() (as the Flex versions do),
+ * discarding the extra argument.
+ */
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
+
+/*
+ * Prefix scanner routines with "Mate_" rather than "yy", so this scanner
+ * can coexist with other scanners.
+ */
+%option prefix="Mate_"
+
+%{
+
+ /* mate_parser.l
+ * lexical analyzer for MATE configuration files
+ *
+ * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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 "mate_grammar.h"
+
+#include <wsutil/file_util.h>
+
+/*
+ * Disable diagnostics in the code generated by Flex.
+ */
+DIAG_OFF_FLEX()
+
+ void MateParseTrace(FILE*,char*);
+
+#define MAX_INCLUDE_DEPTH 10
+typedef struct {
+ mate_config* mc;
+
+ mate_config_frame* current_frame;
+
+ void* pParser;
+
+ YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
+ int include_stack_ptr;
+} Mate_scanner_state_t;
+
+#define MATE_PARSE(token_type) MateParser(yyextra->pParser, (token_type), g_strdup(yytext), yyextra->mc);
+
+/*
+ * Flex (v 2.5.35) uses this symbol to "exclude" unistd.h
+ */
+#ifdef _WIN32
+#define YY_NO_UNISTD_H
+#endif
+
+static void free_config_frame(mate_config_frame *frame) {
+ g_free(frame->filename);
+ g_free(frame);
+}
+
+#define YY_USER_INIT BEGIN OUTSIDE;
+
+/*
+ * Sleazy hack to suppress compiler warnings in yy_fatal_error().
+ */
+#define YY_EXIT_FAILURE ((void)yyscanner, 2)
+
+/*
+ * Macros for the allocators, to discard the extra argument.
+ */
+#define Mate_alloc(size, yyscanner) (void *)malloc(size)
+#define Mate_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size))
+#define Mate_free(ptr, yyscanner) free((char *)ptr)
+
+%}
+
+pdu_kw Pdu
+gop_kw Gop
+gog_kw Gog
+transform_kw Transform
+match_kw Match
+always_kw Always
+strict_kw Strict
+every_kw Every
+loose_kw Loose
+replace_kw Replace
+insert_kw Insert
+gop_tree_kw GopTree
+member_kw Member
+on_kw On
+start_kw Start
+stop_kw Stop
+extra_kw Extra
+show_tree_kw ShowTree
+show_times_kw ShowTimes
+expiration_kw Expiration
+idle_timeout_kw IdleTimeout
+lifetime_kw Lifetime
+no_tree_kw NoTree
+pdu_tree_kw PduTree
+frame_tree_kw FrameTree
+basic_tree_kw BasicTree
+true_kw [Tt][Rr][Uu][Ee]
+false_kw [Ff][Aa][Ll][Ss][Ee]
+proto_kw Proto
+payload_kw Payload
+transport_kw Transport
+criteria_kw Criteria
+accept_kw Accept
+reject_kw Reject
+extract_kw Extract
+from_kw From
+drop_unassigned_kw DropUnassigned
+discard_pdu_data_kw DiscardPduData
+last_pdu_kw LastPdu
+done_kw Done
+filename_kw Filename
+debug_kw Debug
+level_kw Level
+default_kw Default
+
+
+open_parens "("
+close_parens ")"
+open_brace "{"
+close_brace "}"
+comma ","
+semicolon ";"
+slash "/"
+pipe "|"
+
+integer [0-9]+
+floating ([0-9]+\.[0-9]+)
+doted_ip [0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?
+colonized [0-9A-Fa-f:]*[:][0-9A-Fa-f:]*
+
+name [a-z][-\.a-zA-Z0-9_]*
+avp_operator [$^~=<>!]
+quote ["]
+not_quoted [^"]*
+
+include "#include"
+filename [-A-Za-z0-9_/.]+
+
+whitespace [[:blank:]\r]+
+newline \n
+
+comment "//"[^\n]*\n
+
+blk_cmnt_start "/*"
+cmnt_char .
+blk_cmnt_stop "*/"
+
+%START OUTSIDE QUOTED INCLUDING COMMENT
+%%
+
+{newline} yyextra->current_frame->linenum++;
+{whitespace} ;
+
+<OUTSIDE>{include} BEGIN INCLUDING;
+
+<INCLUDING>{filename} {
+ if ( yyextra->include_stack_ptr >= MAX_INCLUDE_DEPTH )
+ ws_error("dtd_preparse: include files nested too deeply");
+
+ yyextra->include_stack[yyextra->include_stack_ptr++] = YY_CURRENT_BUFFER;
+ yyin = ws_fopen( yytext, "r" );
+
+ if (!yyin) {
+ Mate__delete_buffer(YY_CURRENT_BUFFER, yyscanner);
+
+ /* coverity[negative_sink] */
+ Mate__switch_to_buffer(yyextra->include_stack[--yyextra->include_stack_ptr], yyscanner);
+
+ if (errno)
+ g_string_append_printf(yyextra->mc->config_error, "Mate parser: Could not open file: '%s': %s", yytext, g_strerror(errno) );
+
+ } else {
+
+ yyextra->current_frame = g_new(mate_config_frame, 1);
+ yyextra->current_frame->filename = g_strdup(yytext);
+ yyextra->current_frame->linenum = 1;
+
+ g_ptr_array_add(yyextra->mc->config_stack,yyextra->current_frame);
+
+ Mate__switch_to_buffer(Mate__create_buffer(yyin, YY_BUF_SIZE, yyscanner), yyscanner);
+ }
+
+ BEGIN OUTSIDE;
+}
+
+<<EOF>> {
+ /* coverity[check_after_sink] */
+ if ( --yyextra->include_stack_ptr < 0 ) {
+ yyterminate();
+ } else {
+ Mate__delete_buffer(YY_CURRENT_BUFFER, yyscanner);
+ Mate__switch_to_buffer(yyextra->include_stack[yyextra->include_stack_ptr], yyscanner);
+
+ free_config_frame(yyextra->current_frame);
+ yyextra->current_frame = (mate_config_frame *)g_ptr_array_remove_index(yyextra->mc->config_stack,yyextra->mc->config_stack->len-1);
+ }
+}
+
+<OUTSIDE>{comment} ;
+
+<OUTSIDE>{blk_cmnt_start} BEGIN COMMENT;
+<COMMENT>{cmnt_char} ;
+<COMMENT>{blk_cmnt_stop} BEGIN OUTSIDE;
+
+<OUTSIDE>{pdu_kw} MATE_PARSE(TOKEN_PDU_KW);
+<OUTSIDE>{gop_kw} MATE_PARSE(TOKEN_GOP_KW);
+<OUTSIDE>{gog_kw} MATE_PARSE(TOKEN_GOG_KW);
+<OUTSIDE>{transform_kw} MATE_PARSE(TOKEN_TRANSFORM_KW);
+<OUTSIDE>{match_kw} MATE_PARSE(TOKEN_MATCH_KW);
+<OUTSIDE>{strict_kw} MATE_PARSE(TOKEN_STRICT_KW);
+<OUTSIDE>{every_kw} MATE_PARSE(TOKEN_EVERY_KW);
+<OUTSIDE>{loose_kw} MATE_PARSE(TOKEN_LOOSE_KW);
+<OUTSIDE>{replace_kw} MATE_PARSE(TOKEN_REPLACE_KW);
+<OUTSIDE>{insert_kw} MATE_PARSE(TOKEN_INSERT_KW);
+<OUTSIDE>{gop_tree_kw} MATE_PARSE(TOKEN_GOP_TREE_KW);
+<OUTSIDE>{member_kw} MATE_PARSE(TOKEN_MEMBER_KW);
+<OUTSIDE>{on_kw} MATE_PARSE(TOKEN_ON_KW);
+<OUTSIDE>{start_kw} MATE_PARSE(TOKEN_START_KW);
+<OUTSIDE>{stop_kw} MATE_PARSE(TOKEN_STOP_KW);
+<OUTSIDE>{extra_kw} MATE_PARSE(TOKEN_EXTRA_KW);
+<OUTSIDE>{show_tree_kw} MATE_PARSE(TOKEN_SHOW_TREE_KW);
+<OUTSIDE>{show_times_kw} MATE_PARSE(TOKEN_SHOW_TIMES_KW);
+<OUTSIDE>{expiration_kw} MATE_PARSE(TOKEN_EXPIRATION_KW);
+<OUTSIDE>{idle_timeout_kw} MATE_PARSE(TOKEN_IDLE_TIMEOUT_KW);
+<OUTSIDE>{lifetime_kw} MATE_PARSE(TOKEN_LIFETIME_KW);
+<OUTSIDE>{no_tree_kw} MATE_PARSE(TOKEN_NO_TREE_KW);
+<OUTSIDE>{pdu_tree_kw} MATE_PARSE(TOKEN_PDU_TREE_KW);
+<OUTSIDE>{frame_tree_kw} MATE_PARSE(TOKEN_FRAME_TREE_KW);
+<OUTSIDE>{basic_tree_kw} MATE_PARSE(TOKEN_BASIC_TREE_KW);
+<OUTSIDE>{true_kw} MATE_PARSE(TOKEN_TRUE_KW);
+<OUTSIDE>{false_kw} MATE_PARSE(TOKEN_FALSE_KW);
+<OUTSIDE>{proto_kw} MATE_PARSE(TOKEN_PROTO_KW);
+<OUTSIDE>{payload_kw} MATE_PARSE(TOKEN_PAYLOAD_KW);
+<OUTSIDE>{transport_kw} MATE_PARSE(TOKEN_TRANSPORT_KW);
+<OUTSIDE>{criteria_kw} MATE_PARSE(TOKEN_CRITERIA_KW);
+<OUTSIDE>{accept_kw} MATE_PARSE(TOKEN_ACCEPT_KW);
+<OUTSIDE>{reject_kw} MATE_PARSE(TOKEN_REJECT_KW);
+<OUTSIDE>{extract_kw} MATE_PARSE(TOKEN_EXTRACT_KW);
+<OUTSIDE>{from_kw} MATE_PARSE(TOKEN_FROM_KW);
+<OUTSIDE>{drop_unassigned_kw} MATE_PARSE(TOKEN_DROP_UNASSIGNED_KW);
+<OUTSIDE>{discard_pdu_data_kw} MATE_PARSE(TOKEN_DISCARD_PDU_DATA_KW);
+<OUTSIDE>{last_pdu_kw} MATE_PARSE(TOKEN_LAST_PDU_KW);
+<OUTSIDE>{done_kw} MATE_PARSE(TOKEN_DONE_KW);
+<OUTSIDE>{filename_kw} MATE_PARSE(TOKEN_FILENAME_KW);
+<OUTSIDE>{debug_kw} MATE_PARSE(TOKEN_DEBUG_KW);
+<OUTSIDE>{level_kw} MATE_PARSE(TOKEN_LEVEL_KW);
+<OUTSIDE>{default_kw} MATE_PARSE(TOKEN_DEFAULT_KW);
+
+<OUTSIDE>{open_parens} MATE_PARSE(TOKEN_OPEN_PARENS);
+<OUTSIDE>{close_parens} MATE_PARSE(TOKEN_CLOSE_PARENS);
+<OUTSIDE>{open_brace} MATE_PARSE(TOKEN_OPEN_BRACE);
+<OUTSIDE>{close_brace} MATE_PARSE(TOKEN_CLOSE_BRACE);
+<OUTSIDE>{comma} MATE_PARSE(TOKEN_COMMA);
+<OUTSIDE>{semicolon} MATE_PARSE(TOKEN_SEMICOLON);
+<OUTSIDE>{slash} MATE_PARSE(TOKEN_SLASH);
+<OUTSIDE>{pipe} MATE_PARSE(TOKEN_PIPE);
+
+<OUTSIDE>{integer} MATE_PARSE(TOKEN_INTEGER);
+<OUTSIDE>{floating} MATE_PARSE(TOKEN_FLOATING);
+<OUTSIDE>{doted_ip} MATE_PARSE(TOKEN_DOTED_IP);
+<OUTSIDE>{colonized} MATE_PARSE(TOKEN_COLONIZED);
+<OUTSIDE>{name} MATE_PARSE(TOKEN_NAME);
+<OUTSIDE>{avp_operator} MATE_PARSE(TOKEN_AVP_OPERATOR);
+
+
+<OUTSIDE>{quote} BEGIN QUOTED;
+<QUOTED>{not_quoted} MATE_PARSE(TOKEN_QUOTED);
+<QUOTED>{quote} BEGIN OUTSIDE;
+
+%%
+
+/*
+ * Turn diagnostics back on, so we check the code that we've written.
+ */
+DIAG_ON_FLEX()
+
+static void ptr_array_free(gpointer data, gpointer user_data _U_)
+{
+ free_config_frame((mate_config_frame *)data);
+}
+
+extern gboolean mate_load_config(const gchar* filename, mate_config* mc) {
+ FILE *in;
+ yyscan_t scanner;
+ Mate_scanner_state_t state;
+ volatile gboolean status = TRUE;
+
+ in = ws_fopen(filename,"r");
+
+ if (!in) {
+ g_string_append_printf(mc->config_error,"Mate parser: Could not open file: '%s', error: %s", filename, g_strerror(errno) );
+ return FALSE;
+ }
+
+ if (Mate_lex_init(&scanner) != 0) {
+ g_string_append_printf(mc->config_error, "Mate parse: Could not initialize scanner: %s", g_strerror(errno));
+ fclose(in);
+ return FALSE;
+ }
+
+ Mate_set_in(in, scanner);
+
+ mc->config_stack = g_ptr_array_new();
+
+ state.mc = mc;
+
+ state.current_frame = g_new(mate_config_frame, 1);
+ state.current_frame->filename = g_strdup(filename);
+ state.current_frame->linenum = 1;
+
+ g_ptr_array_add(mc->config_stack,state.current_frame);
+
+ state.pParser = MateParserAlloc(g_malloc);
+
+ state.include_stack_ptr = 0;
+
+ /* Associate the state with the scanner */
+ Mate_set_extra(&state, scanner);
+
+ /* MateParserTrace(stdout,""); */
+
+ TRY {
+ Mate_lex(scanner);
+
+ /* Inform parser that end of input has reached. */
+ MateParser(state.pParser, 0, NULL, mc);
+
+ MateParserFree(state.pParser, g_free);
+ } CATCH(MateConfigError) {
+ status = FALSE;
+ } CATCH_ALL {
+ status = FALSE;
+ g_string_append_printf(mc->config_error,"An unexpected error occurred");
+ }
+ ENDTRY;
+
+ Mate_lex_destroy(scanner);
+ fclose(in);
+
+ g_ptr_array_foreach(mc->config_stack, ptr_array_free, NULL);
+ g_ptr_array_free(mc->config_stack, TRUE);
+
+ return status;
+}
diff --git a/plugins/epan/mate/mate_runtime.c b/plugins/epan/mate/mate_runtime.c
new file mode 100644
index 00000000..41d9c0de
--- /dev/null
+++ b/plugins/epan/mate/mate_runtime.c
@@ -0,0 +1,930 @@
+/* mate_runtime.c
+ * MATE -- Meta Analysis Tracing Engine
+ *
+ * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * 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 "mate.h"
+#include <wsutil/ws_assert.h>
+
+typedef struct _mate_range mate_range;
+
+struct _mate_range {
+ guint start;
+ guint end;
+};
+
+
+typedef struct _tmp_pdu_data {
+ GPtrArray* ranges;
+ proto_tree* tree;
+ mate_pdu* pdu;
+} tmp_pdu_data;
+
+
+typedef struct _gogkey {
+ gchar* key;
+ mate_cfg_gop* cfg;
+} gogkey;
+
+
+static mate_runtime_data* rd = NULL;
+
+static int zero = 5;
+
+static int* dbg = &zero;
+static int* dbg_pdu = &zero;
+static int* dbg_gop = &zero;
+static int* dbg_gog = &zero;
+static FILE* dbg_facility = NULL;
+
+static gboolean destroy_mate_pdus(gpointer k _U_, gpointer v, gpointer p _U_) {
+ mate_pdu* pdu = (mate_pdu*) v;
+ if (pdu->avpl) delete_avpl(pdu->avpl,TRUE);
+ g_slice_free(mate_max_size, (mate_max_size *)pdu);
+ return TRUE;
+}
+
+static gboolean destroy_mate_gops(gpointer k _U_, gpointer v, gpointer p _U_) {
+ mate_gop* gop = (mate_gop*) v;
+
+ if (gop->avpl) delete_avpl(gop->avpl,TRUE);
+
+ if (gop->gop_key) {
+ if (g_hash_table_lookup(gop->cfg->gop_index,gop->gop_key) == gop) {
+ g_hash_table_remove(gop->cfg->gop_index,gop->gop_key);
+ }
+
+ g_free(gop->gop_key);
+ }
+
+ g_slice_free(mate_max_size,(mate_max_size*)gop);
+
+ return TRUE;
+}
+
+
+static void gog_remove_keys (mate_gog* gog);
+
+static gboolean destroy_mate_gogs(gpointer k _U_, gpointer v, gpointer p _U_) {
+ mate_gog* gog = (mate_gog*) v;
+
+ if (gog->avpl) delete_avpl(gog->avpl,TRUE);
+
+ if (gog->gog_keys) {
+ gog_remove_keys(gog);
+ g_ptr_array_free(gog->gog_keys, TRUE);
+ }
+
+ g_slice_free(mate_max_size,(mate_max_size*)gog);
+
+ return TRUE;
+}
+
+static gboolean return_true(gpointer k _U_, gpointer v _U_, gpointer p _U_) {
+ return TRUE;
+}
+
+static void destroy_pdus_in_cfg(gpointer k _U_, gpointer v, gpointer p _U_) {
+ mate_cfg_pdu* c = (mate_cfg_pdu *)v;
+ g_hash_table_foreach_remove(c->items,destroy_mate_pdus,NULL);
+ c->last_id = 0;
+}
+
+
+static void destroy_gops_in_cfg(gpointer k _U_, gpointer v, gpointer p _U_) {
+ mate_cfg_gop* c = (mate_cfg_gop *)v;
+
+ g_hash_table_foreach_remove(c->gop_index,return_true,NULL);
+ g_hash_table_destroy(c->gop_index);
+ c->gop_index = g_hash_table_new(g_str_hash,g_str_equal);
+
+ g_hash_table_foreach_remove(c->gog_index,return_true,NULL);
+ g_hash_table_destroy(c->gog_index);
+ c->gog_index = g_hash_table_new(g_str_hash,g_str_equal);
+
+ g_hash_table_foreach_remove(c->items,destroy_mate_gops,NULL);
+ c->last_id = 0;
+}
+
+static void destroy_gogs_in_cfg(gpointer k _U_, gpointer v, gpointer p _U_) {
+ mate_cfg_gog* c = (mate_cfg_gog *)v;
+ g_hash_table_foreach_remove(c->items,destroy_mate_gogs,NULL);
+ c->last_id = 0;
+}
+
+void initialize_mate_runtime(mate_config* mc) {
+
+ dbg_print (dbg,5,dbg_facility,"initialize_mate: entering");
+
+ if (mc) {
+ if (rd == NULL ) {
+ rd = g_new(mate_runtime_data, 1);
+ } else {
+ g_hash_table_foreach(mc->pducfgs,destroy_pdus_in_cfg,NULL);
+ g_hash_table_foreach(mc->gopcfgs,destroy_gops_in_cfg,NULL);
+ g_hash_table_foreach(mc->gogcfgs,destroy_gogs_in_cfg,NULL);
+
+ g_hash_table_destroy(rd->frames);
+ }
+
+ rd->current_items = 0;
+ rd->now = -1.0f;
+ rd->highest_analyzed_frame = 0;
+ rd->frames = g_hash_table_new(g_direct_hash,g_direct_equal);
+
+
+ /*mc->dbg_gop_lvl = 5;
+ mc->dbg_gog_lvl = 5;
+ */
+ dbg_pdu = &(mc->dbg_pdu_lvl);
+ dbg_gop = &(mc->dbg_gop_lvl);
+ dbg_gog = &(mc->dbg_gog_lvl);
+ dbg = &(mc->dbg_lvl);
+ dbg_facility = mc->dbg_facility;
+
+ dbg_print(dbg, 1, dbg_facility, "starting mate");
+
+ } else {
+ rd = NULL;
+ }
+}
+
+
+static mate_gop* new_gop(mate_cfg_gop* cfg, mate_pdu* pdu, gchar* key) {
+ mate_gop* gop = (mate_gop*)g_slice_new(mate_max_size);
+
+ gop->id = ++(cfg->last_id);
+ gop->cfg = cfg;
+
+ dbg_print(dbg_gop, 1, dbg_facility, "new_gop: %s: ``%s:%d''", key, gop->cfg->name, gop->id);
+
+ gop->gop_key = key;
+ gop->avpl = new_avpl(cfg->name);
+ gop->last_n = 0;
+
+ gop->gog = NULL;
+ gop->next = NULL;
+
+ gop->expiration = cfg->expiration > 0.0 ? cfg->expiration + rd->now : (float) -1.0 ;
+ gop->idle_expiration = cfg->idle_timeout > 0.0 ? cfg->idle_timeout + rd->now : (float) -1.0 ;
+ gop->time_to_die = cfg->lifetime > 0.0 ? cfg->lifetime + rd->now : (float) -1.0 ;
+ gop->time_to_timeout = 0.0f;
+
+ gop->last_time = gop->start_time = rd->now;
+ gop->release_time = 0.0f;
+
+ gop->num_of_pdus = 0;
+ gop->num_of_after_release_pdus = 0;
+
+ gop->pdus = pdu;
+ gop->last_pdu = pdu;
+
+ gop->released = FALSE;
+
+ pdu->gop = gop;
+ pdu->next = NULL;
+ pdu->is_start = TRUE;
+ pdu->time_in_gop = 0.0f;
+
+ g_hash_table_insert(cfg->gop_index,gop->gop_key,gop);
+ return gop;
+}
+
+static void adopt_gop(mate_gog* gog, mate_gop* gop) {
+ dbg_print (dbg_gog,5,dbg_facility,"adopt_gop: gog=%p gop=%p",(void*)gog,(void*)gop);
+
+ gop->gog = gog;
+ gop->next = NULL;
+
+ if (gop->cfg->start) {
+ gog->num_of_counting_gops++;
+ }
+
+ gog->num_of_gops++;
+
+ if (gog->last_gop) {
+ gog->last_gop->next = gop;
+ }
+
+ gog->last_gop = gop;
+
+ if (! gog->gops ) {
+ gog->gops = gop;
+ }
+
+}
+
+static mate_gog* new_gog(mate_cfg_gog* cfg, mate_gop* gop) {
+ mate_gog* gog = (mate_gog*)g_slice_new(mate_max_size);
+ gog->id = ++(cfg->last_id);
+ gog->cfg = cfg;
+
+ dbg_print (dbg_gog,1,dbg_facility,"new_gog: %s:%u for %s:%u",gog->cfg->name,gog->id,gop->cfg->name,gop->id);
+
+ gog->avpl = new_avpl(cfg->name);
+ gog->last_n = 0;
+
+ gog->expiration = 0.0f;
+ gog->idle_expiration = 0.0f;
+
+ gog->start_time = rd->now;
+ gog->release_time = 0.0f;
+ gog->last_time = 0.0f;
+
+ gog->gops = NULL;
+ gog->last_gop = NULL;
+
+ gog->num_of_gops = 0;
+ gog->num_of_counting_gops = 0;
+ gog->num_of_released_gops = 0;
+
+ gog->gog_keys = g_ptr_array_new();
+
+ adopt_gop(gog,gop);
+
+ return gog;
+}
+
+static void apply_transforms(GPtrArray* transforms, AVPL* avpl) {
+ AVPL_Transf* transform = NULL;
+ guint i;
+
+ for (i = 0; i < transforms->len; i++) {
+ transform = (AVPL_Transf *)g_ptr_array_index(transforms,i);
+ avpl_transform(avpl, transform);
+ }
+}
+
+
+/* applies the extras for which type to what avpl */
+static void apply_extras(AVPL* from, AVPL* to, AVPL* extras) {
+ AVPL* our_extras = new_avpl_loose_match("",from, extras, FALSE) ;
+
+ if (our_extras) {
+ merge_avpl(to,our_extras,TRUE);
+ delete_avpl(our_extras,FALSE);
+ }
+}
+
+static void gog_remove_keys (mate_gog* gog) {
+ gogkey* gog_key;
+
+ while (gog->gog_keys->len) {
+ gog_key = (gogkey *)g_ptr_array_remove_index_fast(gog->gog_keys,0);
+
+ if (g_hash_table_lookup(gog_key->cfg->gog_index,gog_key->key) == gog) {
+ g_hash_table_remove(gog_key->cfg->gog_index,gog_key->key);
+ }
+
+ g_free(gog_key->key);
+ g_free(gog_key);
+ }
+
+}
+
+static void reanalyze_gop(mate_config* mc, mate_gop* gop) {
+ LoAL* gog_keys = NULL;
+ AVPL* curr_gogkey = NULL;
+ mate_cfg_gop* gop_cfg = NULL;
+ void* cookie = NULL;
+ AVPL* gogkey_match = NULL;
+ mate_gog* gog = gop->gog;
+ gogkey* gog_key;
+
+ if ( ! gog ) return;
+
+ gog->last_time = rd->now;
+
+ dbg_print (dbg_gog,1,dbg_facility,"reanalyze_gop: %s:%d",gop->cfg->name,gop->id);
+
+ apply_extras(gop->avpl,gog->avpl,gog->cfg->extra);
+
+ /* XXX: Instead of using the length of the avpl to check if an avpl has changed,
+ which is not accurate at all, we should have apply_extras,
+ apply_transformations and other functions that can modify the avpl
+ to flag the avpl if it has changed, then we'll check for the flag
+ and clear it after analysis */
+
+ if (gog->last_n != gog->avpl->len) {
+
+ dbg_print (dbg_gog,2,dbg_facility,"reanalyze_gop: gog has new attributes let's look for new keys");
+
+ gog_keys = gog->cfg->keys;
+
+ while (( curr_gogkey = get_next_avpl(gog_keys,&cookie) )) {
+ gop_cfg = (mate_cfg_gop *)g_hash_table_lookup(mc->gopcfgs,curr_gogkey->name);
+
+ if (( gogkey_match = new_avpl_pairs_match(gop_cfg->name, gog->avpl, curr_gogkey, TRUE, FALSE) )) {
+
+ gog_key = g_new(gogkey, 1);
+
+ gog_key->key = avpl_to_str(gogkey_match);
+ delete_avpl(gogkey_match,FALSE);
+
+ gog_key->cfg = gop_cfg;
+
+ if (g_hash_table_lookup(gop_cfg->gog_index,gog_key->key)) {
+ g_free(gog_key->key);
+ g_free(gog_key);
+ gog_key = NULL;
+ }
+
+ if (! gog_key ) {
+ /* XXX: since these gogs actually share key info
+ we should try to merge (non released) gogs
+ that happen to have equal keys */
+ } else {
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: new key for gog=%s:%d : %s",gog->cfg->name,gog->id,gog_key->key);
+ g_ptr_array_add(gog->gog_keys,gog_key);
+ g_hash_table_insert(gog_key->cfg->gog_index,gog_key->key,gog);
+ }
+
+ }
+ }
+
+ gog->last_n = gog->avpl->len;
+ }
+
+ if (gog->num_of_released_gops == gog->num_of_counting_gops) {
+ gog->released = TRUE;
+ gog->expiration = gog->cfg->expiration + rd->now;
+ } else {
+ gog->released = FALSE;
+ }
+}
+
+static void analyze_gop(mate_config* mc, mate_gop* gop) {
+ mate_cfg_gog* cfg = NULL;
+ LoAL* gog_keys = NULL;
+ AVPL* curr_gogkey = NULL;
+ void* cookie = NULL;
+ AVPL* gogkey_match = NULL;
+ mate_gog* gog = NULL;
+ gchar* key = NULL;
+
+ if ( ! gop->gog ) {
+ /* no gog, let's either find one or create it if due */
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: no gog");
+
+ gog_keys = (LoAL *)g_hash_table_lookup(mc->gogs_by_gopname,gop->cfg->name);
+
+ if ( ! gog_keys ) {
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: no gog_keys for this gop");
+ return;
+ }
+
+ /* We have gog_keys! look for matching gogkeys */
+
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: got gog_keys: %s",gog_keys->name) ;
+
+ while (( curr_gogkey = get_next_avpl(gog_keys,&cookie) )) {
+ if (( gogkey_match = new_avpl_pairs_match(gop->cfg->name, gop->avpl, curr_gogkey, TRUE, TRUE) )) {
+
+ key = avpl_to_str(gogkey_match);
+
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: got gogkey_match: %s",key);
+
+ if (( gog = (mate_gog *)g_hash_table_lookup(gop->cfg->gog_index,key) )) {
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: got already a matching gog");
+
+ if (gog->num_of_counting_gops == gog->num_of_released_gops && gog->expiration < rd->now) {
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: this is a new gog, not the old one, let's create it");
+
+ gog_remove_keys(gog);
+
+ new_gog(gog->cfg,gop);
+
+ break;
+ } else {
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: this is our gog");
+
+ if (! gop->gog ) adopt_gop(gog,gop);
+
+ break;
+ }
+ } else {
+ dbg_print (dbg_gog,1,dbg_facility,"analyze_gop: no such gog in hash, let's create a new %s",curr_gogkey->name);
+
+ cfg = (mate_cfg_gog *)g_hash_table_lookup(mc->gogcfgs,curr_gogkey->name);
+
+ if (cfg) {
+ gog = new_gog(cfg,gop);
+ gog->num_of_gops = 1;
+
+ if (gop->cfg->start) {
+ gog->num_of_counting_gops = 1;
+ }
+
+ } else {
+ dbg_print (dbg_gog,0,dbg_facility,"analyze_gop: no such gog_cfg: %s",curr_gogkey->name);
+ }
+
+ break;
+ }
+
+ /** Can't get here because of "breaks" above */
+ ws_assert_not_reached();
+ }
+ } /* while */
+
+ g_free(key);
+ key = NULL;
+
+ if (gogkey_match) delete_avpl(gogkey_match,TRUE);
+
+ reanalyze_gop(mc, gop);
+ }
+}
+
+
+
+static void analyze_pdu(mate_config* mc, mate_pdu* pdu) {
+ /* TODO:
+ return a g_boolean to tell we've destroyed the pdu when the pdu is unnassigned
+ destroy the unassigned pdu
+ */
+ mate_cfg_gop* cfg = NULL;
+ mate_gop* gop = NULL;
+ gchar* gop_key;
+ gchar* orig_gop_key = NULL;
+ AVPL* candidate_start = NULL;
+ AVPL* candidate_stop = NULL;
+ AVPL* is_start = NULL;
+ AVPL* is_stop = NULL;
+ AVPL* gopkey_match = NULL;
+ LoAL* gog_keys = NULL;
+ AVPL* curr_gogkey = NULL;
+ void* cookie = NULL;
+ AVPL* gogkey_match = NULL;
+ gchar* gogkey_str = NULL;
+
+ dbg_print (dbg_gop,1,dbg_facility,"analyze_pdu: %s",pdu->cfg->name);
+
+ if (! (cfg = (mate_cfg_gop *)g_hash_table_lookup(mc->gops_by_pduname,pdu->cfg->name)) )
+ return;
+
+ if ((gopkey_match = new_avpl_pairs_match("gop_key_match", pdu->avpl, cfg->key, TRUE, TRUE))) {
+ gop_key = avpl_to_str(gopkey_match);
+
+ g_hash_table_lookup_extended(cfg->gop_index,(gconstpointer)gop_key,(gpointer *)&orig_gop_key,(gpointer *)&gop);
+
+ if ( gop ) {
+ g_free(gop_key);
+
+ /* is the gop dead ? */
+ if ( ! gop->released &&
+ ( ( gop->cfg->lifetime > 0.0 && gop->time_to_die >= rd->now) ||
+ ( gop->cfg->idle_timeout > 0.0 && gop->time_to_timeout >= rd->now) ) ) {
+ dbg_print (dbg_gop,4,dbg_facility,"analyze_pdu: expiring released gop");
+ gop->released = TRUE;
+
+ if (gop->gog && gop->cfg->start) gop->gog->num_of_released_gops++;
+ }
+
+ /* TODO: is the gop expired? */
+
+ gop_key = orig_gop_key;
+
+ dbg_print (dbg_gop,2,dbg_facility,"analyze_pdu: got gop: %s",gop_key);
+
+ if (( candidate_start = cfg->start )) {
+
+ dbg_print (dbg_gop,2,dbg_facility,"analyze_pdu: got candidate start");
+
+ if (( is_start = new_avpl_pairs_match("", pdu->avpl, candidate_start, TRUE, FALSE) )) {
+ delete_avpl(is_start,FALSE);
+ if ( gop->released ) {
+ dbg_print (dbg_gop,3,dbg_facility,"analyze_pdu: start on released gop, let's create a new gop");
+
+ g_hash_table_remove(cfg->gop_index,gop_key);
+ gop->gop_key = NULL;
+ gop = new_gop(cfg,pdu,gop_key);
+ g_hash_table_insert(cfg->gop_index,gop_key,gop);
+ } else {
+ dbg_print (dbg_gop,1,dbg_facility,"analyze_pdu: duplicate start on gop");
+ }
+ }
+ }
+
+ pdu->gop = gop;
+
+ if (gop->last_pdu) gop->last_pdu->next = pdu;
+ gop->last_pdu = pdu;
+ pdu->next = NULL;
+ pdu->time_in_gop = rd->now - gop->start_time;
+
+ if (gop->released) pdu->after_release = TRUE;
+
+ } else {
+
+ dbg_print (dbg_gop,1,dbg_facility,"analyze_pdu: no gop already");
+
+ if ( ! cfg->start ) {
+ /* there is no GopStart, we'll check for matching GogKeys
+ if we have one we'll create the Gop */
+
+ apply_extras(pdu->avpl,gopkey_match,cfg->extra);
+
+ gog_keys = (LoAL *)g_hash_table_lookup(mc->gogs_by_gopname,cfg->name);
+
+ if (gog_keys) {
+
+ while (( curr_gogkey = get_next_avpl(gog_keys,&cookie) )) {
+ if (( gogkey_match = new_avpl_pairs_match(cfg->name, gopkey_match, curr_gogkey, TRUE, FALSE) )) {
+ gogkey_str = avpl_to_str(gogkey_match);
+
+ if (g_hash_table_lookup(cfg->gog_index,gogkey_str)) {
+ gop = new_gop(cfg,pdu,gop_key);
+ g_hash_table_insert(cfg->gop_index,gop_key,gop);
+ delete_avpl(gogkey_match,FALSE);
+ g_free(gogkey_str);
+ break;
+ } else {
+ delete_avpl(gogkey_match,FALSE);
+ g_free(gogkey_str);
+ }
+ }
+ }
+
+ if ( ! gop ) {
+ g_free(gop_key);
+ delete_avpl(gopkey_match,TRUE);
+ return;
+ }
+
+ } else {
+ g_free(gop_key);
+ delete_avpl(gopkey_match,TRUE);
+ return;
+ }
+
+ } else {
+ candidate_start = cfg->start;
+
+ if (( is_start = new_avpl_pairs_match("", pdu->avpl, candidate_start, TRUE, FALSE) )) {
+ delete_avpl(is_start,FALSE);
+ gop = new_gop(cfg,pdu,gop_key);
+ } else {
+ g_free(gop_key);
+ return;
+ }
+
+ pdu->gop = gop;
+ }
+ }
+
+ if (gop->last_pdu) gop->last_pdu->next = pdu;
+ gop->last_pdu = pdu;
+ pdu->next = NULL;
+
+ pdu->time_in_gop = rd->now - gop->start_time;
+
+ gop->num_of_pdus++;
+ gop->time_to_timeout = cfg->idle_timeout > 0.0 ? cfg->idle_timeout + rd->now : (float) -1.0 ;
+
+ dbg_print (dbg_gop,4,dbg_facility,"analyze_pdu: merge with key");
+
+ merge_avpl(gop->avpl,gopkey_match,TRUE);
+ delete_avpl(gopkey_match,TRUE);
+
+ dbg_print (dbg_gop,4,dbg_facility,"analyze_pdu: apply extras");
+
+ apply_extras(pdu->avpl,gop->avpl,gop->cfg->extra);
+
+ gop->last_time = pdu->rel_time;
+
+ if ( ! gop->released) {
+ candidate_stop = cfg->stop;
+
+ if (candidate_stop) {
+ is_stop = new_avpl_pairs_match("", pdu->avpl, candidate_stop, TRUE, FALSE);
+ } else {
+ is_stop = new_avpl("");
+ }
+
+ if(is_stop) {
+ dbg_print (dbg_gop,1,dbg_facility,"analyze_pdu: is a `stop");
+ delete_avpl(is_stop,FALSE);
+
+ if (! gop->released) {
+ gop->released = TRUE;
+ gop->release_time = pdu->rel_time;
+ if (gop->gog && gop->cfg->start) gop->gog->num_of_released_gops++;
+ }
+
+ pdu->is_stop = TRUE;
+
+ }
+ }
+
+ if (gop->last_n != gop->avpl->len) apply_transforms(gop->cfg->transforms,gop->avpl);
+
+ gop->last_n = gop->avpl->len;
+
+ if (gop->gog) {
+ reanalyze_gop(mc, gop);
+ } else {
+ analyze_gop(mc, gop);
+ }
+
+ } else {
+ dbg_print (dbg_gop,4,dbg_facility,"analyze_pdu: no match for this pdu");
+
+ pdu->gop = NULL;
+ }
+}
+
+static void get_pdu_fields(gpointer k, gpointer v, gpointer p) {
+ int hfid = *((int*) k);
+ gchar* name = (gchar*) v;
+ tmp_pdu_data* data = (tmp_pdu_data*) p;
+ GPtrArray* fis;
+ field_info* fi;
+ guint i,j;
+ mate_range* curr_range;
+ guint start;
+ guint end;
+ AVP* avp;
+ gchar* s;
+
+
+ fis = proto_get_finfo_ptr_array(data->tree, hfid);
+
+ if (fis) {
+ for (i = 0; i < fis->len; i++) {
+ fi = (field_info*) g_ptr_array_index(fis,i);
+
+
+ start = fi->start;
+ end = fi->start + fi->length;
+
+ dbg_print(dbg_pdu,5,dbg_facility,"get_pdu_fields: found field %s, %i-%i, length %i", fi->hfinfo->abbrev, start, end, fi->length);
+
+ for (j = 0; j < data->ranges->len; j++) {
+
+ curr_range = (mate_range*) g_ptr_array_index(data->ranges,j);
+
+ if (curr_range->end >= end && curr_range->start <= start) {
+ avp = new_avp_from_finfo(name, fi);
+
+ if (*dbg_pdu > 4) {
+ s = avp_to_str(avp);
+ dbg_print(dbg_pdu,0,dbg_facility,"get_pdu_fields: got %s",s);
+ g_free(s);
+ }
+
+ if (! insert_avp(data->pdu->avpl,avp) ) {
+ delete_avp(avp);
+ }
+
+ }
+ }
+ }
+ }
+}
+
+static void ptr_array_free(gpointer data, gpointer user_data _U_)
+{
+ g_free(data);
+}
+
+static mate_pdu* new_pdu(mate_cfg_pdu* cfg, guint32 framenum, field_info* proto, proto_tree* tree) {
+ mate_pdu* pdu = (mate_pdu*)g_slice_new(mate_max_size);
+ field_info* cfi;
+ GPtrArray* ptrs;
+ mate_range* range;
+ mate_range* proto_range;
+ tmp_pdu_data data;
+ guint i,j;
+ gint min_dist;
+ field_info* range_fi;
+ gint32 last_start;
+ gint32 first_end;
+ gint32 curr_end;
+ int hfid;
+
+ dbg_print (dbg_pdu,1,dbg_facility,"new_pdu: type=%s framenum=%i",cfg->name,framenum);
+
+ pdu->id = ++(cfg->last_id);
+ pdu->cfg = cfg;
+
+ pdu->avpl = new_avpl(cfg->name);
+
+ pdu->frame = framenum;
+ pdu->next_in_frame = NULL;
+ pdu->rel_time = rd->now;
+
+ pdu->gop = NULL;
+ pdu->next = NULL;
+ pdu->time_in_gop = -1.0f;
+
+ pdu->first = FALSE;
+ pdu->is_start = FALSE;
+ pdu->is_stop = FALSE;
+ pdu->after_release = FALSE;
+
+ data.ranges = g_ptr_array_new();
+ data.pdu = pdu;
+ data.tree = tree;
+
+ /* first we create the proto range */
+ proto_range = g_new(mate_range, 1);
+ proto_range->start = proto->start;
+ proto_range->end = proto->start + proto->length;
+ g_ptr_array_add(data.ranges,proto_range);
+
+ dbg_print(dbg_pdu,3,dbg_facility,"new_pdu: proto range %u-%u",proto_range->start,proto_range->end);
+
+ last_start = proto_range->start;
+
+ /* we move forward in the tranport */
+ for (i = cfg->transport_ranges->len; i--; ) {
+ hfid = *((int*)g_ptr_array_index(cfg->transport_ranges,i));
+ ptrs = proto_get_finfo_ptr_array(tree, hfid);
+ min_dist = 99999;
+ range_fi = NULL;
+
+ if (ptrs) {
+ for (j=0; j < ptrs->len; j++) {
+ cfi = (field_info*) g_ptr_array_index(ptrs,j);
+ if (cfi->start < last_start && min_dist >= (last_start - cfi->start) ) {
+ range_fi = cfi;
+ min_dist = last_start - cfi->start;
+ }
+ }
+
+ if ( range_fi ) {
+ range = (mate_range *)g_malloc(sizeof(*range));
+ range->start = range_fi->start;
+ range->end = range_fi->start + range_fi->length;
+ g_ptr_array_add(data.ranges,range);
+
+ last_start = range_fi->start;
+
+ dbg_print(dbg_pdu,3,dbg_facility,"new_pdu: transport(%i) range %i-%i",hfid,range->start,range->end);
+ } else {
+ /* we missed a range */
+ dbg_print(dbg_pdu,6,dbg_facility,"new_pdu: transport(%i) missed",hfid);
+ }
+
+ }
+ }
+
+ if (cfg->payload_ranges) {
+
+ first_end = proto_range->end;
+
+ for (i = 0 ; i < cfg->payload_ranges->len; i++) {
+ hfid = *((int*)g_ptr_array_index(cfg->payload_ranges,i));
+ ptrs = proto_get_finfo_ptr_array(tree, hfid);
+ min_dist = 99999;
+ range_fi = NULL;
+
+ if (ptrs) {
+ for (j=0; j < ptrs->len; j++) {
+ cfi = (field_info*) g_ptr_array_index(ptrs,j);
+ curr_end = cfi->start + cfi->length;
+ if (curr_end > first_end && min_dist >= (curr_end - first_end) ) {
+ range_fi = cfi;
+ min_dist = curr_end - first_end;
+ }
+ }
+
+ if ( range_fi ) {
+ range = (mate_range *)g_malloc(sizeof(*range));
+ range->start = range_fi->start;
+ range->end = range_fi->start + range_fi->length;
+ g_ptr_array_add(data.ranges,range);
+
+ dbg_print(dbg_pdu,3,dbg_facility,"new_pdu: payload(%i) range %i-%i",hfid,range->start,range->end);
+ } else {
+ /* we missed a range */
+ dbg_print(dbg_pdu,5,dbg_facility,"new_pdu: payload(%i) missed",hfid);
+ }
+
+ }
+ }
+ }
+
+ g_hash_table_foreach(cfg->hfids_attr,get_pdu_fields,&data);
+
+ apply_transforms(pdu->cfg->transforms,pdu->avpl);
+
+ g_ptr_array_foreach(data.ranges, ptr_array_free, NULL);
+ g_ptr_array_free(data.ranges,TRUE);
+
+ return pdu;
+}
+
+
+extern void mate_analyze_frame(mate_config *mc, packet_info *pinfo, proto_tree* tree) {
+ mate_cfg_pdu* cfg;
+ GPtrArray* protos;
+ field_info* proto;
+ guint i,j;
+ AVPL* criterium_match;
+
+ mate_pdu* pdu = NULL;
+ mate_pdu* last = NULL;
+
+ rd->now = (float) nstime_to_sec(&pinfo->rel_ts);
+
+ if ( proto_tracking_interesting_fields(tree)
+ && rd->highest_analyzed_frame < pinfo->num ) {
+ for ( i = 0; i < mc->pducfglist->len; i++ ) {
+
+ cfg = (mate_cfg_pdu *)g_ptr_array_index(mc->pducfglist,i);
+
+ dbg_print (dbg_pdu,4,dbg_facility,"mate_analyze_frame: trying to extract: %s",cfg->name);
+ protos = proto_get_finfo_ptr_array(tree, cfg->hfid_proto);
+
+ if (protos) {
+ pdu = NULL;
+
+ for (j = 0; j < protos->len; j++) {
+
+ dbg_print (dbg_pdu,3,dbg_facility,"mate_analyze_frame: found matching proto, extracting: %s",cfg->name);
+
+ proto = (field_info*) g_ptr_array_index(protos,j);
+ pdu = new_pdu(cfg, pinfo->num, proto, tree);
+
+ if (cfg->criterium) {
+ criterium_match = new_avpl_from_match(cfg->criterium_match_mode,"",pdu->avpl,cfg->criterium,FALSE);
+
+ if (criterium_match) {
+ delete_avpl(criterium_match,FALSE);
+ }
+
+ if ( (criterium_match && cfg->criterium_accept_mode == REJECT_MODE )
+ || ( ! criterium_match && cfg->criterium_accept_mode == ACCEPT_MODE )) {
+
+ delete_avpl(pdu->avpl,TRUE);
+ g_slice_free(mate_max_size,(mate_max_size*)pdu);
+ pdu = NULL;
+
+ continue;
+ }
+ }
+
+ analyze_pdu(mc, pdu);
+
+ if ( ! pdu->gop && cfg->drop_unassigned) {
+ delete_avpl(pdu->avpl,TRUE);
+ g_slice_free(mate_max_size,(mate_max_size*)pdu);
+ pdu = NULL;
+ continue;
+ }
+
+ if ( cfg->discard ) {
+ delete_avpl(pdu->avpl,TRUE);
+ pdu->avpl = NULL;
+ }
+
+ if (!last) {
+ g_hash_table_insert(rd->frames,GINT_TO_POINTER(pinfo->num),pdu);
+ last = pdu;
+ } else {
+ last->next_in_frame = pdu;
+ last = pdu;
+ }
+
+ }
+
+ if ( pdu && cfg->last_extracted ) break;
+ }
+ }
+
+ rd->highest_analyzed_frame = pinfo->num;
+ }
+}
+
+extern mate_pdu* mate_get_pdus(guint32 framenum) {
+
+ if (rd) {
+ return (mate_pdu*) g_hash_table_lookup(rd->frames,GUINT_TO_POINTER(framenum));
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * 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:
+ */
diff --git a/plugins/epan/mate/mate_setup.c b/plugins/epan/mate/mate_setup.c
new file mode 100644
index 00000000..eef97643
--- /dev/null
+++ b/plugins/epan/mate/mate_setup.c
@@ -0,0 +1,667 @@
+/* mate_setup.c
+ * MATE -- Meta Analysis Tracing Engine
+ *
+ * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * 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 "mate.h"
+
+/* appends the formatted string to the current error log */
+static void report_error(mate_config* mc, const gchar* fmt, ...) {
+ static gchar error_buffer[DEBUG_BUFFER_SIZE];
+
+ va_list list;
+
+ va_start( list, fmt );
+ vsnprintf(error_buffer,DEBUG_BUFFER_SIZE,fmt,list);
+ va_end( list );
+
+ g_string_append(mc->config_error,error_buffer);
+ g_string_append_c(mc->config_error,'\n');
+
+}
+
+/* creates a blank pdu config
+ is going to be called only by the grammar
+ which will set all those elements that aren't set here */
+extern mate_cfg_pdu* new_pducfg(mate_config* mc, gchar* name) {
+ mate_cfg_pdu* cfg = g_new(mate_cfg_pdu, 1);
+
+ cfg->name = g_strdup(name);
+ cfg->last_id = 0;
+
+ cfg->items = g_hash_table_new(g_direct_hash,g_direct_equal);
+ cfg->transforms = NULL;
+
+ cfg->hfid = -1;
+
+ cfg->hfid_pdu_rel_time = -1;
+ cfg->hfid_pdu_time_in_gop = -1;
+
+ cfg->my_hfids = g_hash_table_new(g_str_hash,g_str_equal);
+
+ cfg->ett = -1;
+ cfg->ett_attr = -1;
+
+ cfg->criterium = NULL;
+ cfg->criterium_match_mode = AVPL_NO_MATCH;
+ cfg->criterium_accept_mode = ACCEPT_MODE;
+
+ g_ptr_array_add(mc->pducfglist,(gpointer) cfg);
+ g_hash_table_insert(mc->pducfgs,(gpointer) cfg->name,(gpointer) cfg);
+
+ cfg->hfids_attr = g_hash_table_new(g_int_hash,g_int_equal);
+
+ return cfg;
+}
+
+extern mate_cfg_gop* new_gopcfg(mate_config* mc, gchar* name) {
+ mate_cfg_gop* cfg = g_new(mate_cfg_gop, 1);
+
+ cfg->name = g_strdup(name);
+ cfg->last_id = 0;
+
+ cfg->items = g_hash_table_new(g_direct_hash,g_direct_equal);
+ cfg->transforms = NULL;
+
+ cfg->extra = new_avpl("extra");
+
+ cfg->hfid = -1;
+
+ cfg->ett = -1;
+ cfg->ett_attr = -1;
+ cfg->ett_times = -1;
+ cfg->ett_children = -1;
+
+ cfg->hfid_start_time = -1;
+ cfg->hfid_stop_time = -1;
+ cfg->hfid_last_time = -1;
+
+ cfg->hfid_gop_pdu = -1;
+ cfg->hfid_gop_num_pdus = -1;
+
+ cfg->my_hfids = g_hash_table_new(g_str_hash,g_str_equal);
+
+ cfg->gop_index = g_hash_table_new(g_str_hash,g_str_equal);
+ cfg->gog_index = g_hash_table_new(g_str_hash,g_str_equal);
+
+ g_hash_table_insert(mc->gopcfgs,(gpointer) cfg->name, (gpointer) cfg);
+
+ return cfg;
+}
+
+extern mate_cfg_gog* new_gogcfg(mate_config* mc, gchar* name) {
+ mate_cfg_gog* cfg = g_new(mate_cfg_gog, 1);
+
+ cfg->name = g_strdup(name);
+ cfg->last_id = 0;
+
+ cfg->items = g_hash_table_new(g_direct_hash,g_direct_equal);
+ cfg->transforms = NULL;
+
+ cfg->extra = new_avpl("extra");
+
+ cfg->my_hfids = g_hash_table_new(g_str_hash,g_str_equal);
+ cfg->hfid = -1;
+
+ cfg->ett = -1;
+ cfg->ett_attr = -1;
+ cfg->ett_times = -1;
+ cfg->ett_children = -1;
+ cfg->ett_gog_gop = -1;
+
+ cfg->hfid_gog_num_of_gops = -1;
+ cfg->hfid_gog_gop = -1;
+ cfg->hfid_gog_gopstart = -1;
+ cfg->hfid_gog_gopstop = -1;
+
+ cfg->hfid_start_time = -1;
+ cfg->hfid_stop_time = -1;
+ cfg->hfid_last_time = -1;
+
+ g_hash_table_insert(mc->gogcfgs,(gpointer) cfg->name, (gpointer) cfg);
+
+ return cfg;
+}
+
+extern gboolean add_hfid(mate_config* mc, header_field_info* hfi, gchar* how, GHashTable* where) {
+ header_field_info* first_hfi = NULL;
+ gboolean exists = FALSE;
+ gchar* as;
+ gchar* h;
+ int* ip;
+
+ while(hfi) {
+ first_hfi = hfi;
+ hfi = (hfi->same_name_prev_id != -1) ? proto_registrar_get_nth(hfi->same_name_prev_id) : NULL;
+ }
+
+ hfi = first_hfi;
+
+ while (hfi) {
+ exists = TRUE;
+ ip = g_new(int, 1);
+
+ *ip = hfi->id;
+
+ if (( as = (gchar *)g_hash_table_lookup(where,ip) )) {
+ g_free(ip);
+ if (! g_str_equal(as,how)) {
+ report_error(mc,
+ "MATE Error: add field to Pdu: attempt to add %s(%i) as %s"
+ " failed: field already added as '%s'",hfi->abbrev,hfi->id,how,as);
+ return FALSE;
+ }
+ } else {
+ h = g_strdup(how);
+ g_hash_table_insert(where,ip,h);
+ }
+
+ hfi = hfi->same_name_next;
+
+ }
+
+ if (! exists) {
+ report_error(mc, "MATE Error: cannot find field for attribute %s",how);
+ }
+ return exists;
+}
+
+#if 0
+/*
+ * XXX - where is this suposed to be used?
+ */
+extern gchar* add_ranges(mate_config* mc, gchar* range,GPtrArray* range_ptr_arr) {
+ gchar** ranges;
+ guint i;
+ header_field_info* hfi;
+ int* hfidp;
+
+ ranges = g_strsplit(range,"/",0);
+
+ if (ranges) {
+ for (i=0; ranges[i]; i++) {
+ hfi = proto_registrar_get_byname(ranges[i]);
+ if (hfi) {
+ hfidp = g_new(int, 1);
+ *hfidp = hfi->id;
+ g_ptr_array_add(range_ptr_arr,(gpointer)hfidp);
+ } else {
+ g_strfreev(ranges);
+ return ws_strdup_printf("no such proto: '%s'",ranges[i]);
+ }
+ }
+
+ g_strfreev(ranges);
+ }
+
+ return NULL;
+}
+#endif
+
+static void new_attr_hfri(mate_config* mc, gchar* item_name, GHashTable* hfids, gchar* name) {
+ int* p_id = g_new(int, 1);
+ hf_register_info hfri;
+
+ memset(&hfri, 0, sizeof hfri);
+ *p_id = -1;
+ hfri.p_id = p_id;
+ hfri.hfinfo.name = g_strdup(name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.%s",item_name,name);
+ hfri.hfinfo.type = FT_STRING;
+ hfri.hfinfo.display = BASE_NONE;
+ hfri.hfinfo.strings = NULL;
+ hfri.hfinfo.bitmask = 0;
+ hfri.hfinfo.blurb = ws_strdup_printf("%s attribute of %s",name,item_name);
+
+ *p_id = -1;
+ g_hash_table_insert(hfids,name,p_id);
+ g_array_append_val(mc->hfrs,hfri);
+
+}
+
+typedef struct {
+ mate_config* mc;
+ mate_cfg_pdu* cfg;
+} analyze_pdu_hfids_arg;
+
+static void analyze_pdu_hfids(gpointer k, gpointer v, gpointer p) {
+ analyze_pdu_hfids_arg* argp = (analyze_pdu_hfids_arg*)p;
+ mate_config* mc = argp->mc;
+ mate_cfg_pdu* cfg = argp->cfg;
+ new_attr_hfri(mc, cfg->name,cfg->my_hfids,(gchar*) v);
+
+ /*
+ * Add this hfid to our table of wanted hfids.
+ */
+ mc->wanted_hfids = g_array_append_val(mc->wanted_hfids, *(int *)k);
+ mc->num_fields_wanted++;
+}
+
+static void analyze_transform_hfrs(mate_config* mc, gchar* name, GPtrArray* transforms, GHashTable* hfids) {
+ guint i;
+ void* cookie = NULL;
+ AVPL_Transf* t;
+ AVP* avp;
+
+ for (i=0; i < transforms->len;i++) {
+ for (t = (AVPL_Transf *)g_ptr_array_index(transforms,i); t; t=t->next ) {
+ cookie = NULL;
+ while(( avp = get_next_avp(t->replace,&cookie) )) {
+ if (! g_hash_table_lookup(hfids,avp->n)) {
+ new_attr_hfri(mc, name,hfids,avp->n);
+ }
+ }
+ }
+ }
+}
+
+static void analyze_pdu_config(mate_config* mc, mate_cfg_pdu* cfg) {
+ hf_register_info hfri = { NULL, {NULL, NULL, FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL}};
+ gint* ett;
+ analyze_pdu_hfids_arg arg;
+
+ hfri.p_id = &(cfg->hfid);
+ hfri.hfinfo.name = g_strdup(cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s",cfg->name);
+ hfri.hfinfo.blurb = ws_strdup_printf("%s id",cfg->name);
+ hfri.hfinfo.type = FT_UINT32;
+ hfri.hfinfo.display = BASE_DEC;
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_pdu_rel_time);
+ hfri.hfinfo.name = ws_strdup_printf("%s time",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.RelativeTime",cfg->name);
+ hfri.hfinfo.type = FT_FLOAT;
+ hfri.hfinfo.display = BASE_NONE;
+ hfri.hfinfo.blurb = "Seconds passed since the start of capture";
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_pdu_time_in_gop);
+ hfri.hfinfo.name = ws_strdup_printf("%s time since beginning of Gop",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.TimeInGop",cfg->name);
+ hfri.hfinfo.type = FT_FLOAT;
+ hfri.hfinfo.display = BASE_NONE;
+ hfri.hfinfo.blurb = "Seconds passed since the start of the GOP";
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ arg.mc = mc;
+ arg.cfg = cfg;
+ g_hash_table_foreach(cfg->hfids_attr,analyze_pdu_hfids,&arg);
+
+ /* Add the hfids of transport protocols as wanted hfids */
+ for (guint i = 0; i < cfg->transport_ranges->len; i++) {
+ int hfid = *((int*)g_ptr_array_index(cfg->transport_ranges,i));
+ mc->wanted_hfids = g_array_append_val(mc->wanted_hfids, hfid);
+ mc->num_fields_wanted++;
+ }
+
+ ett = &cfg->ett;
+ g_array_append_val(mc->ett,ett);
+
+ ett = &cfg->ett_attr;
+ g_array_append_val(mc->ett,ett);
+
+ analyze_transform_hfrs(mc, cfg->name,cfg->transforms,cfg->my_hfids);
+}
+
+static void analyze_gop_config(gpointer k _U_, gpointer v, gpointer p) {
+ mate_config* mc = (mate_config*)p;
+ mate_cfg_gop* cfg = (mate_cfg_gop *)v;
+ void* cookie = NULL;
+ AVP* avp;
+ gint* ett;
+ hf_register_info hfri = { NULL, {NULL, NULL, FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL}};
+
+ hfri.p_id = &(cfg->hfid);
+ hfri.hfinfo.name = g_strdup(cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s",cfg->name);
+ hfri.hfinfo.blurb = ws_strdup_printf("%s id",cfg->name);
+ hfri.hfinfo.type = FT_UINT32;
+ hfri.hfinfo.display = BASE_DEC;
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_start_time);
+ hfri.hfinfo.name = ws_strdup_printf("%s start time",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.StartTime",cfg->name);
+ hfri.hfinfo.type = FT_FLOAT;
+ hfri.hfinfo.display = BASE_NONE;
+ hfri.hfinfo.blurb = ws_strdup_printf("Seconds passed since the beginning of capture to the start of this %s",cfg->name);
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_stop_time);
+ hfri.hfinfo.name = ws_strdup_printf("%s hold time",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.Time",cfg->name);
+ hfri.hfinfo.blurb = ws_strdup_printf("Duration in seconds from start to stop of this %s",cfg->name);
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_last_time);
+ hfri.hfinfo.name = ws_strdup_printf("%s duration",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.Duration",cfg->name);
+ hfri.hfinfo.blurb = ws_strdup_printf("Time passed between the start of this %s and the last pdu assigned to it",cfg->name);
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_gop_num_pdus);
+ hfri.hfinfo.name = ws_strdup_printf("%s number of PDUs",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.NumOfPdus",cfg->name);
+ hfri.hfinfo.blurb = ws_strdup_printf("Number of PDUs assigned to this %s",cfg->name);
+ hfri.hfinfo.type = FT_UINT32;
+ hfri.hfinfo.display = BASE_DEC;
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_gop_pdu);
+ hfri.hfinfo.name = ws_strdup_printf("A PDU of %s",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.Pdu",cfg->name);
+ hfri.hfinfo.blurb = ws_strdup_printf("A PDU assigned to this %s",cfg->name);
+
+ if (cfg->pdu_tree_mode == GOP_FRAME_TREE) {
+ hfri.hfinfo.type = FT_FRAMENUM;
+ hfri.hfinfo.display = BASE_NONE;
+ g_array_append_val(mc->hfrs,hfri);
+ } else if (cfg->pdu_tree_mode == GOP_PDU_TREE) {
+ hfri.hfinfo.type = FT_UINT32;
+ g_array_append_val(mc->hfrs,hfri);
+ } else {
+ cfg->pdu_tree_mode = GOP_NO_TREE;
+ }
+
+ while(( avp = get_next_avp(cfg->key,&cookie) )) {
+ if (! g_hash_table_lookup(cfg->my_hfids,avp->n)) {
+ new_attr_hfri(mc, cfg->name,cfg->my_hfids,avp->n);
+ }
+ }
+
+ if(cfg->start) {
+ cookie = NULL;
+ while(( avp = get_next_avp(cfg->start,&cookie) )) {
+ if (! g_hash_table_lookup(cfg->my_hfids,avp->n)) {
+ new_attr_hfri(mc, cfg->name,cfg->my_hfids,avp->n);
+ }
+ }
+ }
+
+ if (cfg->stop) {
+ cookie = NULL;
+ while(( avp = get_next_avp(cfg->stop,&cookie) )) {
+ if (! g_hash_table_lookup(cfg->my_hfids,avp->n)) {
+ new_attr_hfri(mc, cfg->name,cfg->my_hfids,avp->n);
+ }
+ }
+ }
+
+ cookie = NULL;
+ while(( avp = get_next_avp(cfg->extra,&cookie) )) {
+ if (! g_hash_table_lookup(cfg->my_hfids,avp->n)) {
+ new_attr_hfri(mc, cfg->name,cfg->my_hfids,avp->n);
+ }
+ }
+
+ analyze_transform_hfrs(mc, cfg->name,cfg->transforms,cfg->my_hfids);
+
+ ett = &cfg->ett;
+ g_array_append_val(mc->ett,ett);
+
+ ett = &cfg->ett_attr;
+ g_array_append_val(mc->ett,ett);
+
+ ett = &cfg->ett_times;
+ g_array_append_val(mc->ett,ett);
+
+ ett = &cfg->ett_children;
+ g_array_append_val(mc->ett,ett);
+
+ g_hash_table_insert(mc->gops_by_pduname,cfg->name,cfg);
+}
+
+static void analyze_gog_config(gpointer k _U_, gpointer v, gpointer p) {
+ mate_config* mc = (mate_config*)p;
+ mate_cfg_gog* cfg = (mate_cfg_gog *)v;
+ void* avp_cookie;
+ void* avpl_cookie;
+ AVP* avp;
+ AVPL* avpl;
+ AVPL* gopkey_avpl;
+ AVPL* key_avps;
+ LoAL* gog_keys = NULL;
+ hf_register_info hfri = { NULL, {NULL, NULL, FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL}};
+ gint* ett;
+
+ /* create the hf array for this gog */
+ hfri.p_id = &(cfg->hfid);
+ hfri.hfinfo.name = g_strdup(cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s",cfg->name);
+ hfri.hfinfo.blurb = ws_strdup_printf("%s Id",cfg->name);
+ hfri.hfinfo.type = FT_UINT32;
+ hfri.hfinfo.display = BASE_DEC;
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_gog_num_of_gops);
+ hfri.hfinfo.name = "number of GOPs";
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.NumOfGops",cfg->name);
+ hfri.hfinfo.type = FT_UINT32;
+ hfri.hfinfo.display = BASE_DEC;
+ hfri.hfinfo.blurb = ws_strdup_printf("Number of GOPs assigned to this %s",cfg->name);
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_gog_gopstart);
+ hfri.hfinfo.name = "GopStart frame";
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.GopStart",cfg->name);
+ hfri.hfinfo.type = FT_FRAMENUM;
+ hfri.hfinfo.display = BASE_NONE;
+ hfri.hfinfo.blurb = g_strdup("The start frame of a GOP");
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_gog_gopstop);
+ hfri.hfinfo.name = "GopStop frame";
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.GopStop",cfg->name);
+ hfri.hfinfo.type = FT_FRAMENUM;
+ hfri.hfinfo.display = BASE_NONE;
+ hfri.hfinfo.blurb = g_strdup("The stop frame of a GOP");
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_start_time);
+ hfri.hfinfo.name = ws_strdup_printf("%s start time",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.StartTime",cfg->name);
+ hfri.hfinfo.type = FT_FLOAT;
+ hfri.hfinfo.blurb = ws_strdup_printf("Seconds passed since the beginning of capture to the start of this %s",cfg->name);
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ hfri.p_id = &(cfg->hfid_last_time);
+ hfri.hfinfo.name = ws_strdup_printf("%s duration",cfg->name);
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.Duration",cfg->name);
+ hfri.hfinfo.blurb = ws_strdup_printf("Time passed between the start of this %s and the last pdu assigned to it",cfg->name);
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ /* this might become mate.gogname.gopname */
+ hfri.p_id = &(cfg->hfid_gog_gop);
+ hfri.hfinfo.name = "a GOP";
+ hfri.hfinfo.abbrev = ws_strdup_printf("mate.%s.Gop",cfg->name);
+ hfri.hfinfo.type = FT_STRING;
+ hfri.hfinfo.display = BASE_NONE;
+ hfri.hfinfo.blurb = ws_strdup_printf("a GOPs assigned to this %s",cfg->name);
+
+ g_array_append_val(mc->hfrs,hfri);
+
+ /* index the keys of gog for every gop
+ and insert the avps of the keys to the hfarray */
+ key_avps = new_avpl("");
+
+ avpl_cookie = NULL;
+ while (( avpl = get_next_avpl(cfg->keys,&avpl_cookie) )) {
+
+ if (! ( gog_keys = (LoAL *)g_hash_table_lookup(mc->gogs_by_gopname,avpl->name))) {
+ gog_keys = new_loal(avpl->name);
+ g_hash_table_insert(mc->gogs_by_gopname,gog_keys->name,gog_keys);
+ }
+
+ gopkey_avpl = new_avpl_from_avpl(cfg->name, avpl, TRUE);
+ loal_append(gog_keys,gopkey_avpl);
+
+ avp_cookie = NULL;
+ while (( avp = get_next_avp(avpl,&avp_cookie) )) {
+ if (! g_hash_table_lookup(cfg->my_hfids,avp->n)) {
+ new_attr_hfri(mc, cfg->name,cfg->my_hfids,avp->n);
+ insert_avp(key_avps,avp);
+ }
+ }
+ }
+
+ /* insert the extra avps to the hfarray */
+ avp_cookie = NULL;
+ while (( avp = get_next_avp(cfg->extra,&avp_cookie) )) {
+ if (! g_hash_table_lookup(cfg->my_hfids,avp->n)) {
+ new_attr_hfri(mc, cfg->name,cfg->my_hfids,avp->n);
+ }
+ }
+
+ /* every key_avp ios an extra as well.
+ one day every Member will have its own extras */
+ merge_avpl(cfg->extra,key_avps,TRUE);
+
+
+ analyze_transform_hfrs(mc, cfg->name,cfg->transforms,cfg->my_hfids);
+
+ ett = &cfg->ett;
+ g_array_append_val(mc->ett,ett);
+
+ ett = &cfg->ett_attr;
+ g_array_append_val(mc->ett,ett);
+
+ ett = &cfg->ett_children;
+ g_array_append_val(mc->ett,ett);
+
+ ett = &cfg->ett_times;
+ g_array_append_val(mc->ett,ett);
+
+ ett = &cfg->ett_gog_gop;
+ g_array_append_val(mc->ett,ett);
+
+}
+
+static void analyze_config(mate_config* mc) {
+ guint i;
+
+ for (i=0; i < mc->pducfglist->len; i++) {
+ analyze_pdu_config(mc, (mate_cfg_pdu*) g_ptr_array_index(mc->pducfglist,i));
+ }
+
+ g_hash_table_foreach(mc->gopcfgs,analyze_gop_config,mc);
+ g_hash_table_foreach(mc->gogcfgs,analyze_gog_config,mc);
+
+}
+
+extern mate_config* mate_make_config(const gchar* filename, int mate_hfid) {
+ mate_config* mc;
+ gint* ett;
+ avp_init();
+
+ mc = g_new(mate_config, 1);
+
+ mc->hfid_mate = mate_hfid;
+
+ mc->wanted_hfids = g_array_new(FALSE, FALSE, (guint)sizeof(int));
+ mc->num_fields_wanted = 0;
+
+ mc->dbg_facility = NULL;
+
+ mc->mate_lib_path = ws_strdup_printf("%s%c%s%c",get_datafile_dir(),DIR_SEP,DEFAULT_MATE_LIB_PATH,DIR_SEP);
+
+ mc->pducfgs = g_hash_table_new(g_str_hash,g_str_equal);
+ mc->gopcfgs = g_hash_table_new(g_str_hash,g_str_equal);
+ mc->gogcfgs = g_hash_table_new(g_str_hash,g_str_equal);
+ mc->transfs = g_hash_table_new(g_str_hash,g_str_equal);
+
+ mc->pducfglist = g_ptr_array_new();
+ mc->gops_by_pduname = g_hash_table_new(g_str_hash,g_str_equal);
+ mc->gogs_by_gopname = g_hash_table_new(g_str_hash,g_str_equal);
+
+ mc->ett_root = -1;
+
+ mc->hfrs = g_array_new(FALSE,FALSE,sizeof(hf_register_info));
+ mc->ett = g_array_new(FALSE,FALSE,sizeof(gint*));
+
+ mc->defaults.pdu.drop_unassigned = FALSE;
+ mc->defaults.pdu.discard = FALSE;
+ mc->defaults.pdu.last_extracted = FALSE;
+ mc->defaults.pdu.match_mode = AVPL_STRICT;
+ mc->defaults.pdu.replace_mode = AVPL_INSERT;
+
+ /* gop prefs */
+ mc->defaults.gop.expiration = -1.0f;
+ mc->defaults.gop.idle_timeout = -1.0f;
+ mc->defaults.gop.lifetime = -1.0f;
+ mc->defaults.gop.pdu_tree_mode = GOP_FRAME_TREE;
+ mc->defaults.gop.show_times = TRUE;
+ mc->defaults.gop.drop_unassigned = FALSE;
+
+ /* gog prefs */
+ mc->defaults.gog.expiration = 5.0f;
+ mc->defaults.gog.show_times = TRUE;
+ mc->defaults.gog.gop_tree_mode = GOP_BASIC_TREE;
+
+ /* what to dbgprint */
+ mc->dbg_lvl = 0;
+ mc->dbg_pdu_lvl = 0;
+ mc->dbg_gop_lvl = 0;
+ mc->dbg_gog_lvl = 0;
+
+ mc->config_error = g_string_new("");
+
+ ett = &mc->ett_root;
+ g_array_append_val(mc->ett,ett);
+
+ if ( mate_load_config(filename,mc) ) {
+ analyze_config(mc);
+ } else {
+ report_failure("MATE failed to configure!\n"
+ "It is recommended that you fix your config and restart Wireshark.\n"
+ "The reported error is:\n%s\n",mc->config_error->str);
+
+ /* if (mc) destroy_mate_config(mc,FALSE); */
+ return NULL;
+ }
+
+ if (mc->num_fields_wanted == 0) {
+ /* We have no interest in any fields, so we have no
+ work to do. */
+ /*destroy_mate_config(mc,FALSE);*/
+ return NULL;
+ }
+
+ return mc;
+}
+
+/*
+ * 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:
+ */
diff --git a/plugins/epan/mate/mate_util.c b/plugins/epan/mate/mate_util.c
new file mode 100644
index 00000000..5b4d0780
--- /dev/null
+++ b/plugins/epan/mate/mate_util.c
@@ -0,0 +1,1690 @@
+/* mate_util.c
+ * MATE -- Meta Analysis Tracing Engine
+ * Utility Library: Single Copy Strings and Attribute Value Pairs
+ *
+ * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * 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 "mate.h"
+#include "mate_util.h"
+
+#include <errno.h>
+#include <wsutil/file_util.h>
+
+
+/***************************************************************************
+* dbg_print
+***************************************************************************
+* This is the debug facility of the thing.
+***************************************************************************/
+
+/* dbg_print:
+ * which: a pointer to the current level of debugging for a feature
+ * how: the level over which this message should be printed out
+ * where: the file on which to print (ws_message if null)
+ * fmt, ...: what to print
+ */
+
+void dbg_print(const gint* which, gint how, FILE* where, const gchar* fmt, ... ) {
+ static gchar debug_buffer[DEBUG_BUFFER_SIZE];
+ va_list list;
+
+ if ( ! which || *which < how ) return;
+
+ va_start( list, fmt );
+ vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE,fmt,list);
+ va_end( list );
+
+ if (! where) {
+ ws_message("%s", debug_buffer);
+ } else {
+ fputs(debug_buffer,where);
+ fputs("\n",where);
+ }
+
+}
+
+/***************************************************************************
+ * single copy strings
+ ***************************************************************************
+ * Strings repeat more often than don't. In order to save memory
+ * we'll keep only one copy of each as key to a hash with a count of
+ * subscribers as value.
+ ***************************************************************************/
+
+/**
+ * scs_init:
+ *
+ * Initializes the scs hash.
+ **/
+
+struct _scs_collection {
+ GHashTable* hash; /* key: a string value: guint number of subscribers */
+};
+
+/* ToDo? free any string,ctr entries pointed to by the hash table ??
+ * XXX: AFAIKT destroy_scs_collection() might be called only when reading a
+ * mate config file. Since reading a new config file can apparently currently
+ * only be done once after starting Wireshark, in theory this fcn
+ * currently should never be called since there will never be an existing
+ * scs_collection to be destroyed.
+ */
+static void destroy_scs_collection(SCS_collection* c) {
+ if (c->hash) g_hash_table_destroy(c->hash);
+}
+
+static SCS_collection* scs_init(void) {
+ SCS_collection* c = g_new(SCS_collection, 1);
+
+ c->hash = g_hash_table_new(g_str_hash,g_str_equal);
+
+ return c;
+}
+
+
+/**
+ * subscribe:
+ * @param c the scs hash
+ * @param s a string
+ *
+ * Checks if the given string exists already and if so it increases the count of
+ * subsscribers and returns a pointer to the stored string. If not It will copy
+ * the given string store it in the hash and return the pointer to the copy.
+ * Remember, containment is handled internally, take care of your own strings.
+ *
+ * Return value: a pointer to the subscribed string.
+ **/
+gchar* scs_subscribe(SCS_collection* c, const gchar* s) {
+ gchar* orig = NULL;
+ guint* ip = NULL;
+ size_t len = 0;
+
+ g_hash_table_lookup_extended(c->hash,(gconstpointer)s,(gpointer *)&orig,(gpointer *)&ip);
+
+ if (ip) {
+ (*ip)++;
+ } else {
+ ip = g_slice_new(guint);
+ *ip = 0;
+
+ len = strlen(s) + 1;
+
+ if (len <= SCS_SMALL_SIZE) {
+ len = SCS_SMALL_SIZE;
+ } else if (len <= SCS_MEDIUM_SIZE) {
+ len = SCS_MEDIUM_SIZE;
+ } else if (len <= SCS_LARGE_SIZE) {
+ len = SCS_LARGE_SIZE;
+ } else if (len < SCS_HUGE_SIZE) {
+ len = SCS_HUGE_SIZE;
+ } else {
+ len = SCS_HUGE_SIZE;
+ ws_warning("mate SCS: string truncated due to huge size");
+ }
+
+ orig = (gchar *)g_slice_alloc(len);
+ (void) g_strlcpy(orig,s,len);
+
+ g_hash_table_insert(c->hash,orig,ip);
+ }
+
+ return orig;
+}
+
+/**
+ * unsubscribe:
+ * @param c the scs hash
+ * @param s a string.
+ *
+ * decreases the count of subscribers, if zero frees the internal copy of
+ * the string.
+ **/
+void scs_unsubscribe(SCS_collection* c, gchar* s) {
+ gchar* orig = NULL;
+ guint* ip = NULL;
+ size_t len = 0xffff;
+
+ g_hash_table_lookup_extended(c->hash,(gconstpointer)s,(gpointer *)&orig,(gpointer *)&ip);
+
+ if (ip) {
+ if (*ip == 0) {
+ g_hash_table_remove(c->hash,orig);
+
+ len = strlen(orig);
+
+ if (len < SCS_SMALL_SIZE) {
+ len = SCS_SMALL_SIZE;
+ } else if (len < SCS_MEDIUM_SIZE) {
+ len = SCS_MEDIUM_SIZE;
+ } else if (len < SCS_LARGE_SIZE) {
+ len = SCS_LARGE_SIZE;
+ } else {
+ len = SCS_HUGE_SIZE;
+ }
+
+ g_slice_free1(len, orig);
+ g_slice_free(guint,ip);
+ }
+ else {
+ (*ip)--;
+ }
+ } else {
+ ws_warning("unsubscribe: not subscribed");
+ }
+}
+
+/**
+ * scs_subscribe_printf:
+ * @param fmt a format string ...
+ *
+ * Formats the input and subscribes it.
+ *
+ * Return value: the stored copy of the formated string.
+ *
+ **/
+gchar* scs_subscribe_printf(SCS_collection* c, gchar* fmt, ...) {
+ va_list list;
+ static gchar buf[SCS_HUGE_SIZE];
+
+ va_start( list, fmt );
+ vsnprintf(buf, SCS_HUGE_SIZE, fmt, list);
+ va_end( list );
+
+ return scs_subscribe(c,buf);
+}
+
+/***************************************************************************
+* AVPs & Co.
+***************************************************************************
+* The Thing operates mainly on avps, avpls and loals
+* - attribute value pairs (two strings: the name and the value and an operator)
+* - avp lists a somehow sorted list of avps
+* - loal (list of avp lists) an arbitrarily sorted list of avpls
+*
+*
+***************************************************************************/
+
+
+typedef union _any_avp_type {
+ AVP avp;
+ AVPN avpn;
+ AVPL avpl;
+ LoAL loal;
+ LoALnode loaln;
+} any_avp_type;
+
+
+static SCS_collection* avp_strings = NULL;
+
+#ifdef _AVP_DEBUGGING
+static FILE* dbg_fp = NULL;
+
+static int dbg_level = 0;
+static int* dbg = &dbg_level;
+
+static int dbg_avp_level = 0;
+static int* dbg_avp = &dbg_avp_level;
+
+static int dbg_avp_op_level = 0;
+static int* dbg_avp_op = &dbg_avp_op_level;
+
+static int dbg_avpl_level = 0;
+static int* dbg_avpl = &dbg_avpl_level;
+
+static int dbg_avpl_op_level = 0;
+static int* dbg_avpl_op = &dbg_avpl_op_level;
+
+/**
+ * setup_avp_debug:
+ * @param fp the file in which to send debugging output.
+ * @param general a pointer to the level of debugging of facility "general"
+ * @param avp a pointer to the level of debugging of facility "avp"
+ * @param avp_op a pointer to the level of debugging of facility "avp_op"
+ * @param avpl a pointer to the level of debugging of facility "avpl"
+ * @param avpl_op a pointer to the level of debugging of facility "avpl_op"
+ *
+ * If enabled sets up the debug facilities for the avp library.
+ *
+ **/
+extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op) {
+ dbg_fp = fp;
+ dbg = general;
+ dbg_avp = avp;
+ dbg_avp_op = avp_op;
+ dbg_avpl = avpl;
+ dbg_avpl_op = avpl_op;
+}
+
+#endif /* _AVP_DEBUGGING */
+
+/**
+ * avp_init:
+ *
+ * (Re)Initializes the avp library.
+ *
+ **/
+extern void avp_init(void) {
+
+ if (avp_strings) destroy_scs_collection(avp_strings);
+ avp_strings = scs_init();
+
+}
+
+/**
+ * new_avp_from_finfo:
+ * @param name the name the avp will have.
+ * @param finfo the field_info from which to fetch the data.
+ *
+ * Creates an avp from a field_info record.
+ *
+ * Return value: a pointer to the newly created avp.
+ *
+ **/
+extern AVP* new_avp_from_finfo(const gchar* name, field_info* finfo) {
+ AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
+ gchar* value;
+ gchar* repr;
+
+ new_avp_val->n = scs_subscribe(avp_strings, name);
+
+ repr = fvalue_to_string_repr(NULL, finfo->value, FTREPR_DISPLAY, finfo->hfinfo->display);
+
+ if (repr) {
+ value = scs_subscribe(avp_strings, repr);
+ wmem_free(NULL, repr);
+#ifdef _AVP_DEBUGGING
+ dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from string: %s",value);
+#endif
+ } else {
+#ifdef _AVP_DEBUGGING
+ dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: a proto: %s",finfo->hfinfo->abbrev);
+#endif
+ value = scs_subscribe(avp_strings, "");
+ }
+
+ new_avp_val->v = value;
+
+ new_avp_val->o = '=';
+
+#ifdef _AVP_DEBUGGING
+ dbg_print (dbg_avp,1,dbg_fp,"new_avp_from_finfo: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
+#endif
+
+ return new_avp_val;
+}
+
+
+/**
+ * new_avp:
+ * @param name the name the avp will have.
+ * @param value the value the avp will have.
+ * @param o the operator of this avp.
+ *
+ * Creates an avp given every parameter.
+ *
+ * Return value: a pointer to the newly created avp.
+ *
+ **/
+extern AVP* new_avp(const gchar* name, const gchar* value, gchar o) {
+ AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
+
+ new_avp_val->n = scs_subscribe(avp_strings, name);
+ new_avp_val->v = scs_subscribe(avp_strings, value);
+ new_avp_val->o = o;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avp,1,dbg_fp,"new_avp_val: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
+#endif
+ return new_avp_val;
+}
+
+
+/**
+* delete_avp:
+ * @param avp the avp to delete.
+ *
+ * Destroys an avp and releases the resources it uses.
+ *
+ **/
+extern void delete_avp(AVP* avp) {
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avp,1,dbg_fp,"delete_avp: %p %s%c%s;",avp,avp->n,avp->o,avp->v);
+#endif
+
+ scs_unsubscribe(avp_strings, avp->n);
+ scs_unsubscribe(avp_strings, avp->v);
+ g_slice_free(any_avp_type,(any_avp_type*)avp);
+}
+
+
+/**
+* avp_copy:
+ * @param from the avp to be copied.
+ *
+ * Creates an avp whose name op and value are copies of the given one.
+ *
+ * Return value: a pointer to the newly created avp.
+ *
+ **/
+extern AVP* avp_copy(AVP* from) {
+ AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
+
+ new_avp_val->n = scs_subscribe(avp_strings, from->n);
+ new_avp_val->v = scs_subscribe(avp_strings, from->v);
+ new_avp_val->o = from->o;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avp,1,dbg_fp,"copy_avp: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
+#endif
+
+ return new_avp_val;
+}
+
+/**
+ * new_avpl:
+ * @param name the name the avpl will have.
+ *
+ * Creates an empty avpl.
+ *
+ * Return value: a pointer to the newly created avpl.
+ *
+ **/
+extern AVPL* new_avpl(const gchar* name) {
+ AVPL* new_avpl_p = (AVPL*)g_slice_new(any_avp_type);
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl_p: %p name=%s",new_avpl_p,name);
+#endif
+
+ new_avpl_p->name = name ? scs_subscribe(avp_strings, name) : scs_subscribe(avp_strings, "");
+ new_avpl_p->len = 0;
+ new_avpl_p->null.avp = NULL;
+ new_avpl_p->null.next = &new_avpl_p->null;
+ new_avpl_p->null.prev = &new_avpl_p->null;
+
+
+ return new_avpl_p;
+}
+
+extern void rename_avpl(AVPL* avpl, gchar* name) {
+ scs_unsubscribe(avp_strings,avpl->name);
+ avpl->name = scs_subscribe(avp_strings,name);
+}
+
+/**
+ * insert_avp_before_node:
+ * @param avpl the avpl in which to insert.
+ * @param next_node the next node before which the new avpn has to be inserted.
+ * @param avp the avp to be inserted.
+ * @param copy_avp whether the original AVP or a copy thereof must be inserted.
+ *
+ * Pre-condition: the avp is sorted before before_avp and does not already exist
+ * in the avpl.
+ */
+static void insert_avp_before_node(AVPL* avpl, AVPN* next_node, AVP *avp, gboolean copy_avp) {
+ AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type);
+
+ new_avp_val->avp = copy_avp ? avp_copy(avp) : avp;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %p",new_avp_val);
+ dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %p in %p before %p;",avp,avpl,next_node);
+#endif
+
+ new_avp_val->next = next_node;
+ new_avp_val->prev = next_node->prev;
+ next_node->prev->next = new_avp_val;
+ next_node->prev = new_avp_val;
+
+ avpl->len++;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
+#endif
+}
+
+/**
+ * insert_avp:
+ * @param avpl the avpl in which to insert.
+ * @param avp the avp to be inserted.
+ *
+ * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
+ *
+ * Return value: whether it was inserted or not.
+ *
+ * BEWARE: Check the return value, you might need to delete the avp if
+ * it is not inserted.
+ **/
+extern gboolean insert_avp(AVPL* avpl, AVP* avp) {
+ AVPN* c;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %p %p %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
+#endif
+
+ /* get to the insertion point */
+ for (c=avpl->null.next; c->avp; c = c->next) {
+ int name_diff = strcmp(avp->n, c->avp->n);
+
+ if (name_diff == 0) {
+ int value_diff = strcmp(avp->v, c->avp->v);
+
+ if (value_diff < 0) {
+ break;
+ }
+
+ if (value_diff == 0) {
+ // ignore duplicate values, prevents (a=1, a=1)
+ // note that this is also used to insert
+ // conditions AVPs, so really check if the name,
+ // value and operator are all equal.
+ if (c->avp->o == avp->o && avp->o == AVP_OP_EQUAL) {
+ return FALSE;
+ }
+ }
+ }
+
+ if (name_diff < 0) {
+ break;
+ }
+ }
+
+ insert_avp_before_node(avpl, c, avp, FALSE);
+
+ return TRUE;
+}
+
+/**
+ * get_avp_by_name:
+ * @param avpl the avpl from which to try to get the avp.
+ * @param name the name of the avp we are looking for.
+ * @param cookie variable in which to store the state between calls.
+ *
+ * Gets pointer to the next avp whose name is given; uses cookie to store its
+ * state between calls.
+ *
+ * Return value: a pointer to the next matching avp if there's one, else NULL.
+ *
+ **/
+extern AVP* get_avp_by_name(AVPL* avpl, gchar* name, void** cookie) {
+ AVPN* curr;
+ AVPN* start = (AVPN*) *cookie;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %p %s %p",avpl,name,*cookie);
+#endif
+
+ name = scs_subscribe(avp_strings, name);
+
+ if (!start) start = avpl->null.next;
+
+ for ( curr = start; curr->avp; curr = curr->next ) {
+ if ( curr->avp->n == name ) {
+ break;
+ }
+ }
+
+ *cookie = curr;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %p",curr);
+#endif
+
+ scs_unsubscribe(avp_strings, name);
+
+ return curr->avp;
+}
+
+/**
+ * extract_avp_by_name:
+ * @param avpl the avpl from which to try to extract the avp.
+ * @param name the name of the avp we are looking for.
+ *
+ * Extracts from the avpl the next avp whose name is given;
+ *
+ * Return value: a pointer to extracted avp if there's one, else NULL.
+ *
+ **/
+extern AVP* extract_avp_by_name(AVPL* avpl, gchar* name) {
+ AVPN* curr;
+ AVP* avp = NULL;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %p %s",avpl,name);
+#endif
+
+ name = scs_subscribe(avp_strings, name);
+
+ for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
+ if ( curr->avp->n == name ) {
+ break;
+ }
+ }
+
+ scs_unsubscribe(avp_strings, name);
+
+ if( ! curr->avp ) return NULL;
+
+ curr->next->prev = curr->prev;
+ curr->prev->next = curr->next;
+
+ avp = curr->avp;
+
+ g_slice_free(any_avp_type,(any_avp_type*)curr);
+
+ (avpl->len)--;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
+#endif
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %p",avp);
+#endif
+
+ return avp;
+}
+
+
+/**
+ * extract_first_avp:
+ * @param avpl the avpl from which to try to extract the avp.
+ *
+ * Extracts the fisrt avp from the avpl.
+ *
+ * Return value: a pointer to extracted avp if there's one, else NULL.
+ *
+ **/
+extern AVP* extract_first_avp(AVPL* avpl) {
+ AVP* avp;
+ AVPN* node;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %p",avpl);
+#endif
+
+ node = avpl->null.next;
+
+ avpl->null.next->prev = &avpl->null;
+ avpl->null.next = node->next;
+
+ avp = node->avp;
+
+ if (avp) {
+ g_slice_free(any_avp_type,(any_avp_type*)node);
+ (avpl->len)--;
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
+#endif
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %p",avp);
+#endif
+
+ return avp;
+
+}
+
+
+/**
+ * extract_last_avp:
+ * @param avpl the avpl from which to try to extract the avp.
+ *
+ * Extracts the last avp from the avpl.
+ *
+ * Return value: a pointer to extracted avp if there's one, else NULL.
+ *
+ **/
+extern AVP* extract_last_avp(AVPL* avpl) {
+ AVP* avp;
+ AVPN* node;
+
+ node = avpl->null.prev;
+
+ avpl->null.prev->next = &avpl->null;
+ avpl->null.prev = node->prev;
+
+ avp = node->avp;
+
+ if (avp) {
+ g_slice_free(any_avp_type,(any_avp_type*)node);
+ (avpl->len)--;
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
+#endif
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",avp);
+#endif
+
+ return avp;
+
+}
+
+
+/**
+ * delete_avpl:
+ * @param avpl the avpl from which to try to extract the avp.
+ * @param avps_too whether or not it should delete the avps as well.
+ *
+ * Destroys an avpl and releases the resources it uses. If told to do
+ * so releases the avps as well.
+ *
+ **/
+extern void delete_avpl(AVPL* avpl, gboolean avps_too) {
+ AVP* avp;
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %p",avpl);
+#endif
+
+ while(( avp = extract_last_avp(avpl))) {
+ if (avps_too) {
+ delete_avp(avp);
+ }
+ }
+
+ scs_unsubscribe(avp_strings,avpl->name);
+ g_slice_free(any_avp_type,(any_avp_type*)avpl);
+}
+
+
+
+/**
+ * get_next_avp:
+ * @param avpl the avpl from which to try to get the avps.
+ * @param cookie variable in which to store the state between calls.
+ *
+ * Iterates on an avpl to get its avps.
+ *
+ * Return value: a pointer to the next avp if there's one, else NULL.
+ *
+ **/
+extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
+ AVPN* node;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %p avpn: %p",avpl,*cookie);
+#endif
+
+ if (*cookie) {
+ node = (AVPN*) *cookie;
+ } else {
+ node = avpl->null.next;
+ }
+
+ *cookie = node->next;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",node->avp);
+#endif
+
+ return node->avp;
+}
+
+/**
+ * avpl_to_str:
+ * @param avpl the avpl to represent.
+ *
+ * Creates a newly allocated string containing a representation of an avpl.
+ *
+ * Return value: a pointer to the newly allocated string.
+ *
+ **/
+gchar* avpl_to_str(AVPL* avpl) {
+ AVPN* c;
+ GString* s = g_string_new("");
+ gchar* avp_s;
+ gchar* r;
+
+ for(c=avpl->null.next; c->avp; c = c->next) {
+ avp_s = avp_to_str(c->avp);
+ g_string_append_printf(s," %s;",avp_s);
+ g_free(avp_s);
+ }
+
+ r = g_string_free(s,FALSE);
+
+ /* g_strchug(r); ? */
+ return r;
+}
+
+extern gchar* avpl_to_dotstr(AVPL* avpl) {
+ AVPN* c;
+ GString* s = g_string_new("");
+ gchar* avp_s;
+ gchar* r;
+
+ for(c=avpl->null.next; c->avp; c = c->next) {
+ avp_s = avp_to_str(c->avp);
+ g_string_append_printf(s," .%s;",avp_s);
+ g_free(avp_s);
+ }
+
+ r = g_string_free(s,FALSE);
+
+ /* g_strchug(r); ? */
+ return r;
+}
+
+/**
+* merge_avpl:
+ * @param dst the avpl in which to merge the avps.
+ * @param src the avpl from which to get the avps.
+ * @param copy_avps whether avps should be copied instead of referenced.
+ *
+ * Adds the avps of src that are not existent in dst into dst.
+ *
+ **/
+extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
+ AVPN* cd = NULL;
+ AVPN* cs = NULL;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %p %p",dst,src);
+#endif
+
+ cs = src->null.next;
+ cd = dst->null.next;
+
+ while (cs->avp && cd->avp) {
+
+ int name_diff = strcmp(cd->avp->n, cs->avp->n);
+
+ if (name_diff < 0) {
+ // dest < source, advance dest to find a better place to insert
+ cd = cd->next;
+ } else if (name_diff > 0) {
+ // dest > source, so it can be definitely inserted here.
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
+ cs = cs->next;
+ } else {
+ // attribute names are equal. Ignore duplicate values but ensure that other values are sorted.
+ int value_diff = strcmp(cd->avp->v, cs->avp->v);
+
+ if (value_diff < 0) {
+ // dest < source, do not insert it yet
+ cd = cd->next;
+ } else if (value_diff > 0) {
+ // dest > source, insert AVP before the current dest AVP
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
+ cs = cs->next;
+ } else {
+ // identical AVPs, do not create a duplicate.
+ cs = cs->next;
+ }
+ }
+ }
+
+ // if there are remaing source AVPs while there are no more destination
+ // AVPs (cd now represents the NULL item, after the last item), append
+ // all remaining source AVPs to the end
+ while (cs->avp) {
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
+ cs = cs->next;
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
+#endif
+
+ return;
+}
+
+
+/**
+ * new_avpl_from_avpl:
+ * @param name the name of the new avpl.
+ * @param avpl the avpl from which to get the avps.
+ * @param copy_avps whether avps should be copied instead of referenced.
+ *
+ * Creates a new avpl containing the same avps as the given avpl
+ * It will either reference or copie the avps.
+ *
+ * Return value: a pointer to the newly allocated string.
+ *
+ **/
+extern AVPL* new_avpl_from_avpl(const gchar* name, AVPL* avpl, gboolean copy_avps) {
+ AVPL* newavpl = new_avpl(name);
+ void* cookie = NULL;
+ AVP* avp;
+ AVP* copy;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_from_avpl: %p from=%p name='%s'",newavpl,avpl,name);
+#endif
+
+ while(( avp = get_next_avp(avpl,&cookie) )) {
+ if (copy_avps) {
+ copy = avp_copy(avp);
+ if ( ! insert_avp(newavpl,copy) ) {
+ delete_avp(copy);
+ }
+ } else {
+ insert_avp(newavpl,avp);
+ }
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
+#endif
+
+ return newavpl;
+}
+
+/**
+* match_avp:
+ * @param src an src to be compared agains an "op" avp
+ * @param op the "op" avp that will be matched against the src avp
+ *
+ * Checks whether or not two avp's match.
+ *
+ * Return value: a pointer to the src avp if there's a match.
+ *
+ **/
+extern AVP* match_avp(AVP* src, AVP* op) {
+ gchar** splited;
+ int i;
+ gchar* p;
+ guint ls;
+ guint lo;
+ float fs = 0.0f;
+ float fo = 0.0f;
+ gboolean lower = FALSE;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"match_avp: %s%c%s; vs. %s%c%s;",src->n,src->o,src->v,op->n,op->o,op->v);
+#endif
+
+ if ( src->n != op->n ) {
+ return NULL;
+ }
+
+ switch (op->o) {
+ case AVP_OP_EXISTS:
+ return src;
+ case AVP_OP_EQUAL:
+ return src->v == op->v ? src : NULL;
+ case AVP_OP_NOTEQUAL:
+ return !( src->v == op->v) ? src : NULL;
+ case AVP_OP_STARTS:
+ return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL;
+ case AVP_OP_ONEOFF:
+ splited = g_strsplit(op->v,"|",0);
+ if (splited) {
+ for (i=0;splited[i];i++) {
+ if(g_str_equal(splited[i],src->v)) {
+ g_strfreev(splited);
+ return src;
+ }
+ }
+ g_strfreev(splited);
+ }
+ return NULL;
+
+ case AVP_OP_LOWER:
+ lower = TRUE;
+ /* FALLTHRU */
+ case AVP_OP_HIGHER:
+
+ fs = (float) g_ascii_strtod(src->v, NULL);
+ fo = (float) g_ascii_strtod(op->v, NULL);
+
+ if (lower) {
+ if (fs<fo) return src;
+ else return NULL;
+ } else {
+ if (fs>fo) return src;
+ else return NULL;
+ }
+ case AVP_OP_ENDS:
+ /* does this work? */
+ ls = (guint) strlen(src->v);
+ lo = (guint) strlen(op->v);
+
+ if ( ls < lo ) {
+ return NULL;
+ } else {
+ p = src->v + ( ls - lo );
+ return g_str_equal(p,op->v) ? src : NULL;
+ }
+
+ /* case AVP_OP_TRANSF: */
+ /* return do_transform(src,op); */
+ case AVP_OP_CONTAINS:
+ return g_strrstr(src->v, op->v) ? src : NULL;;
+ }
+ /* will never get here */
+ return NULL;
+}
+
+
+
+/**
+ * new_avpl_loose_match:
+ * @param name the name of the resulting avpl
+ * @param src the data AVPL to be matched against a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
+ * @param copy_avps whether the avps in the resulting avpl should be copied
+ *
+ * Creates a new AVP list containing all data AVPs that matched any of the
+ * conditions AVPs. If there are no matches, an empty list will be returned.
+ *
+ * Note: Loose will always be considered a successful match, it matches zero or
+ * more conditions.
+ */
+extern AVPL* new_avpl_loose_match(const gchar* name,
+ AVPL* src,
+ AVPL* op,
+ gboolean copy_avps) {
+
+ AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
+ AVPN* co = NULL;
+ AVPN* cs = NULL;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %p src=%p op=%p name='%s'",newavpl,src,op,name);
+#endif
+
+
+ cs = src->null.next;
+ co = op->null.next;
+ while (cs->avp && co->avp) {
+ int name_diff = strcmp(co->avp->n, cs->avp->n);
+
+ if (name_diff < 0) {
+ // op < source, op is not matching
+ co = co->next;
+ } else if (name_diff > 0) {
+ // op > source, source is not matching
+ cs = cs->next;
+ } else {
+ // attribute match found, let's see if there is any condition (op) that accepts this data AVP.
+ AVPN *cond = co;
+ do {
+ if (match_avp(cs->avp, cond->avp)) {
+ insert_avp_before_node(newavpl, newavpl->null.prev, cs->avp, copy_avps);
+ break;
+ }
+ cond = cond->next;
+ } while (cond->avp && cond->avp->n == cs->avp->n);
+ cs = cs->next;
+ }
+ }
+
+ // return matches (possible none)
+ return newavpl;
+}
+
+/**
+* new_avpl_pairs_match:
+ * @param name the name of the resulting avpl
+ * @param src the data AVPL to be matched against a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
+ * @param strict TRUE if every condition must have a matching data AVP, FALSE if
+ * it is also acceptable that only one of the condition AVPs for the same
+ * attribute is matching.
+ * @param copy_avps whether the avps in the resulting avpl should be copied
+ *
+ * Creates an AVP list by matching pairs of conditions and data AVPs, returning
+ * the data AVPs. If strict is TRUE, then each condition must be paired with a
+ * matching data AVP. If strict is FALSE, then some conditions are allowed to
+ * fail when other conditions for the same attribute do have a match. Note that
+ * if the condition AVPL is empty, the result will be a match (an empty list).
+ *
+ * Return value: a pointer to the newly created avpl containing the
+ * matching avps or NULL if there is no match.
+ */
+extern AVPL* new_avpl_pairs_match(const gchar* name, AVPL* src, AVPL* op, gboolean strict, gboolean copy_avps) {
+ AVPL* newavpl;
+ AVPN* co = NULL;
+ AVPN* cs = NULL;
+ const gchar *last_match = NULL;
+ gboolean matched = TRUE;
+
+ newavpl = new_avpl(scs_subscribe(avp_strings, name));
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"%s: %p src=%p op=%p name='%s'",G_STRFUNC,newavpl,src,op,name);
+#endif
+
+ cs = src->null.next;
+ co = op->null.next;
+ while (cs->avp && co->avp) {
+ int name_diff = g_strcmp0(co->avp->n, cs->avp->n);
+ const gchar *failed_match = NULL;
+
+ if (name_diff < 0) {
+ // op < source, op has no data avp with same attribute.
+ failed_match = co->avp->n;
+ co = co->next;
+ } else if (name_diff > 0) {
+ // op > source, the source avp is not matched by any condition
+ cs = cs->next;
+ } else {
+ // Matching attributes found, now try to find a matching data AVP for the condition.
+ if (match_avp(cs->avp, co->avp)) {
+ insert_avp_before_node(newavpl, newavpl->null.prev, cs->avp, copy_avps);
+ last_match = co->avp->n;
+ cs = cs->next;
+ } else {
+ failed_match = co->avp->n;
+ }
+ co = co->next;
+ }
+
+ // condition did not match, check if we can continue matching.
+ if (failed_match) {
+ if (strict) {
+ matched = FALSE;
+ break;
+ } else if (last_match != failed_match) {
+ // None of the conditions so far matched the attribute, check for other candidates
+ if (!co->avp || co->avp->n != last_match) {
+ matched = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ // if there are any conditions remaining, then those could not be matched
+ if (matched && strict && co->avp) {
+ matched = FALSE;
+ }
+
+ if (matched) {
+ // there was a match, accept it
+ return newavpl;
+ } else {
+ // no match, only delete AVPs too if they were copied
+ delete_avpl(newavpl, copy_avps);
+ return NULL;
+ }
+}
+
+
+/**
+ * new_avpl_from_match:
+ * @param mode The matching method, one of AVPL_STRICT, AVPL_LOOSE, AVPL_EVERY.
+ * @param name the name of the resulting avpl
+ * @param src the data AVPL to be matched agains a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
+ *
+ * Matches the conditions AVPL against the original AVPL according to the mode.
+ * If there is no match, NULL is returned. If there is actually a match, then
+ * the matching AVPs (a subset of the data) are returned.
+ */
+extern AVPL* new_avpl_from_match(avpl_match_mode mode, const gchar* name,AVPL* src, AVPL* op, gboolean copy_avps) {
+ AVPL* avpl = NULL;
+
+ switch (mode) {
+ case AVPL_STRICT:
+ avpl = new_avpl_pairs_match(name, src, op, TRUE, copy_avps);
+ break;
+ case AVPL_LOOSE:
+ avpl = new_avpl_loose_match(name,src,op,copy_avps);
+ break;
+ case AVPL_EVERY:
+ avpl = new_avpl_pairs_match(name, src, op, FALSE, copy_avps);
+ break;
+ case AVPL_NO_MATCH:
+ // XXX this seems unused
+ avpl = new_avpl_from_avpl(name,src,copy_avps);
+ merge_avpl(avpl, op, copy_avps);
+ break;
+ }
+
+ return avpl;
+}
+
+/**
+ * delete_avpl_transform:
+ * @param op a pointer to the avpl transformation object
+ *
+ * Destroys an avpl transformation object and releases all the resources it
+ * uses.
+ *
+ **/
+extern void delete_avpl_transform(AVPL_Transf* op) {
+ AVPL_Transf* next;
+
+ for (; op ; op = next) {
+ next = op->next;
+
+ g_free(op->name);
+
+ if (op->match) {
+ delete_avpl(op->match,TRUE);
+ }
+
+ if (op->replace) {
+ delete_avpl(op->replace,TRUE);
+ }
+
+ g_free(op);
+ }
+
+}
+
+
+/**
+ * avpl_transform:
+ * @param src the source avpl for the transform operation.
+ * @param op a pointer to the avpl transformation object to apply.
+ *
+ * Applies the "op" transformation to an avpl, matches it and eventually
+ * replaces or inserts the transformed avps.
+ *
+ * Return value: whether the transformation was performed or not.
+ **/
+extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
+ AVPL* avpl = NULL;
+ AVPN* cs;
+ AVPN* cm;
+ AVPN* n;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%p op=%p",src,op);
+#endif
+
+ for ( ; op ; op = op->next) {
+
+ avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, TRUE);
+
+ if (avpl) {
+ switch (op->replace_mode) {
+ case AVPL_NO_REPLACE:
+ delete_avpl(avpl,TRUE);
+ return;
+ case AVPL_INSERT:
+ merge_avpl(src,op->replace,TRUE);
+ delete_avpl(avpl,TRUE);
+ return;
+ case AVPL_REPLACE:
+ cs = src->null.next;
+ cm = avpl->null.next;
+ // Removes AVPs from the source which are in the matched data.
+ // Assume that the matched set is a subset of the source.
+ while (cs->avp && cm->avp) {
+ if (cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
+ n = cs->next;
+
+ cs->prev->next = cs->next;
+ cs->next->prev = cs->prev;
+ g_slice_free(any_avp_type,(any_avp_type*)cs);
+
+ cs = n;
+ cm = cm->next;
+ } else {
+ // Current matched AVP is not equal to the current
+ // source AVP. Since there must be a source AVP for
+ // each matched AVP, advance current source and not
+ // the match AVP.
+ cs = cs->next;
+ }
+ }
+
+ merge_avpl(src,op->replace,TRUE);
+ delete_avpl(avpl,TRUE);
+ return;
+ }
+ }
+ }
+}
+
+
+/**
+ * new_loal:
+ * @param name the name the loal will take.
+ *
+ * Creates an empty list of avp lists.
+ *
+ * Return value: a pointer to the newly created loal.
+ **/
+extern LoAL* new_loal(const gchar* name) {
+ LoAL* new_loal_p = (LoAL*)g_slice_new(any_avp_type);
+
+ if (! name) {
+ name = "anonymous";
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_p: %p name=%s",new_loal_p,name);
+#endif
+
+ new_loal_p->name = scs_subscribe(avp_strings,name);
+ new_loal_p->null.avpl = NULL;
+ new_loal_p->null.next = &new_loal_p->null;
+ new_loal_p->null.prev = &new_loal_p->null;
+ new_loal_p->len = 0;
+ return new_loal_p;
+}
+
+/**
+ * loal_append:
+ * @param loal the loal on which to operate.
+ * @param avpl the avpl to append.
+ *
+ * Appends an avpl to a loal.
+ *
+ **/
+extern void loal_append(LoAL* loal, AVPL* avpl) {
+ LoALnode* node = (LoALnode*)g_slice_new(any_avp_type);
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %p",node);
+#endif
+
+ node->avpl = avpl;
+ node->next = &loal->null;
+ node->prev = loal->null.prev;
+
+ loal->null.prev->next = node;
+ loal->null.prev = node;
+ loal->len++;
+}
+
+
+/**
+ * extract_first_avpl:
+ * @param loal the loal on which to operate.
+ *
+ * Extracts the first avpl contained in a loal.
+ *
+ * Return value: a pointer to the extracted avpl.
+ *
+ **/
+extern AVPL* extract_first_avpl(LoAL* loal) {
+ LoALnode* node;
+ AVPL* avpl;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
+#endif
+
+ node = loal->null.next;
+
+ loal->null.next->next->prev = &loal->null;
+ loal->null.next = node->next;
+
+ loal->len--;
+
+ avpl = node->avpl;
+
+ if ( avpl ) {
+ g_slice_free(any_avp_type,(any_avp_type*)node);
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
+ dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node);
+#endif
+ }
+
+ return avpl;
+}
+
+/**
+* extract_first_avpl:
+ * @param loal the loal on which to operate.
+ *
+ * Extracts the last avpl contained in a loal.
+ *
+ * Return value: a pointer to the extracted avpl.
+ *
+ **/
+extern AVPL* extract_last_avpl(LoAL* loal){
+ LoALnode* node;
+ AVPL* avpl;
+
+ node = loal->null.prev;
+
+ loal->null.prev->prev->next = &loal->null;
+ loal->null.prev = node->prev;
+
+ loal->len--;
+
+ avpl = node->avpl;
+
+ if ( avpl ) {
+ g_slice_free(any_avp_type,(any_avp_type*)node);
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node);
+#endif
+ }
+
+ return avpl;
+}
+
+/**
+ * extract_first_avpl:
+ * @param loal the loal on which to operate.
+ * @param cookie pointer to the pointer variable to contain the state between calls
+ *
+ * At each call will return the following avpl from a loal. The given cookie
+ * will be used to manatain the state between calls.
+ *
+ * Return value: a pointer to the next avpl.
+ *
+ **/
+extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
+ LoALnode* node;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%p node=%p",loal,*cookie);
+#endif
+
+ if (*cookie) {
+ node = (LoALnode*) *cookie;
+ } else {
+ node = loal->null.next;
+ }
+
+ *cookie = node->next;
+
+ return node->avpl;
+}
+
+/**
+ * delete_loal:
+ * @param loal the loal to be deleted.
+ * @param avpls_too whether avpls contained by the loal should be deleted as well
+ * @param avps_too whether avps contained by the avpls should be also deleted
+ *
+ * Destroys a loal and eventually desstroys avpls and avps.
+ *
+ **/
+extern void delete_loal(LoAL* loal, gboolean avpls_too, gboolean avps_too) {
+ AVPL* avpl;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %p",loal);
+#endif
+
+ while(( avpl = extract_last_avpl(loal) )) {
+ if (avpls_too) {
+ delete_avpl(avpl,avps_too);
+ }
+ }
+
+ scs_unsubscribe(avp_strings,loal->name);
+ g_slice_free(any_avp_type,(any_avp_type*)loal);
+}
+
+
+
+/****************************************************************************
+ ******************* the following are used in load_loal_from_file
+ ****************************************************************************/
+
+/**
+ * load_loal_error:
+ * Used by loal_from_file to handle errors while loading.
+ **/
+static LoAL* load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, const gchar* fmt, ...) {
+ va_list list;
+ gchar* desc;
+ LoAL* ret = NULL;
+ gchar* err;
+
+ va_start( list, fmt );
+ desc = ws_strdup_vprintf(fmt, list);
+ va_end( list );
+
+ if (loal) {
+ err = ws_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal->name,linenum,desc);
+ } else {
+ err = ws_strdup_printf("Error Loading LoAL at line: %i, %s",linenum,desc);
+ }
+ ret = new_loal(err);
+
+ g_free(desc);
+ g_free(err);
+
+ if (fp) fclose(fp);
+ if (loal) delete_loal(loal,TRUE,TRUE);
+ if (curr) delete_avpl(curr,TRUE);
+
+ return ret;
+}
+
+
+/* the maximum length allowed for a line */
+#define MAX_ITEM_LEN 8192
+
+/* this two ugly things are used for tokenizing */
+#define AVP_OP_CHAR '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
+
+#define AVP_NAME_CHAR 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
+case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
+case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
+case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
+case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
+case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
+case '7': case '8': case '9': case '.'
+
+
+/**
+ * loal_from_file:
+ * @param filename the file containing a loals text representation.
+ *
+ * Given a filename it will attempt to load a loal containing a copy of
+ * the avpls represented in the file.
+ *
+ * Return value: if successful a pointer to the new populated loal, else NULL.
+ *
+ **/
+extern LoAL* loal_from_file(gchar* filename) {
+ FILE *fp = NULL;
+ gchar c;
+ int i = 0;
+ guint32 linenum = 1;
+ gchar *linenum_buf;
+ gchar *name;
+ gchar *value;
+ gchar op = '?';
+ LoAL *loal_error, *loal = new_loal(filename);
+ AVPL* curr = NULL;
+ AVP* avp;
+
+ enum _load_loal_states {
+ START,
+ BEFORE_NAME,
+ IN_NAME,
+ IN_VALUE,
+ MY_IGNORE
+ } state;
+
+ linenum_buf = (gchar*)g_malloc(MAX_ITEM_LEN);
+ name = (gchar*)g_malloc(MAX_ITEM_LEN);
+ value = (gchar*)g_malloc(MAX_ITEM_LEN);
+#ifndef _WIN32
+ if (! getuid()) {
+ loal_error = load_loal_error(fp,loal,curr,linenum,"MATE Will not run as root");
+ goto error;
+ }
+#endif
+
+ state = START;
+
+ if (( fp = ws_fopen(filename,"r") )) {
+ while(( c = (gchar) fgetc(fp) )){
+
+ if ( feof(fp) ) {
+ if ( ferror(fp) ) {
+ report_read_failure(filename,errno);
+ loal_error = load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
+ goto error;
+ }
+ break;
+ }
+
+ if ( c == '\n' ) {
+ linenum++;
+ }
+
+ if ( i >= MAX_ITEM_LEN - 1 ) {
+ loal_error = load_loal_error(fp,loal,curr,linenum,"Maximum item length exceeded");
+ goto error;
+ }
+
+ switch(state) {
+ case MY_IGNORE:
+ switch (c) {
+ case '\n':
+ state = START;
+ i = 0;
+ continue;
+ default:
+ continue;
+ }
+ case START:
+ switch (c) {
+ case ' ': case '\t':
+ /* ignore whitespace at line start */
+ continue;
+ case '\n':
+ /* ignore empty lines */
+ i = 0;
+ continue;
+ case AVP_NAME_CHAR:
+ state = IN_NAME;
+ i = 0;
+ name[i++] = c;
+ name[i] = '\0';
+ snprintf(linenum_buf,MAX_ITEM_LEN,"%s:%u",filename,linenum);
+ curr = new_avpl(linenum_buf);
+ continue;
+ case '#':
+ state = MY_IGNORE;
+ continue;
+ default:
+ loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
+ goto error;
+ }
+ case BEFORE_NAME:
+ i = 0;
+ name[0] = '\0';
+ switch (c) {
+ case '\\':
+ c = (gchar) fgetc(fp);
+ if (c != '\n') ungetc(c,fp);
+ continue;
+ case ' ':
+ case '\t':
+ continue;
+ case AVP_NAME_CHAR:
+ state = IN_NAME;
+
+ name[i++] = c;
+ name[i] = '\0';
+ continue;
+ case '\n':
+ loal_append(loal,curr);
+ state = START;
+ continue;
+ default:
+ loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
+ goto error;
+ }
+ case IN_NAME:
+ switch (c) {
+ case ';':
+ state = BEFORE_NAME;
+
+ op = '?';
+ name[i] = '\0';
+ value[0] = '\0';
+ i = 0;
+
+ avp = new_avp(name,value,op);
+
+ if (! insert_avp(curr,avp) ) {
+ delete_avp(avp);
+ }
+
+ continue;
+ case AVP_OP_CHAR:
+ name[i] = '\0';
+ i = 0;
+ op = c;
+ state = IN_VALUE;
+ continue;
+ case AVP_NAME_CHAR:
+ name[i++] = c;
+ continue;
+ case '\n':
+ loal_error = load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
+ goto error;
+ default:
+ loal_error = load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
+ goto error;
+ }
+ case IN_VALUE:
+ switch (c) {
+ case '\\':
+ value[i++] = (gchar) fgetc(fp);
+ continue;
+ case ';':
+ state = BEFORE_NAME;
+
+ value[i] = '\0';
+ i = 0;
+
+ avp = new_avp(name,value,op);
+
+ if (! insert_avp(curr,avp) ) {
+ delete_avp(avp);
+ }
+ continue;
+ case '\n':
+ loal_error = load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
+ goto error;
+ default:
+ value[i++] = c;
+ continue;
+ }
+ }
+ }
+ fclose (fp);
+
+ g_free(linenum_buf);
+ g_free(name);
+ g_free(value);
+
+ return loal;
+
+ } else {
+ report_open_failure(filename,errno,FALSE);
+ loal_error = load_loal_error(NULL,loal,NULL,0,"Cannot Open file '%s'",filename);
+ }
+
+error:
+ g_free(linenum_buf);
+ g_free(name);
+ g_free(value);
+
+ return loal_error;
+}
+
+/*
+ * 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:
+ */
diff --git a/plugins/epan/mate/mate_util.h b/plugins/epan/mate/mate_util.h
new file mode 100644
index 00000000..0764ea5e
--- /dev/null
+++ b/plugins/epan/mate/mate_util.h
@@ -0,0 +1,249 @@
+/* mate_util.h
+ *
+ * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#ifndef __AVP_H_
+#define __AVP_H_
+#include "epan/proto.h"
+#include <sys/types.h>
+
+/* #define _AVP_DEBUGGING */
+
+
+/******* dbg_print *********/
+#define DEBUG_BUFFER_SIZE 4096
+extern void dbg_print(const gint* which, gint how, FILE* where,
+ const gchar* fmt, ... ) G_GNUC_PRINTF(4, 5);
+
+
+/******* single copy strings *********/
+typedef struct _scs_collection SCS_collection;
+
+#define SCS_SMALL_SIZE 16
+#define SCS_MEDIUM_SIZE 256
+#define SCS_LARGE_SIZE 4096
+#define SCS_HUGE_SIZE 65536
+
+extern gchar* scs_subscribe(SCS_collection* collection, const gchar* s);
+extern void scs_unsubscribe(SCS_collection* collection, gchar* s);
+extern gchar* scs_subscribe_printf(SCS_collection* collection, gchar* fmt, ...)
+ G_GNUC_PRINTF(2, 3);
+
+/******* AVPs & Co. *********/
+
+/* these are the defined oreators of avps */
+#define AVP_OP_EQUAL '='
+#define AVP_OP_NOTEQUAL '!'
+#define AVP_OP_STARTS '^'
+#define AVP_OP_ENDS '$'
+#define AVP_OP_CONTAINS '~'
+#define AVP_OP_LOWER '<'
+#define AVP_OP_HIGHER '>'
+#define AVP_OP_EXISTS '?'
+#define AVP_OP_ONEOFF '|'
+#define AVP_OP_TRANSF '&'
+
+
+/* an avp is an object made of a name a value and an operator */
+typedef struct _avp {
+ gchar* n;
+ gchar* v;
+ gchar o;
+} AVP;
+
+/* avp nodes are used in avp lists */
+typedef struct _avp_node {
+ AVP* avp;
+ struct _avp_node* next;
+ struct _avp_node* prev;
+} AVPN;
+
+/* an avp list is a sorted set of avps */
+typedef struct _avp_list {
+ gchar* name;
+ guint32 len;
+ AVPN null;
+} AVPL;
+
+
+
+/* an avpl transformation operation */
+typedef enum _avpl_match_mode {
+ AVPL_NO_MATCH,
+ AVPL_STRICT,
+ AVPL_LOOSE,
+ AVPL_EVERY
+} avpl_match_mode;
+
+typedef enum _avpl_replace_mode {
+ AVPL_NO_REPLACE,
+ AVPL_INSERT,
+ AVPL_REPLACE
+} avpl_replace_mode;
+
+typedef struct _avpl_transf AVPL_Transf;
+
+struct _avpl_transf {
+ gchar* name;
+
+ AVPL* match;
+ AVPL* replace;
+
+ avpl_match_mode match_mode;
+ avpl_replace_mode replace_mode;
+
+ GHashTable* map;
+ AVPL_Transf* next;
+};
+
+/* loalnodes are used in LoALs */
+typedef struct _loal_node {
+ AVPL* avpl;
+ struct _loal_node *next;
+ struct _loal_node *prev;
+} LoALnode;
+
+
+/* a loal is a list of avp lists */
+typedef struct _loal {
+ gchar* name;
+ guint len;
+ LoALnode null;
+} LoAL;
+
+
+/* avp library (re)initialization */
+extern void avp_init(void);
+
+/* If enabled set's up the debug facilities for the avp library */
+#ifdef _AVP_DEBUGGING
+extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op);
+#endif /* _AVP_DEBUGGING */
+
+/*
+ * avp constructors
+ */
+
+/* creates a new avp */
+extern AVP* new_avp(const gchar* name, const gchar* value, gchar op);
+
+/* creates a copy od an avp */
+extern AVP* avp_copy(AVP* from);
+
+/* creates an avp from a field_info record */
+extern AVP* new_avp_from_finfo(const gchar* name, field_info* finfo);
+
+/*
+ * avp destructor
+ */
+extern void delete_avp(AVP* avp);
+
+/*
+ * avp methods
+ */
+/* returns a newly allocated string containing a representation of the avp */
+#define avp_to_str(avp) (ws_strdup_printf("%s%c%s",avp->n,avp->o,avp->v))
+
+/* returns the src avp if the src avp matches(*) the op avp or NULL if it doesn't */
+extern AVP* match_avp(AVP* src, AVP* op);
+
+
+/*
+ * avplist constructors
+ */
+
+/* creates an empty avp list */
+extern AVPL* new_avpl(const gchar* name);
+
+
+/* creates a copy of an avp list */
+extern AVPL* new_avpl_from_avpl(const gchar* name, AVPL* avpl, gboolean copy_avps);
+
+extern AVPL* new_avpl_loose_match(const gchar* name, AVPL* src, AVPL* op, gboolean copy_avps);
+
+extern AVPL* new_avpl_pairs_match(const gchar* name, AVPL* src, AVPL* op, gboolean strict, gboolean copy_avps);
+
+/* uses mode to call one of the former matches. NO_MATCH = merge(merge(copy(src),op)) */
+extern AVPL* new_avpl_from_match(avpl_match_mode mode, const gchar* name,AVPL* src, AVPL* op, gboolean copy_avps);
+
+
+/*
+ * functions on avpls
+ */
+
+/* it will insert an avp to an avpl */
+extern gboolean insert_avp(AVPL* avpl, AVP* avp);
+
+/* renames an avpl */
+extern void rename_avpl(AVPL* avpl, gchar* name);
+
+/* it will add all the avps in src which don't match(*) any attribute in dest */
+extern void merge_avpl(AVPL* dest, AVPL* src, gboolean copy);
+
+/* it will return the first avp in an avpl whose name matches the given name.
+ will return NULL if there is not anyone matching */
+extern AVP* get_avp_by_name(AVPL* avpl, gchar* name, void** cookie);
+
+/* it will get the next avp from an avpl, using cookie to keep state */
+extern AVP* get_next_avp(AVPL* avpl, void** cookie);
+
+/* it will extract the first avp from an avp list */
+extern AVP* extract_first_avp(AVPL* avpl);
+
+/* it will extract the last avp from an avp list */
+extern AVP* extract_last_avp(AVPL* avpl);
+
+/* it will extract the first avp in an avpl whose name matches the given name.
+ it will not extract any and return NULL if there is not anyone matching */
+extern AVP* extract_avp_by_name(AVPL* avpl, gchar* name);
+
+/* returns a newly allocated string containing a representation of the avp list */
+extern gchar* avpl_to_str(AVPL* avpl);
+extern gchar* avpl_to_dotstr(AVPL*);
+
+/* deletes an avp list and eventually its contents */
+extern void delete_avpl(AVPL* avpl, gboolean avps_too);
+
+/*
+ * AVPL transformations
+ */
+extern void delete_avpl_transform(AVPL_Transf* it);
+extern void avpl_transform(AVPL* src, AVPL_Transf* op);
+
+
+/*
+ * Lists of AVP lists
+ */
+
+/* creates an empty list of avp lists */
+extern LoAL* new_loal(const gchar* name);
+
+/* given a file loads all the avpls contained in it
+ every line is formatted as it is the output of avplist_to_string */
+extern LoAL* loal_from_file(gchar* filename);
+
+/* inserts an avplist into a LoAL */
+extern void loal_append(LoAL* loal, AVPL* avpl);
+
+/* extracts the first avp list from the loal */
+extern AVPL* extract_first_avpl(LoAL* loal);
+
+/* extracts the last avp list from the loal */
+extern AVPL* extract_last_avpl(LoAL* loal);
+
+/* it will get the next avp list from a LoAL, using cookie to keep state */
+extern AVPL* get_next_avpl(LoAL* loal,void** cookie);
+
+/* deletes a loal and eventually its contents */
+extern void delete_loal(LoAL* loal, gboolean avpls_too, gboolean avps_too);
+
+
+#endif
diff --git a/plugins/epan/mate/matelib/dns.mate b/plugins/epan/mate/matelib/dns.mate
new file mode 100644
index 00000000..be426bc5
--- /dev/null
+++ b/plugins/epan/mate/matelib/dns.mate
@@ -0,0 +1,6 @@
+# dns.mate
+
+Action=PduDef; Name=dns_pdu; Proto=dns; Transport=udp/ip; addr=ip.addr; port=udp.port; dns_id=dns.id; dns_rsp=dns.flags.response;
+Action=GopDef; Name=dns_req; On=dns_pdu; addr; addr; port!53; dns_id;
+Action=GopStart; For=dns_req; dns_rsp=0;
+Action=GopStop; For=dns_req; dns_rsp=1;
diff --git a/plugins/epan/mate/matelib/h225_ras.mate b/plugins/epan/mate/matelib/h225_ras.mate
new file mode 100644
index 00000000..7c6698ef
--- /dev/null
+++ b/plugins/epan/mate/matelib/h225_ras.mate
@@ -0,0 +1,9 @@
+# h225_ras.mate
+
+Action=PduDef; Name=ras_pdu; Proto=h225.RasMessage; Transport=udp/ip; ras_sn=h225.requestSeqNum; ras_msg=h225.RasMessage; addr=ip.addr;
+Action=GopDef; Name=ras_leg; On=ras_pdu; addr; addr; ras_sn;
+Action=GopStart; For=ras_leg; ras_msg|0|3|6|9|12|15|18|21|26|30;
+Action=GopStop; For=ras_leg; ras_msg|1|2|4|5|7|8|10|11|13|14|16|17|19|20|22|24|27|28|29|31;
+
+Action=PduExtra; For=ras_pdu; guid=h225.guid;
+Action=GopExtra; For=ras_leg; guid;
diff --git a/plugins/epan/mate/matelib/isup.mate b/plugins/epan/mate/matelib/isup.mate
new file mode 100644
index 00000000..8ff6d309
--- /dev/null
+++ b/plugins/epan/mate/matelib/isup.mate
@@ -0,0 +1,23 @@
+# isup.mate
+
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=1; .isup_IAM=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=2; .isup_SAM=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=3; .isup_INR=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=4; .isup_INF=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=5; .isup_COT=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=6; .isup_ACM=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=7; .isup_CON=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=8; .isup_FOT=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=9; .isup_ANM=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=12; .isup_REL=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=13; .isup_SUS=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=14; .isup_RES=;
+#Action=Transform; Name=isup_msg_type; Mode=Insert; Match=Strict; isup_msg=16; .isup_RLC=;
+
+Action=PduDef; Name=isup_pdu; Proto=isup; Transport=mtp3; mtp3pc=mtp3.dpc; mtp3pc=mtp3.opc; cic=isup.cic; isup_msg=isup.message_type;
+#Action=PduTransform; For=isup_pdu; Name=isup_msg_type;
+
+Action=GopDef; Name=isup_leg; On=isup_pdu; ShowPduTree=TRUE; mtp3pc; mtp3pc; cic;
+Action=GopStart; For=isup_leg; isup_msg=1;
+Action=GopStop; For=isup_leg; isup_msg=16;
+
diff --git a/plugins/epan/mate/matelib/megaco.mate b/plugins/epan/mate/matelib/megaco.mate
new file mode 100644
index 00000000..044aba78
--- /dev/null
+++ b/plugins/epan/mate/matelib/megaco.mate
@@ -0,0 +1,8 @@
+# megaco.mate
+
+Action=PduDef; Name=mgc_pdu; Proto=megaco; Transport=ip; addr=ip.addr; megaco_ctx=megaco.context; megaco_trx=megaco.transid; megaco_msg=megaco.transaction; term=megaco.termid;
+
+Action=GopDef; Name=mgc_tr; On=mgc_pdu; addr; addr; megaco_trx;
+Action=GopStart; For=mgc_tr; megaco_msg|Request|Notify;
+Action=GopStop; For=mgc_tr; megaco_msg=Reply;
+Action=GopExtra; For=mgc_tr; term^DS1; megaco_ctx!Choose one;
diff --git a/plugins/epan/mate/matelib/q931.mate b/plugins/epan/mate/matelib/q931.mate
new file mode 100644
index 00000000..5bea5b1a
--- /dev/null
+++ b/plugins/epan/mate/matelib/q931.mate
@@ -0,0 +1,6 @@
+# q931.thing
+
+Action=PduDef; Name=q931_pdu; Proto=q931; Stop=TRUE; Transport=tcp/ip; addr=ip.addr; call_ref=q931.call_ref; q931_msg=q931.message_type;
+Action=GopDef; Name=q931_leg; On=q931_pdu; addr; addr; call_ref;
+Action=GopStart; For=q931_leg; q931_msg=5;
+Action=GopStop; For=q931_leg; q931_msg=90;
diff --git a/plugins/epan/mate/matelib/radius.mate b/plugins/epan/mate/matelib/radius.mate
new file mode 100644
index 00000000..66a910b8
--- /dev/null
+++ b/plugins/epan/mate/matelib/radius.mate
@@ -0,0 +1,12 @@
+# radius.mate
+
+Action=Transform; Name=radius_same_port; Mode=Insert; Match=Strict; radius_port; radius_port;
+Action=Transform; Name=radius_same_port; Mode=Insert; Match=Every; radius_port; .radius_port=0;
+
+Action=PduDef; Name=radius_pdu; Proto=radius; Transport=udp/ip; radius_addr=ip.addr; radius_port=udp.port; radius_id=radius.id; radius_code=radius.code;
+Action=PduTransform; For=radius_pdu; Name=radius_same_port;
+
+Action=GopDef; Name=radius_req; On=radius_pdu; radius_id; radius_addr; radius_addr; radius_port; radius_port;
+Action=GopStart; For=radius_req; radius_code|1|4|7;
+Action=GopStop; For=radius_req; radius_code|2|3|5|8|9;
+
diff --git a/plugins/epan/mate/matelib/rtsp.mate b/plugins/epan/mate/matelib/rtsp.mate
new file mode 100644
index 00000000..35f25ab2
--- /dev/null
+++ b/plugins/epan/mate/matelib/rtsp.mate
@@ -0,0 +1,10 @@
+# rtsp.mate
+
+Action=PduDef; Name=rtsp_pdu; Proto=rtsp; Transport=tcp/ip; addr=ip.addr; port=tcp.port; rtsp_method=rtsp.method;
+Action=PduExtra; For=rtsp_pdu; rtsp_ses=rtsp.session; rtsp_url=rtsp.url;
+
+Action=GopDef; Name=rtsp_ses; On=rtsp_pdu; addr; addr; port; port;
+Action=GopStart; For=rtsp_ses; rtsp_method=DESCRIBE;
+Action=GopStop; For=rtsp_ses; rtsp_method=TEARDOWN;
+Action=GopExtra; For=rtsp_ses; rtsp_ses; rtsp_url;
+
diff --git a/plugins/epan/mate/matelib/sip.mate b/plugins/epan/mate/matelib/sip.mate
new file mode 100644
index 00000000..593c915b
--- /dev/null
+++ b/plugins/epan/mate/matelib/sip.mate
@@ -0,0 +1,12 @@
+# sip.mate
+
+Action=PduDef; Name=sip_pdu; Proto=sip; Transport=tcp/ip; addr=ip.addr; port=tcp.port; sip_method=sip.Method; sip_callid=sip.Call-ID; calling=sdp.owner.username;
+Action=GopDef; Name=sip_leg; On=sip_pdu; addr; addr; port; port;
+Action=GopStart; For=sip_leg; sip_method=INVITE;
+Action=GopStop; For=sip_leg; sip_method=BYE;
+
+Action=PduDef; Name=sip_trunk_pdu; Proto=sip; Transport=udp/ip; addr=ip.addr; port=udp.port; sip_method=sip.Method; sip_callid=sip.Call-ID; calling=sdp.owner.username;
+Action=GopDef; Name=sip_trunk_leg; On=sip_trunk_pdu; addr; addr; sip_callid;
+Action=GopStart; For=sip_trunk_leg; sip_method=INVITE;
+Action=GopStop; For=sip_trunk_leg; sip_method=BYE;
+
diff --git a/plugins/epan/mate/packet-mate.c b/plugins/epan/mate/packet-mate.c
new file mode 100644
index 00000000..97774cd1
--- /dev/null
+++ b/plugins/epan/mate/packet-mate.c
@@ -0,0 +1,453 @@
+/* packet-mate.c
+ * Routines for the mate Facility's Pseudo-Protocol dissection
+ *
+ * Copyright 2004, Luis E. Garcia Ontanon <gopo@webflies.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+/**************************************************************************
+ * This is the pseudo protocol dissector for the mate module. ***
+ * It is intended for this to be just the user interface to the module. ***
+ **************************************************************************/
+
+#include "config.h"
+
+#include "mate.h"
+#include <epan/expert.h>
+
+void proto_register_mate(void);
+void proto_reg_handoff_mate(void);
+
+static mate_config* mc = NULL;
+
+static int proto_mate = -1;
+
+static int hf_mate_released_time = -1;
+static int hf_mate_duration = -1;
+static int hf_mate_number_of_pdus = -1;
+static int hf_mate_started_at = -1;
+static int hf_mate_gop_key = -1;
+
+static expert_field ei_mate_undefined_attribute = EI_INIT;
+
+static const gchar* pref_mate_config_filename = "";
+static const gchar* current_mate_config_filename = NULL;
+
+#ifdef _AVP_DEBUGGING
+static int pref_avp_debug_general = 0;
+static int pref_avp_debug_avp = 0;
+static int pref_avp_debug_avp_op = 0;
+static int pref_avp_debug_avpl = 0;
+static int pref_avp_debug_avpl_op = 0;
+#endif
+
+static dissector_handle_t mate_handle;
+
+static void
+pdu_attrs_tree(proto_tree* tree, packet_info *pinfo, tvbuff_t *tvb, mate_pdu* pdu)
+{
+ AVPN* c;
+ proto_tree *avpl_t;
+ int* hfi_p;
+
+ avpl_t = proto_tree_add_subtree_format(tree,tvb,0,0,pdu->cfg->ett_attr,NULL,"%s Attributes",pdu->cfg->name);
+
+ for ( c = pdu->avpl->null.next; c->avp; c = c->next) {
+ hfi_p = (int *)g_hash_table_lookup(pdu->cfg->my_hfids,(char*)c->avp->n);
+
+ if (hfi_p) {
+ proto_tree_add_string(avpl_t,*hfi_p,tvb,0,0,c->avp->v);
+ } else {
+ proto_tree_add_expert_format(avpl_t,pinfo,&ei_mate_undefined_attribute,tvb,0,0,"Undefined attribute: %s=%s",c->avp->n, c->avp->v);
+ }
+ }
+}
+
+static void
+gop_attrs_tree(proto_tree* tree, packet_info *pinfo, tvbuff_t *tvb, mate_gop* gop)
+{
+ AVPN* c;
+ proto_tree *avpl_t;
+ int* hfi_p;
+
+ avpl_t = proto_tree_add_subtree_format(tree,tvb,0,0,gop->cfg->ett_attr,NULL,"%s Attributes",gop->cfg->name);
+
+ for ( c = gop->avpl->null.next; c->avp; c = c->next) {
+ hfi_p = (int *)g_hash_table_lookup(gop->cfg->my_hfids,(char*)c->avp->n);
+
+ if (hfi_p) {
+ proto_tree_add_string(avpl_t,*hfi_p,tvb,0,0,c->avp->v);
+ } else {
+ proto_tree_add_expert_format(avpl_t,pinfo,&ei_mate_undefined_attribute,tvb,0,0,"Undefined attribute: %s=%s",c->avp->n, c->avp->v);
+ }
+ }
+}
+
+static void
+gog_attrs_tree(proto_tree* tree, packet_info *pinfo, tvbuff_t *tvb, mate_gog* gog)
+{
+ AVPN* c;
+ proto_tree *avpl_t;
+ int* hfi_p;
+
+ avpl_t = proto_tree_add_subtree_format(tree,tvb,0,0,gog->cfg->ett_attr,NULL,"%s Attributes",gog->cfg->name);
+
+ for ( c = gog->avpl->null.next; c->avp; c = c->next) {
+ hfi_p = (int *)g_hash_table_lookup(gog->cfg->my_hfids,(char*)c->avp->n);
+
+ if (hfi_p) {
+ proto_tree_add_string(avpl_t,*hfi_p,tvb,0,0,c->avp->v);
+ } else {
+ proto_tree_add_expert_format(avpl_t,pinfo,&ei_mate_undefined_attribute,tvb,0,0,"Undefined attribute: %s=%s",c->avp->n, c->avp->v);
+ }
+ }
+}
+
+static void mate_gop_tree(proto_tree* pdu_tree, packet_info *pinfo, tvbuff_t *tvb, mate_gop* gop);
+
+static void
+mate_gog_tree(proto_tree* tree, packet_info *pinfo, tvbuff_t *tvb, mate_gog* gog, mate_gop* gop)
+{
+ proto_item *gog_item;
+ proto_tree *gog_tree;
+ proto_tree *gog_time_tree;
+ proto_item *gog_gops_item;
+ proto_tree *gog_gops_tree;
+ mate_gop* gog_gops;
+ proto_item *gog_gop_item;
+ proto_tree *gog_gop_tree;
+ mate_pdu* pdu;
+
+ gog_item = proto_tree_add_uint(tree,gog->cfg->hfid,tvb,0,0,gog->id);
+ gog_tree = proto_item_add_subtree(gog_item,gog->cfg->ett);
+
+ gog_attrs_tree(gog_tree,pinfo,tvb,gog);
+
+ if (gog->cfg->show_times) {
+ gog_time_tree = proto_tree_add_subtree_format(gog_tree,tvb,0,0,gog->cfg->ett_times,NULL,"%s Times",gog->cfg->name);
+
+ proto_tree_add_float(gog_time_tree, gog->cfg->hfid_start_time, tvb, 0, 0, gog->start_time);
+ proto_tree_add_float(gog_time_tree, gog->cfg->hfid_last_time, tvb, 0, 0, gog->last_time - gog->start_time);
+ }
+
+ gog_gops_item = proto_tree_add_uint(gog_tree, gog->cfg->hfid_gog_num_of_gops, tvb, 0, 0, gog->num_of_gops);
+
+ gog_gops_tree = proto_item_add_subtree(gog_gops_item, gog->cfg->ett_children);
+
+ for (gog_gops = gog->gops; gog_gops; gog_gops = gog_gops->next) {
+
+ if (gop != gog_gops) {
+ if (gog->cfg->gop_tree_mode == GOP_FULL_TREE) {
+ mate_gop_tree(gog_gops_tree, pinfo, tvb, gog_gops);
+ } else {
+ gog_gop_item = proto_tree_add_uint(gog_gops_tree,gog_gops->cfg->hfid,tvb,0,0,gog_gops->id);
+
+ if (gog->cfg->gop_tree_mode == GOP_BASIC_TREE) {
+ gog_gop_tree = proto_item_add_subtree(gog_gop_item, gog->cfg->ett_gog_gop);
+
+ proto_tree_add_float(gog_gop_tree, hf_mate_started_at, tvb,0,0,gog_gops->start_time);
+
+ proto_tree_add_float_format(gog_gop_tree, hf_mate_duration, tvb,0,0, gog_gops->last_time - gog_gops->start_time,
+ "%s Duration: %f", gog_gops->cfg->name, gog_gops->last_time - gog_gops->start_time);
+
+ if (gog_gops->released)
+ proto_tree_add_float_format(gog_gop_tree, hf_mate_released_time, tvb,0,0, gog_gops->release_time - gog_gops->start_time,
+ "%s has been released, Time: %f", gog_gops->cfg->name, gog_gops->release_time - gog_gops->start_time);
+
+ proto_tree_add_uint(gog_gop_tree, hf_mate_number_of_pdus, tvb,0,0, gog_gops->num_of_pdus);
+
+ if (gop->pdus && gop->cfg->pdu_tree_mode != GOP_NO_TREE) {
+ proto_tree_add_uint(gog_gop_tree,gog->cfg->hfid_gog_gopstart,tvb,0,0,gog_gops->pdus->frame);
+
+ for (pdu = gog_gops->pdus->next ; pdu; pdu = pdu->next) {
+ if (pdu->is_stop) {
+ proto_tree_add_uint(gog_gop_tree,gog->cfg->hfid_gog_gopstop,tvb,0,0,pdu->frame);
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ } else {
+ proto_tree_add_uint_format(gog_gops_tree,gop->cfg->hfid,tvb,0,0,gop->id,"current %s Gop: %d",gop->cfg->name,gop->id);
+ }
+ }
+}
+
+static void
+mate_gop_tree(proto_tree* tree, packet_info *pinfo, tvbuff_t *tvb, mate_gop* gop)
+{
+ proto_item *gop_item;
+ proto_tree *gop_time_tree;
+ proto_tree *gop_tree;
+ proto_item *gop_pdu_item;
+ proto_tree *gop_pdu_tree;
+ mate_pdu* gop_pdus;
+ float rel_time;
+ float pdu_rel_time;
+ const gchar* pdu_str;
+ const gchar* type_str;
+ guint32 pdu_item;
+
+ gop_item = proto_tree_add_uint(tree,gop->cfg->hfid,tvb,0,0,gop->id);
+ gop_tree = proto_item_add_subtree(gop_item, gop->cfg->ett);
+
+ if (gop->gop_key) proto_tree_add_string(gop_tree,hf_mate_gop_key,tvb,0,0,gop->gop_key);
+
+ gop_attrs_tree(gop_tree,pinfo,tvb,gop);
+
+ if (gop->cfg->show_times) {
+ gop_time_tree = proto_tree_add_subtree_format(gop_tree,tvb,0,0,gop->cfg->ett_times,NULL,"%s Times",gop->cfg->name);
+
+ proto_tree_add_float(gop_time_tree, gop->cfg->hfid_start_time, tvb, 0, 0, gop->start_time);
+
+ if (gop->released) {
+ proto_tree_add_float(gop_time_tree, gop->cfg->hfid_stop_time, tvb, 0, 0, gop->release_time - gop->start_time);
+ proto_tree_add_float(gop_time_tree, gop->cfg->hfid_last_time, tvb, 0, 0, gop->last_time - gop->start_time);
+ } else {
+ proto_tree_add_float(gop_time_tree, gop->cfg->hfid_last_time, tvb, 0, 0, gop->last_time - gop->start_time);
+ }
+ }
+
+ gop_pdu_item = proto_tree_add_uint(gop_tree, gop->cfg->hfid_gop_num_pdus, tvb, 0, 0,gop->num_of_pdus);
+
+ if (gop->cfg->pdu_tree_mode != GOP_NO_TREE) {
+
+ gop_pdu_tree = proto_item_add_subtree(gop_pdu_item, gop->cfg->ett_children);
+
+ rel_time = gop->start_time;
+
+ type_str = (gop->cfg->pdu_tree_mode == GOP_FRAME_TREE ) ? "in frame:" : "id:";
+
+ for (gop_pdus = gop->pdus; gop_pdus; gop_pdus = gop_pdus->next) {
+
+ pdu_item = (gop->cfg->pdu_tree_mode == GOP_FRAME_TREE ) ? gop_pdus->frame : gop_pdus->id;
+
+ if (gop_pdus->is_start) {
+ pdu_str = "Start ";
+ } else if (gop_pdus->is_stop) {
+ pdu_str = "Stop ";
+ } else if (gop_pdus->after_release) {
+ pdu_str = "After stop ";
+ } else {
+ pdu_str = "";
+ }
+
+ pdu_rel_time = gop_pdus->time_in_gop != 0.0 ? gop_pdus->time_in_gop - rel_time : (float) 0.0;
+
+ proto_tree_add_uint_format(gop_pdu_tree,gop->cfg->hfid_gop_pdu,tvb,0,0,pdu_item,
+ "%sPDU: %s %i (%f : %f)",pdu_str, type_str,
+ pdu_item, gop_pdus->time_in_gop,
+ pdu_rel_time);
+
+ rel_time = gop_pdus->time_in_gop;
+
+ }
+ }
+}
+
+
+static void
+mate_pdu_tree(mate_pdu *pdu, packet_info *pinfo, tvbuff_t *tvb, proto_item *item, proto_tree* tree)
+{
+ proto_item *pdu_item;
+ proto_tree *pdu_tree;
+
+ if ( ! pdu ) return;
+
+ if (pdu->gop && pdu->gop->gog) {
+ proto_item_append_text(item," %s:%d->%s:%d->%s:%d",
+ pdu->cfg->name,pdu->id,
+ pdu->gop->cfg->name,pdu->gop->id,
+ pdu->gop->gog->cfg->name,pdu->gop->gog->id);
+ } else if (pdu->gop) {
+ proto_item_append_text(item," %s:%d->%s:%d",
+ pdu->cfg->name,pdu->id,
+ pdu->gop->cfg->name,pdu->gop->id);
+ } else {
+ proto_item_append_text(item," %s:%d",pdu->cfg->name,pdu->id);
+ }
+
+ pdu_item = proto_tree_add_uint(tree,pdu->cfg->hfid,tvb,0,0,pdu->id);
+ pdu_tree = proto_item_add_subtree(pdu_item, pdu->cfg->ett);
+ proto_tree_add_float(pdu_tree,pdu->cfg->hfid_pdu_rel_time, tvb, 0, 0, pdu->rel_time);
+
+ if (pdu->gop) {
+ proto_tree_add_float(pdu_tree,pdu->cfg->hfid_pdu_time_in_gop, tvb, 0, 0, pdu->time_in_gop);
+ mate_gop_tree(tree,pinfo,tvb,pdu->gop);
+
+ if (pdu->gop->gog)
+ mate_gog_tree(tree,pinfo,tvb,pdu->gop->gog,pdu->gop);
+ }
+
+ if (pdu->avpl) {
+ pdu_attrs_tree(pdu_tree,pinfo,tvb,pdu);
+ }
+}
+
+static int
+mate_tree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+ mate_pdu* pdus;
+ proto_item *mate_i;
+ proto_tree *mate_t;
+
+ /* If there is no MATE configuration, don't claim the packet */
+ if ( mc == NULL)
+ return 0;
+
+ /* There is a MATE configuration, just no tree, so there's nothing to do */
+ if ( tree == NULL)
+ return tvb_captured_length(tvb);
+
+ mate_analyze_frame(mc, pinfo,tree);
+
+ if (( pdus = mate_get_pdus(pinfo->num) )) {
+ for ( ; pdus; pdus = pdus->next_in_frame) {
+ mate_i = proto_tree_add_protocol_format(tree,mc->hfid_mate,tvb,0,0,"MATE");
+ mate_t = proto_item_add_subtree(mate_i, mc->ett_root);
+ mate_pdu_tree(pdus,pinfo,tvb,mate_i,mate_t);
+ }
+ }
+ return tvb_captured_length(tvb);
+}
+
+static void
+initialize_mate(void)
+{
+ initialize_mate_runtime(mc);
+#ifdef _AVP_DEBUGGING
+ setup_avp_debug(mc->dbg_facility,
+ &pref_avp_debug_general,
+ &pref_avp_debug_avp,
+ &pref_avp_debug_avp_op,
+ &pref_avp_debug_avpl,
+ &pref_avp_debug_avpl_op);
+#endif
+}
+
+static void
+flush_mate_debug(void)
+{
+ /* Flush debug information */
+ if (mc->dbg_facility)
+ fflush(mc->dbg_facility);
+}
+
+extern
+void
+proto_reg_handoff_mate(void)
+{
+ if ( *pref_mate_config_filename != '\0' ) {
+
+ if (current_mate_config_filename) {
+ report_failure("MATE cannot reconfigure itself.\n"
+ "For changes to be applied you have to restart Wireshark\n");
+ return;
+ }
+
+ if (!mc) {
+ mc = mate_make_config(pref_mate_config_filename,proto_mate);
+
+ if (mc) {
+ /* XXX: alignment warnings, what do they mean? */
+ proto_register_field_array(proto_mate, (hf_register_info*)(void *)mc->hfrs->data, mc->hfrs->len );
+ proto_register_subtree_array((gint**)(void*)mc->ett->data, mc->ett->len);
+ register_init_routine(initialize_mate);
+ register_postseq_cleanup_routine(flush_mate_debug);
+
+ /*
+ * Set the list of hfids we want.
+ */
+ set_postdissector_wanted_hfids(mate_handle,
+ mc->wanted_hfids);
+
+ initialize_mate_runtime(mc);
+ }
+
+ current_mate_config_filename = pref_mate_config_filename;
+
+ }
+ }
+}
+
+extern
+void
+proto_register_mate(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_mate_started_at, { "Started at", "mate.started_at", FT_FLOAT, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+ { &hf_mate_duration, { "Duration", "mate.duration", FT_FLOAT, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+ { &hf_mate_released_time, { "Release time", "mate.released_time", FT_FLOAT, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+ { &hf_mate_number_of_pdus, { "Number of Pdus", "mate.number_of_pdus", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
+ { &hf_mate_gop_key, { "GOP Key", "mate.gop_key", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+ };
+
+ static ei_register_info ei[] = {
+ { &ei_mate_undefined_attribute, { "mate.undefined_attribute", PI_PROTOCOL, PI_ERROR, "Undefined attribute", EXPFILL }},
+ };
+
+ expert_module_t* expert_mate;
+ module_t *mate_module;
+
+ proto_mate = proto_register_protocol("Meta Analysis Tracing Engine", "MATE", "mate");
+ proto_register_field_array(proto_mate, hf, array_length(hf));
+ expert_mate = expert_register_protocol(proto_mate);
+ expert_register_field_array(expert_mate, ei, array_length(ei));
+
+ mate_handle = register_dissector("mate",mate_tree,proto_mate);
+ mate_module = prefs_register_protocol(proto_mate, proto_reg_handoff_mate);
+ prefs_register_filename_preference(mate_module, "config",
+ "Configuration Filename",
+ "The name of the file containing the mate module's configuration",
+ &pref_mate_config_filename, FALSE);
+#ifdef _AVP_DEBUGGING
+ prefs_register_uint_preference(mate_module, "avp_debug_general",
+ "AVP Debug general",
+ "General debugging level (0..5)",
+ 10,
+ &pref_avp_debug_general);
+ prefs_register_uint_preference(mate_module, "avp_debug_avp",
+ "Debug AVP",
+ "Attribute Value Pairs debugging level (0..5)",
+ 10,
+ &pref_avp_debug_avp);
+ prefs_register_uint_preference(mate_module, "avp_debug_avp_op",
+ "Debug AVP operations",
+ "Attribute Value Pairs operations debugging level (0..5)",
+ 10,
+ &pref_avp_debug_avp_op);
+ prefs_register_uint_preference(mate_module, "avp_debug_avpl",
+ "Debug AVP list",
+ "Attribute Value Pairs list debugging level (0..5)",
+ 10,
+ &pref_avp_debug_avpl);
+ prefs_register_uint_preference(mate_module, "avp_debug_avpl_op",
+ "Debug AVP list operations",
+ "Attribute Value Pairs list operations debugging level (0..5)",
+ 10,
+ &pref_avp_debug_avpl_op);
+#endif
+
+ register_postdissector(mate_handle);
+}
+
+/*
+ * 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:
+ */