QUIC: fix null-ptr dereference in gQUIC version check
[metze/wireshark/wip.git] / ui / tap-rtp-common.c
index 2643ee343d0f727e248da11b975d11e34936a7d3..d5a991fd14a4da8c7f7d046aff55ee10eecf54b9 100644 (file)
  * 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.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
 
-#include <glib.h>
-
+#include <stdlib.h>
+#include <string.h>
 #include <math.h>
-#include "globals.h"
 
-#include <string.h>
+#include <glib.h>
+
 #include <epan/rtp_pt.h>
 #include <epan/addr_resolv.h>
+#include <epan/proto_data.h>
 #include <epan/dissectors/packet-rtp.h>
+#include <wsutil/pint.h>
 #include "rtp_stream.h"
 #include "tap-rtp-common.h"
 
 /****************************************************************************/
 /* Type for storing and writing rtpdump information */
 typedef struct st_rtpdump_info {
-    double rec_time;        /**< milliseconds since start of recording */
+    double rec_time;       /**< milliseconds since start of recording */
     guint16 num_samples;   /**< number of bytes in *frame */
-    const guint8 *samples;                 /**< data bytes */
+    const guint8 *samples; /**< data bytes */
 } rtpdump_info_t;
 
 /****************************************************************************/
-/* GCompareFunc style comparison function for _rtp_stream_info */
-static gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
+/* init rtpstream_info_t structure */
+void rtpstream_info_init(rtpstream_info_t *info)
+{
+    memset(info, 0, sizeof(rtpstream_info_t));
+}
+
+/****************************************************************************/
+/* malloc and init rtpstream_info_t structure */
+rtpstream_info_t *rtpstream_info_malloc_and_init(void)
+{
+    rtpstream_info_t *dest;
+
+    dest = (rtpstream_info_t *)g_malloc(sizeof(rtpstream_info_t));
+    rtpstream_info_init(dest);
+
+    return dest;
+}
+
+/****************************************************************************/
+/* deep copy of rtpstream_info_t */
+void rtpstream_info_copy_deep(rtpstream_info_t *dest, const rtpstream_info_t *src)
+{
+    /* Deep clone of contents */
+    *dest = *src;  /* memberwise copy of struct */
+    copy_address(&(dest->id.src_addr), &(src->id.src_addr));
+    copy_address(&(dest->id.dst_addr), &(src->id.dst_addr));
+    dest->all_payload_type_names = g_strdup(src->all_payload_type_names);
+}
+
+/****************************************************************************/
+/* malloc and deep copy rtpstream_info_t structure */
+rtpstream_info_t *rtpstream_info_malloc_and_copy_deep(const rtpstream_info_t *src)
 {
-       const struct _rtp_stream_info* a = (const struct _rtp_stream_info*)aa;
-       const struct _rtp_stream_info* b = (const struct _rtp_stream_info*)bb;
-
-       if (a==b)
-               return 0;
-       if (a==NULL || b==NULL)
-               return 1;
-       if (addresses_equal(&(a->src_addr), &(b->src_addr))
-               && (a->src_port == b->src_port)
-               && addresses_equal(&(a->dest_addr), &(b->dest_addr))
-               && (a->dest_port == b->dest_port)
-               && (a->ssrc == b->ssrc))
-               return 0;
-       else
-               return 1;
+    rtpstream_info_t *dest;
+
+    dest = (rtpstream_info_t *)g_malloc(sizeof(rtpstream_info_t));
+    rtpstream_info_copy_deep(dest, src);
+
+    return dest;
 }
 
+/****************************************************************************/
+/* free rtpstream_info_t referenced values */
+void rtpstream_info_free_data(rtpstream_info_t *info)
+{
+    if (info->all_payload_type_names != NULL) {
+        g_free(info->all_payload_type_names);
+    }
+
+    rtpstream_id_free(&info->id);
+}
+
+/****************************************************************************/
+/* free rtpstream_info_t referenced values and whole structure */
+void rtpstream_info_free_all(rtpstream_info_t *info)
+{
+    rtpstream_info_free_data(info);
+    g_free(info);
+}
+
+/****************************************************************************/
+/* GCompareFunc style comparison function for rtpstream_info_t */
+gint rtpstream_info_cmp(gconstpointer aa, gconstpointer bb)
+{
+    const rtpstream_info_t *a = (const rtpstream_info_t *)aa;
+    const rtpstream_info_t *b = (const rtpstream_info_t *)bb;
+
+    if (a==b)
+        return 0;
+    if (a==NULL || b==NULL)
+        return 1;
+    if (rtpstream_id_equal(&(a->id),&(b->id),RTPSTREAM_ID_EQUAL_SSRC))
+        return 0;
+    else
+        return 1;
+}
+
+/****************************************************************************/
+/* compare the endpoints of two RTP streams */
+gboolean rtpstream_info_is_reverse(const rtpstream_info_t *stream_a, rtpstream_info_t *stream_b)
+{
+    if (stream_a == NULL || stream_b == NULL)
+        return FALSE;
+
+    if ((addresses_equal(&(stream_a->id.src_addr), &(stream_b->id.dst_addr)))
+        && (stream_a->id.src_port == stream_b->id.dst_port)
+        && (addresses_equal(&(stream_a->id.dst_addr), &(stream_b->id.src_addr)))
+        && (stream_a->id.dst_port == stream_b->id.src_port))
+        return TRUE;
+    else
+        return FALSE;
+}
 
 /****************************************************************************/
 /* when there is a [re]reading of packet's */
 void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
 {
-       GList* list;
-
-       if (tapinfo->mode == TAP_ANALYSE) {
-               /* free the data items first */
-               list = g_list_first(tapinfo->strinfo_list);
-               while (list)
-               {
-                       g_free(list->data);
-                       list = g_list_next(list);
-               }
-               g_list_free(tapinfo->strinfo_list);
-               tapinfo->strinfo_list = NULL;
-               tapinfo->nstreams = 0;
-               tapinfo->npackets = 0;
-       }
-
-       return;
+    GList* list;
+    rtpstream_info_t *stream_info;
+
+    if (tapinfo->mode == TAP_ANALYSE) {
+        /* free the data items first */
+        list = g_list_first(tapinfo->strinfo_list);
+        while (list)
+        {
+            stream_info = (rtpstream_info_t *)(list->data);
+            rtpstream_info_free_data(stream_info);
+            g_free(list->data);
+            list = g_list_next(list);
+        }
+        g_list_free(tapinfo->strinfo_list);
+        tapinfo->strinfo_list = NULL;
+        tapinfo->nstreams = 0;
+        tapinfo->npackets = 0;
+    }
+
+    return;
 }
 
 void rtpstream_reset_cb(void *arg)
 {
-       rtpstream_tapinfo_t *ti =(rtpstream_tapinfo_t *)arg;
-       if (ti->tap_reset) {
-               /* Give listeners a chance to cleanup references. */
-               ti->tap_reset(ti);
-       }
-       rtpstream_reset(ti);
+    rtpstream_tapinfo_t *ti =(rtpstream_tapinfo_t *)arg;
+    if (ti->tap_reset) {
+        /* Give listeners a chance to cleanup references. */
+        ti->tap_reset(ti);
+    }
+    rtpstream_reset(ti);
+}
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+
+/****************************************************************************/
+/* redraw the output */
+static void rtpstream_draw_cb(void *ti_ptr)
+{
+    rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)ti_ptr;
+/* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
+    g_signal_emit_by_name(top_level, "signal_rtpstream_update");
+*/
+    if (tapinfo && tapinfo->tap_draw) {
+        /* RTP_STREAM_DEBUG("streams: %d packets: %d", tapinfo->nstreams, tapinfo->npackets); */
+        tapinfo->tap_draw(tapinfo);
+    }
+    return;
+}
+
+
+
+/****************************************************************************/
+void
+remove_tap_listener_rtpstream(rtpstream_tapinfo_t *tapinfo)
+{
+    if (tapinfo && tapinfo->is_registered) {
+        remove_tap_listener(tapinfo);
+        tapinfo->is_registered = FALSE;
+    }
+}
+
+/****************************************************************************/
+void
+register_tap_listener_rtpstream(rtpstream_tapinfo_t *tapinfo, const char *fstring, rtpstream_tap_error_cb tap_error)
+{
+    GString *error_string;
+
+    if (!tapinfo) {
+        return;
+    }
+
+    if (!tapinfo->is_registered) {
+        error_string = register_tap_listener("rtp", tapinfo,
+            fstring, 0, rtpstream_reset_cb, rtpstream_packet_cb,
+            rtpstream_draw_cb, NULL);
+
+        if (error_string != NULL) {
+            if (tap_error) {
+                tap_error(error_string);
+            }
+            g_string_free(error_string, TRUE);
+            exit(1);
+        }
+
+        tapinfo->is_registered = TRUE;
+    }
 }
 
 /*
@@ -125,6 +248,43 @@ void rtpstream_reset_cb(void *arg)
 * the RTP/RTCP header and (optionally) the actual payload.
 */
 
+static const gchar *PAYLOAD_UNKNOWN_STR = "Unknown";
+
+static void update_payload_names(rtpstream_info_t *stream_info, const struct _rtp_info *rtpinfo)
+{
+    GString *payload_type_names;
+    const gchar *new_payload_type_str;
+
+    /* Ensure that we have non empty payload_type_str */
+    if (rtpinfo->info_payload_type_str != NULL) {
+        new_payload_type_str = rtpinfo->info_payload_type_str;
+    }
+    else {
+        /* String is created from const strings only */
+        new_payload_type_str = val_to_str_ext_const(rtpinfo->info_payload_type,
+            &rtp_payload_type_short_vals_ext,
+            PAYLOAD_UNKNOWN_STR
+        );
+    }
+    stream_info->payload_type_names[rtpinfo->info_payload_type] = new_payload_type_str;
+
+    /* Join all existing payload names to one string */
+    payload_type_names = g_string_sized_new(40); /* Preallocate memory */
+    for(int i=0; i<256; i++) {
+        if (stream_info->payload_type_names[i] != NULL) {
+            if (payload_type_names->len > 0) {
+                g_string_append(payload_type_names, ", ");
+            }
+            g_string_append(payload_type_names, stream_info->payload_type_names[i]);
+        }
+    }
+    if (stream_info->all_payload_type_names != NULL) {
+        g_free(stream_info->all_payload_type_names);
+    }
+    stream_info->all_payload_type_names = payload_type_names->str;
+    g_string_free(payload_type_names, FALSE);
+}
+
 #define RTPFILE_VERSION "1.0"
 
 /*
@@ -132,583 +292,247 @@ void rtpstream_reset_cb(void *arg)
 * The header consists of an identifying string, followed
 * by a binary structure.
 */
-void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file)
+void rtp_write_header(rtpstream_info_t *strinfo, FILE *file)
 {
-       guint32 start_sec;     /* start of recording (GMT) (seconds) */
-       guint32 start_usec;    /* start of recording (GMT) (microseconds)*/
-       guint32 source;        /* network source (multicast address) */
-       size_t sourcelen;
-       guint16 port;          /* UDP port */
-       guint16 padding;       /* 2 padding bytes */
-       char* addr_str = (char*)address_to_display(NULL, &(strinfo->dest_addr));
-
-       fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
-               addr_str,
-               strinfo->dest_port);
-       wmem_free(NULL, addr_str);
-
-       start_sec = g_htonl(strinfo->start_fd->abs_ts.secs);
-       start_usec = g_htonl(strinfo->start_fd->abs_ts.nsecs / 1000000);
-       /* rtpdump only accepts guint32 as source, will be fake for IPv6 */
-       memset(&source, 0, sizeof source);
-       sourcelen = strinfo->src_addr.len;
-       if (sourcelen > sizeof source)
-               sourcelen = sizeof source;
-       memcpy(&source, strinfo->src_addr.data, sourcelen);
-       port = g_htons(strinfo->src_port);
-       padding = 0;
-
-       if (fwrite(&start_sec, 4, 1, file) == 0)
-               return;
-       if (fwrite(&start_usec, 4, 1, file) == 0)
-               return;
-       if (fwrite(&source, 4, 1, file) == 0)
-               return;
-       if (fwrite(&port, 2, 1, file) == 0)
-               return;
-       if (fwrite(&padding, 2, 1, file) == 0)
-               return;
+    guint32 start_sec;     /* start of recording (GMT) (seconds) */
+    guint32 start_usec;    /* start of recording (GMT) (microseconds)*/
+    guint32 source;        /* network source (multicast address) */
+    size_t sourcelen;
+    guint16 port;          /* UDP port */
+    guint16 padding;       /* 2 padding bytes */
+    char* addr_str = address_to_display(NULL, &(strinfo->id.dst_addr));
+
+    fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
+            addr_str,
+            strinfo->id.dst_port);
+    wmem_free(NULL, addr_str);
+
+    start_sec = g_htonl(strinfo->start_fd->abs_ts.secs);
+    start_usec = g_htonl(strinfo->start_fd->abs_ts.nsecs / 1000000);
+    /* rtpdump only accepts guint32 as source, will be fake for IPv6 */
+    memset(&source, 0, sizeof source);
+    sourcelen = strinfo->id.src_addr.len;
+    if (sourcelen > sizeof source)
+        sourcelen = sizeof source;
+    memcpy(&source, strinfo->id.src_addr.data, sourcelen);
+    port = g_htons(strinfo->id.src_port);
+    padding = 0;
+
+    if (fwrite(&start_sec, 4, 1, file) == 0)
+        return;
+    if (fwrite(&start_usec, 4, 1, file) == 0)
+        return;
+    if (fwrite(&source, 4, 1, file) == 0)
+        return;
+    if (fwrite(&port, 2, 1, file) == 0)
+        return;
+    if (fwrite(&padding, 2, 1, file) == 0)
+        return;
 }
 
 /* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/
 static void rtp_write_sample(rtpdump_info_t* rtpdump_info, FILE* file)
 {
-       guint16 length;    /* length of packet, including this header (may
-                            be smaller than plen if not whole packet recorded) */
-       guint16 plen;      /* actual header+payload length for RTP, 0 for RTCP */
-       guint32 offset;    /* milliseconds since the start of recording */
-
-       length = g_htons(rtpdump_info->num_samples + 8);
-       plen = g_htons(rtpdump_info->num_samples);
-       offset = g_htonl(rtpdump_info->rec_time);
-
-       if (fwrite(&length, 2, 1, file) == 0)
-               return;
-       if (fwrite(&plen, 2, 1, file) == 0)
-               return;
-       if (fwrite(&offset, 4, 1, file) == 0)
-               return;
-       if (fwrite(rtpdump_info->samples, rtpdump_info->num_samples, 1, file) == 0)
-               return;
+    guint16 length;    /* length of packet, including this header (may
+                          be smaller than plen if not whole packet recorded) */
+    guint16 plen;      /* actual header+payload length for RTP, 0 for RTCP */
+    guint32 offset;    /* milliseconds since the start of recording */
+
+    length = g_htons(rtpdump_info->num_samples + 8);
+    plen = g_htons(rtpdump_info->num_samples);
+    offset = g_htonl(rtpdump_info->rec_time);
+
+    if (fwrite(&length, 2, 1, file) == 0)
+        return;
+    if (fwrite(&plen, 2, 1, file) == 0)
+        return;
+    if (fwrite(&offset, 4, 1, file) == 0)
+        return;
+    if (fwrite(rtpdump_info->samples, rtpdump_info->num_samples, 1, file) == 0)
+        return;
 }
 
 
 /****************************************************************************/
 /* whenever a RTP packet is seen by the tap listener */
-int rtpstream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
-{
-       rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)arg;
-       const struct _rtp_info *rtpinfo = (const struct _rtp_info *)arg2;
-       rtp_stream_info_t new_stream_info;
-       rtp_stream_info_t *stream_info = NULL;
-       GList* list;
-       rtpdump_info_t rtpdump_info;
-
-       struct _rtp_conversation_info *p_conv_data = NULL;
-
-       /* gather infos on the stream this packet is part of */
-       memset(&new_stream_info, 0, sizeof(rtp_stream_info_t));
-       copy_address(&(new_stream_info.src_addr), &(pinfo->src));
-       new_stream_info.src_port = pinfo->srcport;
-       copy_address(&(new_stream_info.dest_addr), &(pinfo->dst));
-       new_stream_info.dest_port = pinfo->destport;
-       new_stream_info.ssrc = rtpinfo->info_sync_src;
-       new_stream_info.payload_type = rtpinfo->info_payload_type;
-       new_stream_info.payload_type_name = g_strdup(rtpinfo->info_payload_type_str);
-
-       if (tapinfo->mode == TAP_ANALYSE) {
-               /* check whether we already have a stream with these parameters in the list */
-               list = g_list_first(tapinfo->strinfo_list);
-               while (list)
-               {
-                       if (rtp_stream_info_cmp(&new_stream_info, (rtp_stream_info_t*)(list->data))==0)
-                       {
-                               stream_info = (rtp_stream_info_t*)(list->data);  /*found!*/
-                               break;
-                       }
-                       list = g_list_next(list);
-               }
-
-               /* not in the list? then create a new entry */
-               if (!stream_info) {
-                       new_stream_info.start_fd = pinfo->fd;
-                       new_stream_info.start_rel_time = pinfo->rel_ts;
-
-                       /* reset RTP stats */
-                       new_stream_info.rtp_stats.first_packet = TRUE;
-                       new_stream_info.rtp_stats.reg_pt = PT_UNDEFINED;
-
-                       /* Get the Setup frame number who set this RTP stream */
-                       p_conv_data = (struct _rtp_conversation_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_get_id_by_filter_name("rtp"), 0);
-                       if (p_conv_data)
-                               new_stream_info.setup_frame_number = p_conv_data->frame_number;
-                       else
-                               new_stream_info.setup_frame_number = 0xFFFFFFFF;
-
-                       stream_info = g_new(rtp_stream_info_t,1);
-                       *stream_info = new_stream_info;  /* memberwise copy of struct */
-                       tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, stream_info);
-               }
-
-               /* get RTP stats for the packet */
-               rtp_packet_analyse(&(stream_info->rtp_stats), pinfo, rtpinfo);
-               if (stream_info->rtp_stats.flags & STAT_FLAG_WRONG_TIMESTAMP
-                               || stream_info->rtp_stats.flags & STAT_FLAG_WRONG_SEQ)
-                       stream_info->problem = TRUE;
-
-
-               /* increment the packets counter for this stream */
-               ++(stream_info->packet_count);
-               stream_info->stop_rel_time = pinfo->rel_ts;
-
-               /* increment the packets counter of all streams */
-               ++(tapinfo->npackets);
-
-               return 1;  /* refresh output */
-       }
-       else if (tapinfo->mode == TAP_SAVE) {
-               if (rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0) {
-                       /* XXX - what if rtpinfo->info_all_data_present is
-                          FALSE, so that we don't *have* all the data? */
-                       rtpdump_info.rec_time = nstime_to_msec(&pinfo->fd->abs_ts) -
-                               nstime_to_msec(&tapinfo->filter_stream_fwd->start_fd->abs_ts);
-                       rtpdump_info.num_samples = rtpinfo->info_data_len;
-                       rtpdump_info.samples = rtpinfo->info_data;
-                       rtp_write_sample(&rtpdump_info, tapinfo->save_file);
-               }
-       }
-       else if (tapinfo->mode == TAP_MARK && tapinfo->tap_mark_packet) {
-               if (rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0
-                   || rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_rev)==0)
-               {
-                       tapinfo->tap_mark_packet(tapinfo, pinfo->fd);
-               }
-       }
-       return 0;
-}
-
-
-typedef struct _key_value {
-  guint32  key;
-  guint32  value;
-} key_value;
-
-
-/* RTP sampling clock rates for fixed payload types as defined in
- http://www.iana.org/assignments/rtp-parameters */
-static const key_value clock_map[] = {
-       {PT_PCMU,       8000},
-       {PT_1016,       8000},
-       {PT_G721,       8000},
-       {PT_GSM,        8000},
-       {PT_G723,       8000},
-       {PT_DVI4_8000,  8000},
-       {PT_DVI4_16000, 16000},
-       {PT_LPC,        8000},
-       {PT_PCMA,       8000},
-       {PT_G722,       8000},
-       {PT_L16_STEREO, 44100},
-       {PT_L16_MONO,   44100},
-       {PT_QCELP,      8000},
-       {PT_CN,         8000},
-       {PT_MPA,        90000},
-       {PT_G728,       8000},
-       {PT_G728,       8000},
-       {PT_DVI4_11025, 11025},
-       {PT_DVI4_22050, 22050},
-       {PT_G729,       8000},
-       {PT_CN_OLD,     8000},
-       {PT_CELB,       90000},
-       {PT_JPEG,       90000},
-       {PT_NV,         90000},
-       {PT_H261,       90000},
-       {PT_MPV,        90000},
-       {PT_MP2T,       90000},
-       {PT_H263,       90000},
-};
-
-#define NUM_CLOCK_VALUES       (sizeof clock_map / sizeof clock_map[0])
-
-static guint32
-get_clock_rate(guint32 key)
+int rtpstream_packet_cb(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
 {
-       size_t i;
-
-       for (i = 0; i < NUM_CLOCK_VALUES; i++) {
-               if (clock_map[i].key == key)
-                       return clock_map[i].value;
-       }
-       return 0;
+    rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)arg;
+    const struct _rtp_info *rtpinfo = (const struct _rtp_info *)arg2;
+    rtpstream_info_t new_stream_info;
+    rtpstream_info_t *stream_info = NULL;
+    GList* list;
+    rtpdump_info_t rtpdump_info;
+
+    struct _rtp_conversation_info *p_conv_data = NULL;
+
+    /* gather infos on the stream this packet is part of.
+     * Addresses and strings are read-only and must be duplicated if copied. */
+    rtpstream_info_init(&new_stream_info);
+    rtpstream_id_copy_pinfo(pinfo,&(new_stream_info.id),FALSE);
+    new_stream_info.id.ssrc = rtpinfo->info_sync_src;
+    new_stream_info.first_payload_type = rtpinfo->info_payload_type;
+    new_stream_info.first_payload_type_name = rtpinfo->info_payload_type_str;
+
+    if (tapinfo->mode == TAP_ANALYSE) {
+        /* check whether we already have a stream with these parameters in the list */
+        list = g_list_first(tapinfo->strinfo_list);
+        while (list)
+        {
+            if (rtpstream_info_cmp(&new_stream_info, (rtpstream_info_t *)(list->data))==0)
+            {
+                stream_info = (rtpstream_info_t *)(list->data);  /*found!*/
+                break;
+            }
+            list = g_list_next(list);
+        }
+
+        /* not in the list? then create a new entry */
+        if (!stream_info) {
+            new_stream_info.start_fd = pinfo->fd;
+            new_stream_info.start_rel_time = pinfo->rel_ts;
+            new_stream_info.first_payload_type = rtpinfo->info_payload_type;
+            new_stream_info.first_payload_type_name = rtpinfo->info_payload_type_str;
+
+            /* reset RTP stats */
+            new_stream_info.rtp_stats.first_packet = TRUE;
+            new_stream_info.rtp_stats.reg_pt = PT_UNDEFINED;
+
+            /* Get the Setup frame number who set this RTP stream */
+            p_conv_data = (struct _rtp_conversation_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_get_id_by_filter_name("rtp"), 0);
+            if (p_conv_data)
+                new_stream_info.setup_frame_number = p_conv_data->frame_number;
+            else
+                new_stream_info.setup_frame_number = 0xFFFFFFFF;
+
+            stream_info = rtpstream_info_malloc_and_init();
+            rtpstream_info_copy_deep(stream_info, &new_stream_info);
+            tapinfo->strinfo_list = g_list_prepend(tapinfo->strinfo_list, stream_info);
+        }
+
+        /* get RTP stats for the packet */
+        rtppacket_analyse(&(stream_info->rtp_stats), pinfo, rtpinfo);
+        if (stream_info->payload_type_names[rtpinfo->info_payload_type] == NULL ) {
+            update_payload_names(stream_info, rtpinfo);
+        }
+
+        if (stream_info->rtp_stats.flags & STAT_FLAG_WRONG_TIMESTAMP
+                || stream_info->rtp_stats.flags & STAT_FLAG_WRONG_SEQ)
+            stream_info->problem = TRUE;
+
+
+        /* increment the packets counter for this stream */
+        ++(stream_info->packet_count);
+        stream_info->stop_rel_time = pinfo->rel_ts;
+
+        /* increment the packets counter of all streams */
+        ++(tapinfo->npackets);
+
+        return 1;  /* refresh output */
+    }
+    else if (tapinfo->mode == TAP_SAVE) {
+        if (rtpstream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0) {
+            /* XXX - what if rtpinfo->info_all_data_present is
+               FALSE, so that we don't *have* all the data? */
+            rtpdump_info.rec_time = nstime_to_msec(&pinfo->abs_ts) -
+                nstime_to_msec(&tapinfo->filter_stream_fwd->start_fd->abs_ts);
+            rtpdump_info.num_samples = rtpinfo->info_data_len;
+            rtpdump_info.samples = rtpinfo->info_data;
+            rtp_write_sample(&rtpdump_info, tapinfo->save_file);
+        }
+    }
+    else if (tapinfo->mode == TAP_MARK && tapinfo->tap_mark_packet) {
+        if (rtpstream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0
+                || rtpstream_info_cmp(&new_stream_info, tapinfo->filter_stream_rev)==0)
+        {
+            tapinfo->tap_mark_packet(tapinfo, pinfo->fd);
+        }
+    }
+    return 0;
 }
 
-typedef struct _mimetype_and_clock {
-       const gchar   *pt_mime_name_str;
-       guint32 value;
-} mimetype_and_clock;
-/*     RTP sampling clock rates for
-       "In addition to the RTP payload formats (encodings) listed in the RTP
-       Payload Types table, there are additional payload formats that do not
-       have static RTP payload types assigned but instead use dynamic payload
-       type number assignment.  Each payload format is named by a registered
-       MIME subtype"
-       http://www.iana.org/assignments/rtp-parameters.
-
-       NOTE: Please keep the mimetypes in case insensitive alphabetical order.
-*/
-static const mimetype_and_clock mimetype_and_clock_map[] = {
-       {"AMR",         8000},                  /* [RFC4867][RFC3267] */
-       {"AMR-WB",      16000},                 /* [RFC4867][RFC3267] */
-       {"BMPEG",       90000},                 /* [RFC2343],[RFC3555] */
-       {"BT656",       90000},                 /* [RFC2431],[RFC3555] */
-       {"DV",          90000},                 /* [RFC3189] */
-       {"EVRC",        8000},                  /* [RFC3558] */
-       {"EVRC0",       8000},                  /* [RFC4788] */
-       {"EVRC1",       8000},                  /* [RFC4788] */
-       {"EVRCB",       8000},                  /* [RFC4788] */
-       {"EVRCB0",      8000},                  /* [RFC4788] */
-       {"EVRCB1",      8000},                  /* [RFC4788] */
-       {"EVRCWB",      16000},                 /* [RFC5188] */
-       {"EVRCWB0",     16000},                 /* [RFC5188] */
-       {"EVRCWB1",     16000},                 /* [RFC5188] */
-       {"G7221",       16000},                 /* [RFC3047] */
-       {"G726-16",     8000},                  /* [RFC3551][RFC4856] */
-       {"G726-24",     8000},                  /* [RFC3551][RFC4856] */
-       {"G726-32",     8000},                  /* [RFC3551][RFC4856] */
-       {"G726-40",     8000},                  /* [RFC3551][RFC4856] */
-       {"G729D",       8000},                  /* [RFC3551][RFC4856] */
-       {"G729E",       8000},                  /* [RFC3551][RFC4856] */
-       {"GSM-EFR",     8000},                  /* [RFC3551] */
-       {"H263-1998",   90000},                 /* [RFC2429],[RFC3555] */
-       {"H263-2000",   90000},                 /* [RFC2429],[RFC3555] */
-       {"H264",        90000},                 /* [RFC3984] */
-       {"MP1S",        90000},                 /* [RFC2250],[RFC3555] */
-       {"MP2P",        90000},                 /* [RFC2250],[RFC3555] */
-       {"MP4V-ES",     90000},                 /* [RFC3016] */
-       {"mpa-robust",  90000},                 /* [RFC3119] */
-       {"pointer",     90000},                 /* [RFC2862] */
-       {"raw",         90000},                 /* [RFC4175] */
-       {"red",         1000},                  /* [RFC4102] */
-       {"SMV",         8000},                  /* [RFC3558] */
-       {"SMV0",        8000},                  /* [RFC3558] */
-       {"t140",        1000},                  /* [RFC4103] */
-       {"telephone-event", 8000},              /* [RFC4733] */
-};
-
-#define NUM_DYN_CLOCK_VALUES   (sizeof mimetype_and_clock_map / sizeof mimetype_and_clock_map[0])
-
-static guint32
-get_dyn_pt_clock_rate(const gchar *payload_type_str)
+/****************************************************************************/
+/* evaluate rtpstream_info_t calculations */
+/* - code is gathered from existing GTK/Qt/tui sources related to RTP statistics calculation
+ * - one place for calculations ensures that all wireshark tools shows same output for same input and avoids code duplication
+ */
+void rtpstream_info_calculate(const rtpstream_info_t *strinfo, rtpstream_info_calc_t *calc)
 {
-       int i;
-
-       /* Search for matching mimetype in reverse order to avoid false matches
-        * when pt_mime_name_str is the prefix of payload_type_str */
-       for (i = NUM_DYN_CLOCK_VALUES - 1; i > -1 ; i--) {
-               if (g_ascii_strncasecmp(mimetype_and_clock_map[i].pt_mime_name_str,payload_type_str,(strlen(mimetype_and_clock_map[i].pt_mime_name_str))) == 0)
-                       return mimetype_and_clock_map[i].value;
-       }
-
-       return 0;
+        double sumt;
+        double sumTS;
+        double sumt2;
+        double sumtTS;
+        double clock_drift_x;
+        guint32 clock_rate_x;
+        double duration_x;
+
+        calc->src_addr_str = address_to_display(NULL, &(strinfo->id.src_addr));
+        calc->src_port = strinfo->id.src_port;
+        calc->dst_addr_str = address_to_display(NULL, &(strinfo->id.dst_addr));
+        calc->dst_port = strinfo->id.dst_port;
+        calc->ssrc = strinfo->id.ssrc;
+
+        calc->all_payload_type_names = wmem_strdup(NULL, strinfo->all_payload_type_names);
+
+        calc->packet_count = strinfo->packet_count;
+        /* packet count, lost packets */
+        calc->packet_expected = (strinfo->rtp_stats.stop_seq_nr + strinfo->rtp_stats.cycles*65536)
+            - strinfo->rtp_stats.start_seq_nr + 1;
+        calc->total_nr = strinfo->rtp_stats.total_nr;
+        calc->lost_num = calc->packet_expected - strinfo->rtp_stats.total_nr;
+        if (calc->packet_expected) {
+                calc->lost_perc = (double)(calc->lost_num*100)/(double)calc->packet_expected;
+        } else {
+                calc->lost_perc = 0;
+        }
+
+        calc->max_delta = strinfo->rtp_stats.max_delta;
+        calc->max_jitter = strinfo->rtp_stats.max_jitter;
+        calc->mean_jitter = strinfo->rtp_stats.mean_jitter;
+        calc->max_skew = strinfo->rtp_stats.max_skew;
+        calc->problem = strinfo->problem;
+        sumt = strinfo->rtp_stats.sumt;
+        sumTS = strinfo->rtp_stats.sumTS;
+        sumt2 = strinfo->rtp_stats.sumt2;
+        sumtTS = strinfo->rtp_stats.sumtTS;
+        duration_x = strinfo->rtp_stats.time - strinfo->rtp_stats.start_time;
+
+        if ((calc->packet_count >0) && (sumt2 > 0)) {
+                clock_drift_x = (calc->packet_count * sumtTS - sumt * sumTS) / (calc->packet_count * sumt2 - sumt * sumt);
+                calc->clock_drift_ms = duration_x * (clock_drift_x - 1.0);
+                clock_rate_x = (guint32)(strinfo->rtp_stats.clock_rate * clock_drift_x);
+                calc->freq_drift_hz = clock_drift_x * clock_rate_x;
+                calc->freq_drift_perc = 100.0 * (clock_drift_x - 1.0);
+        } else {
+                calc->clock_drift_ms = 0.0;
+                calc->freq_drift_hz = 0.0;
+                calc->freq_drift_perc = 0.0;
+        }
+        calc->duration_ms = duration_x / 1000.0;
+        calc->sequence_err = strinfo->rtp_stats.sequence;
+        calc->start_time_ms = strinfo->rtp_stats.start_time / 1000.0;
+        calc->first_packet_num = strinfo->rtp_stats.first_packet_num;
+        calc->last_packet_num = strinfo->rtp_stats.max_nr;
 }
 
 /****************************************************************************/
-void
-rtp_packet_analyse(tap_rtp_stat_t *statinfo,
-                      packet_info *pinfo,
-                      const struct _rtp_info *rtpinfo)
+/* free rtpstream_info_calc_t structure (internal items) */
+void rtpstream_info_calc_free(rtpstream_info_calc_t *calc)
 {
-       double current_time;
-       double current_jitter;
-       double current_diff = 0;
-       double nominaltime;
-       double arrivaltime;             /* Time relative to start_time */
-       double expected_time;
-       double absskew;
-       guint32 clock_rate;
-
-       /* Store the current time */
-       current_time = nstime_to_msec(&pinfo->rel_ts);
-
-       /*  Is this the first packet we got in this direction? */
-       if (statinfo->first_packet) {
-               /* Save the MAC address of the first RTP frame */
-               if( pinfo->dl_src.type == AT_ETHER){
-                       copy_address(&(statinfo->first_packet_mac_addr), &(pinfo->dl_src));
-               }
-               statinfo->start_seq_nr = rtpinfo->info_seq_num;
-               statinfo->stop_seq_nr = rtpinfo->info_seq_num;
-               statinfo->seq_num = rtpinfo->info_seq_num;
-               statinfo->start_time = current_time;
-               statinfo->timestamp = rtpinfo->info_timestamp;
-               statinfo->first_timestamp = rtpinfo->info_timestamp;
-               statinfo->time = current_time;
-               statinfo->lastnominaltime = 0;
-               statinfo->pt = rtpinfo->info_payload_type;
-               statinfo->reg_pt = rtpinfo->info_payload_type;
-               statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
-               statinfo->bw_history[statinfo->bw_index].time = current_time;
-               statinfo->bw_index++;
-               statinfo->total_bytes += rtpinfo->info_data_len + 28;
-               statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
-               /* Not needed ? initialised to zero? */
-               statinfo->delta = 0;
-               statinfo->jitter = 0;
-               statinfo->diff = 0;
-
-               statinfo->total_nr++;
-               statinfo->flags |= STAT_FLAG_FIRST;
-               if (rtpinfo->info_marker_set) {
-                       statinfo->flags |= STAT_FLAG_MARKER;
-               }
-               statinfo->first_packet = FALSE;
-               return;
-       }
-
-       /* Reset flags */
-       statinfo->flags = 0;
-
-       /* Chek for duplicates (src mac differs from first_packet_mac_addr) */
-       if( pinfo->dl_src.type == AT_ETHER){
-               if(!addresses_equal(&(statinfo->first_packet_mac_addr), &(pinfo->dl_src))){
-                       statinfo->flags |= STAT_FLAG_DUP_PKT;
-                       statinfo->delta = current_time-(statinfo->time);
-                       return;
-               }
-       }
-
-       /* When calculating expected rtp packets the seq number can wrap around
-        * so we have to count the number of cycles
-        * Variable cycles counts the wraps around in forwarding connection and
-        * under is flag that indicates where we are
-        *
-        * XXX How to determine number of cycles with all possible lost, late
-        * and duplicated packets without any doubt? It seems to me, that
-        * because of all possible combination of late, duplicated or lost
-        * packets, this can only be more or less good approximation
-        *
-        * There are some combinations (rare but theoretically possible),
-        * where below code won't work correctly - statistic may be wrong then.
-        */
-
-       /* So if the current sequence number is less than the start one
-        * we assume, that there is another cycle running
-        */
-       if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
-               statinfo->cycles++;
-               statinfo->under = TRUE;
-       }
-       /* what if the start seq nr was 0? Then the above condition will never
-        * be true, so we add another condition. XXX The problem would arise
-        * if one of the packets with seq nr 0 or 65535 would be lost or late
-        */
-       else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
-               (statinfo->under == FALSE)){
-               statinfo->cycles++;
-               statinfo->under = TRUE;
-       }
-       /* the whole round is over, so reset the flag */
-       else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
-               statinfo->under = FALSE;
-       }
-
-       /* Since it is difficult to count lost, duplicate or late packets separately,
-        * we would like to know at least how many times the sequence number was not ok
-        */
-
-       /* If the current seq number equals the last one or if we are here for
-        * the first time, then it is ok, we just store the current one as the last one
-        */
-       if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
-               statinfo->seq_num = rtpinfo->info_seq_num;
-       /* If the first one is 65535 we wrap */
-       else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
-               statinfo->seq_num = rtpinfo->info_seq_num;
-       /* Lost packets. If the prev seq is enormously larger than the cur seq
-        * we assume that instead of being massively late we lost the packet(s)
-        * that would have indicated the sequence number wrapping. An imprecise
-        * heuristic at best, but it seems to work well enough.
-        * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5958 */
-       else if (statinfo->seq_num+1 < rtpinfo->info_seq_num || statinfo->seq_num - rtpinfo->info_seq_num > 0xFF00) {
-               statinfo->seq_num = rtpinfo->info_seq_num;
-               statinfo->sequence++;
-               statinfo->flags |= STAT_FLAG_WRONG_SEQ;
-       }
-       /* Late or duplicated */
-       else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
-               statinfo->sequence++;
-               statinfo->flags |= STAT_FLAG_WRONG_SEQ;
-       }
-
-       /* Check payload type */
-       if (rtpinfo->info_payload_type == PT_CN
-               || rtpinfo->info_payload_type == PT_CN_OLD)
-               statinfo->flags |= STAT_FLAG_PT_CN;
-       if (statinfo->pt == PT_CN
-               || statinfo->pt == PT_CN_OLD)
-               statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
-       if (rtpinfo->info_payload_type != statinfo->pt)
-               statinfo->flags |= STAT_FLAG_PT_CHANGE;
-       statinfo->pt = rtpinfo->info_payload_type;
-
-       /*
-        * Return for unknown payload types
-        * Ignore jitter calculation for clockrate = 0
-        */
-       if (statinfo->pt < 96 ){
-               clock_rate = get_clock_rate(statinfo->pt);
-       }else{ /* Dynamic PT */
-               if ( rtpinfo->info_payload_type_str != NULL ){
-                       /* Is it a "telephone-event" ?
-                        * Timestamp is not increased for telepone-event packets impacting
-                        * calculation of Jitter Skew and clock drift.
-                        * see 2.2.1 of RFC 4733
-                        */
-                       if (g_ascii_strncasecmp("telephone-event",rtpinfo->info_payload_type_str,(strlen("telephone-event")))==0){
-                               clock_rate = 0;
-                               statinfo->flags |= STAT_FLAG_PT_T_EVENT;
-                       }else{
-                               if(rtpinfo->info_payload_rate !=0){
-                                       clock_rate = rtpinfo->info_payload_rate;
-                               }else{
-                                       clock_rate = get_dyn_pt_clock_rate(rtpinfo->info_payload_type_str);
-                               }
-                       }
-               }else{
-                       clock_rate = 0;
-               }
-       }
-
-               /* Handle wraparound ? */
-       arrivaltime = current_time - statinfo->start_time;
-
-       if (statinfo->first_timestamp > rtpinfo->info_timestamp){
-               /* Handle wraparound */
-               nominaltime = (double)(rtpinfo->info_timestamp + 0xffffffff - statinfo->first_timestamp + 1);
-       }else{
-               nominaltime = (double)(rtpinfo->info_timestamp - statinfo->first_timestamp);
-       }
-
-       /* Can only analyze defined sampling rates */
-       if (clock_rate != 0) {
-               statinfo->clock_rate = clock_rate;
-               /* Convert from sampling clock to ms */
-               nominaltime = nominaltime /(clock_rate/1000);
-
-               /* Calculate the current jitter(in ms) */
-               if (!statinfo->first_packet) {
-                       expected_time = statinfo->time + (nominaltime - statinfo->lastnominaltime);
-                       current_diff = fabs(current_time - expected_time);
-                       current_jitter = (15 * statinfo->jitter + current_diff) / 16;
-
-                       statinfo->delta = current_time-(statinfo->time);
-                       statinfo->jitter = current_jitter;
-                       statinfo->diff = current_diff;
-               }
-               statinfo->lastnominaltime = nominaltime;
-               /* Calculate skew, i.e. absolute jitter that also catches clock drift
-                * Skew is positive if TS (nominal) is too fast
-                */
-               statinfo->skew    = nominaltime - arrivaltime;
-               absskew = fabs(statinfo->skew);
-               if(absskew > fabs(statinfo->max_skew)){
-                       statinfo->max_skew = statinfo->skew;
-               }
-               /* Gather data for calculation of average, minimum and maximum framerate based on timestamp */
-#if 0
-               if (numPackets > 0 && (!hardPayloadType || !alternatePayloadType)) {
-                       /* Skip first packet and possibly alternate payload type packets */
-                       double dt;
-                       dt     = nominaltime - statinfo->lastnominaltime;
-                       sumdt += 1.0 * dt;
-                       numdt += (dt != 0 ? 1 : 0);
-                       mindt  = (dt < mindt ? dt : mindt);
-                       maxdt  = (dt > maxdt ? dt : maxdt);
-               }
-#endif
-               /* Gather data for calculation of skew least square */
-               statinfo->sumt   += 1.0 * current_time;
-               statinfo->sumTS  += 1.0 * nominaltime;
-               statinfo->sumt2  += 1.0 * current_time * current_time;
-               statinfo->sumtTS += 1.0 * current_time * nominaltime;
-       }
-
-       /* Calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP) + 8bytes(UDP) */
-       statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
-       statinfo->bw_history[statinfo->bw_index].time = current_time;
-
-       /* Check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
-       while ((statinfo->bw_history[statinfo->bw_start_index].time+1000/* ms */)<current_time){
-               statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
-               statinfo->bw_start_index++;
-               if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
-       };
-       /* IP hdr + UDP + RTP */
-       statinfo->total_bytes += rtpinfo->info_data_len + 28;
-       statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
-       statinfo->bw_index++;
-       if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
-
-
-       /* Is it a packet with the mark bit set? */
-       if (rtpinfo->info_marker_set) {
-               statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
-               if (rtpinfo->info_timestamp > statinfo->timestamp){
-                       statinfo->flags |= STAT_FLAG_MARKER;
-               }
-               else{
-                       statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
-               }
-       }
-       /* Is it a regular packet? */
-       if (!(statinfo->flags & STAT_FLAG_FIRST)
-               && !(statinfo->flags & STAT_FLAG_MARKER)
-               && !(statinfo->flags & STAT_FLAG_PT_CN)
-               && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
-               && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
-               /* Include it in maximum delta calculation */
-               if (statinfo->delta > statinfo->max_delta) {
-                       statinfo->max_delta = statinfo->delta;
-                       statinfo->max_nr = pinfo->fd->num;
-               }
-               if (clock_rate != 0) {
-                       /* Maximum and mean jitter calculation */
-                       if (statinfo->jitter > statinfo->max_jitter) {
-                               statinfo->max_jitter = statinfo->jitter;
-                       }
-                       statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
-               }
-       }
-       /* Regular payload change? (CN ignored) */
-       if (!(statinfo->flags & STAT_FLAG_FIRST)
-               && !(statinfo->flags & STAT_FLAG_PT_CN)) {
-               if ((statinfo->pt != statinfo->reg_pt)
-                       && (statinfo->reg_pt != PT_UNDEFINED)) {
-                       statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
-               }
-       }
-
-       /* Set regular payload*/
-       if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
-               statinfo->reg_pt = statinfo->pt;
-       }
-
-       statinfo->time = current_time;
-       statinfo->timestamp = rtpinfo->info_timestamp;
-       statinfo->stop_seq_nr = rtpinfo->info_seq_num;
-       statinfo->total_nr++;
-
-       return;
+        wmem_free(NULL, calc->src_addr_str);
+        wmem_free(NULL, calc->dst_addr_str);
+        wmem_free(NULL, calc->all_payload_type_names);
 }
 
 /*
- * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
  *
- * Local variables:
- * c-basic-offset: 8
+ * Local Variables:
+ * c-basic-offset: 4
  * tab-width: 8
- * indent-tabs-mode: t
+ * indent-tabs-mode: nil
  * End:
  *
- * vi: set shiftwidth=8 tabstop=8 noexpandtab:
- * :indentSize=8:tabSize=8:noTabs=false:
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
  */