diff options
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/Makefile.am | 14 | ||||
-rw-r--r-- | gtk/Makefile.nmake | 6 | ||||
-rw-r--r-- | gtk/rtp_analysis.c | 1895 | ||||
-rw-r--r-- | gtk/rtp_analysis.h | 50 | ||||
-rw-r--r-- | gtk/rtp_stream.c | 407 | ||||
-rw-r--r-- | gtk/rtp_stream.h | 140 | ||||
-rw-r--r-- | gtk/rtp_stream_dlg.c | 806 | ||||
-rw-r--r-- | gtk/rtp_stream_dlg.h | 45 | ||||
-rw-r--r-- | gtk/tap_rtp.c | 1761 |
9 files changed, 3358 insertions, 1766 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 1935b851d6..c8cdd6fe00 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am # Automake file for the GTK interface routines for Ethereal # -# $Id: Makefile.am,v 1.69 2003/09/24 06:15:51 oabad Exp $ +# $Id: Makefile.am,v 1.70 2003/09/24 07:48:10 guy Exp $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@ethereal.com> @@ -45,7 +45,9 @@ ETHEREAL_TAP_SRC = \ rpc_stat.c \ rpc_progs.c \ smb_stat.c \ - tap_rtp.c \ + rtp_analysis.c \ + rtp_stream.c \ + rtp_stream_dlg.c \ wsp_stat.c ethereal-tap-register.c: $(ETHEREAL_TAP_SRC) $(top_srcdir)/make-tapreg-dotc @@ -122,9 +124,12 @@ libui_a_SOURCES = \ proto_draw.h \ proto_hier_stats_dlg.h \ proto_hier_stats_dlg.c \ - simple_dialog.c \ + rtp_analysis.h \ + rtp_stream.h \ + rtp_stream_dlg.h \ service_response_time_table.c \ service_response_time_table.h \ + simple_dialog.c \ stream_prefs.c \ stream_prefs.h \ summary_dlg.c \ @@ -205,6 +210,9 @@ libui_a_SOURCES = \ proto_draw.h \ proto_hier_stats_dlg.h \ proto_hier_stats_dlg.c \ + rtp_analysis.h \ + rtp_stream.h \ + rtp_stream_dlg.h \ service_response_time_table.c \ service_response_time_table.h \ simple_dialog.c \ diff --git a/gtk/Makefile.nmake b/gtk/Makefile.nmake index 28787f8081..878eabcefb 100644 --- a/gtk/Makefile.nmake +++ b/gtk/Makefile.nmake @@ -1,7 +1,7 @@ ## Makefile for building ethereal.exe with Microsoft C and nmake ## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake # -# $Id: Makefile.nmake,v 1.52 2003/09/10 05:35:25 guy Exp $ +# $Id: Makefile.nmake,v 1.53 2003/09/24 07:48:11 guy Exp $ include ..\config.nmake @@ -37,7 +37,9 @@ ETHEREAL_TAP_SRC = \ rpc_stat.c \ rpc_progs.c \ smb_stat.c \ - tap_rtp.c \ + rtp_analysis.c \ + rtp_stream.c \ + rtp_stream_dlg.c \ wsp_stat.c ETHEREAL_TAP_OBJECTS = $(ETHEREAL_TAP_SRC:.c=.obj) diff --git a/gtk/rtp_analysis.c b/gtk/rtp_analysis.c new file mode 100644 index 0000000000..95f20db06a --- /dev/null +++ b/gtk/rtp_analysis.c @@ -0,0 +1,1895 @@ +/* rtp_analysis.c + * RTP analysis addition for ethereal + * + * $Id: rtp_analysis.c,v 1.1 2003/09/24 07:48:11 guy Exp $ + * + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff <lars.ruoff@gmx.net> + * + * based on tap_rtp.c + * Copyright 2003, Iskratel, Ltd, Kranj + * By Miha Jemec <m.jemec@iskratel.si> + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/*do not define this symbol. will be added soon*/ +/*#define USE_CONVERSATION_GRAPH 1*/ + +#include "rtp_analysis.h" +#include "rtp_stream.h" +#include "rtp_stream_dlg.h" + +#ifdef USE_CONVERSATION_GRAPH +#include "../graph/graph.h" +#endif + +#include "epan/epan_dissect.h" +#include "epan/filesystem.h" +#include "tap.h" +#include "register.h" +#include "packet-rtp.h" +#include "g711.h" + +/* in /gtk ... */ +#include "dlg_utils.h" +#include "ui_util.h" +#include "simple_dialog.h" +#include "menu.h" +#include "main.h" +#include "progress_dlg.h" +#include "compat_macros.h" + +#include <math.h> +#include <fcntl.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_IO_H +#include <io.h> /* open/close on win32 */ +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/****************************************************************************/ + +typedef struct _dialog_data_t { + GtkWidget *window; + GtkWidget *clist_fwd; + GtkWidget *clist_rev; + GtkWidget *label_stats_fwd; + GtkWidget *label_stats_rev; + GtkWidget *notebook; + GtkCList *selected_clist; + GtkWidget *save_voice_as_w; + GtkWidget *save_csv_as_w; + gint selected_row; +#ifdef USE_CONVERSATION_GRAPH + GtkWidget *graph_window; +#endif +} dialog_data_t; + +/* type of error when saving voice in a file didn't succeed */ +typedef enum { + TAP_RTP_WRONG_CODEC, + TAP_RTP_WRONG_LENGTH, + TAP_RTP_PADDING_ERROR, + TAP_RTP_FILE_OPEN_ERROR, + TAP_RTP_NO_DATA +} error_type_t; + +typedef enum { + FIRST_PACKET, + MARK_SET, + NORMAL_PACKET +} packet_type; + +/* structure that holds the information about the forward and reversed direction */ +struct _info_direction { + gboolean first_packet; + guint16 seq_num; + guint32 timestamp; + guint32 delta_timestamp; + double delay; + double jitter; + double time; + double start_time; + double max_delay; + guint32 max_nr; + guint16 start_seq_nr; + guint16 stop_seq_nr; + guint32 total_nr; + guint32 sequence; + gboolean under; + gint cycles; + FILE *fp; + guint32 count; + error_type_t error_type; + gboolean saved; +}; + +/* structure that holds general information about the connection +* and structures for both directions */ +typedef struct _user_data_t { + /* tap associated data*/ + guint32 ip_src_fwd; + guint16 port_src_fwd; + guint32 ip_dst_fwd; + guint16 port_dst_fwd; + guint32 ssrc_fwd; + guint32 ip_src_rev; + guint16 port_src_rev; + guint32 ip_dst_rev; + guint16 port_dst_rev; + guint32 ssrc_rev; + + struct _info_direction forward; + struct _info_direction reversed; + + char f_tempname[100]; + char r_tempname[100]; + + /* dialog associated data */ + dialog_data_t dlg; + +#ifdef USE_CONVERSATION_GRAPH + time_series_t series_fwd; + time_series_t series_rev; +#endif +} user_data_t; + + +typedef const guint8 * ip_addr_p; + + +/****************************************************************************/ +/* TAP FUNCTIONS */ + +/****************************************************************************/ +/* when there is a [re]reading of packet's */ +static void +rtp_reset(user_data_t *user_data _U_) +{ + user_data->forward.first_packet = TRUE; + user_data->reversed.first_packet = TRUE; + user_data->forward.max_delay = 0; + user_data->reversed.max_delay = 0; + user_data->forward.delay = 0; + user_data->reversed.delay = 0; + user_data->forward.jitter = 0; + user_data->reversed.jitter = 0; + user_data->forward.timestamp = 0; + user_data->reversed.timestamp = 0; + user_data->forward.max_nr = 0; + user_data->reversed.max_nr = 0; + user_data->forward.total_nr = 0; + user_data->reversed.total_nr = 0; + user_data->forward.sequence = 0; + user_data->reversed.sequence = 0; + user_data->forward.start_seq_nr = 0; + user_data->reversed.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */ + user_data->forward.stop_seq_nr = 0; + user_data->reversed.stop_seq_nr = 0; + user_data->forward.cycles = 0; + user_data->reversed.cycles = 0; + user_data->forward.under = FALSE; + user_data->reversed.under = FALSE; + user_data->forward.saved = FALSE; + user_data->reversed.saved = FALSE; + user_data->forward.start_time = 0; + user_data->reversed.start_time = 0; + user_data->forward.time = 0; + user_data->reversed.time = 0; + user_data->forward.count = 0; + user_data->reversed.count = 0; + +#ifdef USE_CONVERSATION_GRAPH + if (user_data->dlg.graph_window != NULL) + gtk_widget_destroy(user_data->dlg.graph_window); + + g_array_free(user_data->series_fwd.value_pairs, TRUE); + user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t)); + + g_array_free(user_data->series_rev.value_pairs, TRUE); + user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t)); +#endif + + /* XXX check for error at fclose? */ + if (user_data->forward.fp != NULL) + fclose(user_data->forward.fp); + if (user_data->reversed.fp != NULL) + fclose(user_data->reversed.fp); + user_data->forward.fp = fopen(user_data->f_tempname, "wb"); + if (user_data->forward.fp == NULL) + user_data->forward.error_type = TAP_RTP_FILE_OPEN_ERROR; + user_data->reversed.fp = fopen(user_data->r_tempname, "wb"); + if (user_data->reversed.fp == NULL) + user_data->reversed.error_type = TAP_RTP_FILE_OPEN_ERROR; + return; +} + +/****************************************************************************/ +/* here we can redraw the output */ +/* not used yet */ +static void rtp_draw(void *prs _U_) +{ + return; +} + +static int do_calculation(GtkWidget *clist, packet_type pkt_type, void *ptrs, void *vpri, void *vpinfo); +static void add_to_clist(GtkWidget *clist, guint32 number, guint16 seq_num, + double delay, double jitter, gboolean status, gboolean marker, + gchar *timeStr, guint32 pkt_len); + +/****************************************************************************/ +/* whenever a RTP packet is seen by the tap listener */ +/* this function works as follows: +* 1) packets that are not displayed are ignored +* return +* 3) if not, is current packet matching the forward direction +* if yes, call the function that does the calculation and saves the voice info +* 4) if not, is current packet matching the reversed connection +* if yes, call the function that does the calculation and saves the voice info +*/ +static int rtp_packet(user_data_t *user_data, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo) +{ +#ifdef USE_CONVERSATION_GRAPH + value_pair_t vp; +#endif + + /* we ignore packets that are not displayed */ + if (pinfo->fd->flags.passed_dfilter == 0) + return 0; + + /* is it the forward direction? */ + else if (user_data->ssrc_fwd == rtpinfo->info_sync_src) { +#ifdef USE_CONVERSATION_GRAPH + vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000); + vp.fnumber = pinfo->fd->num; + g_array_append_val(user_data->series_fwd.value_pairs, vp); +#endif + + if (user_data->forward.first_packet != FALSE) + /* first argument is the direction TRUE == forward */ + return do_calculation(user_data->dlg.clist_fwd, FIRST_PACKET, &user_data->forward, rtpinfo, pinfo); + else if (rtpinfo->info_marker_set != FALSE) + return do_calculation(user_data->dlg.clist_fwd, MARK_SET, &user_data->forward, rtpinfo, pinfo); + else + return do_calculation(user_data->dlg.clist_fwd, NORMAL_PACKET, &user_data->forward, rtpinfo, pinfo); + } + /* is it the reversed direction? */ + else if (user_data->ssrc_rev == rtpinfo->info_sync_src) { +#ifdef USE_CONVERSATION_GRAPH + vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000); + vp.fnumber = pinfo->fd->num; + g_array_append_val(user_data->series_rev.value_pairs, vp); +#endif + + if (user_data->reversed.first_packet != FALSE) + return do_calculation(user_data->dlg.clist_rev, FIRST_PACKET, &user_data->reversed, rtpinfo, pinfo); + else if (rtpinfo->info_marker_set != FALSE) + return do_calculation(user_data->dlg.clist_rev, MARK_SET, &user_data->reversed, rtpinfo, pinfo); + else + return do_calculation(user_data->dlg.clist_rev, NORMAL_PACKET, &user_data->reversed, rtpinfo, pinfo); + } + + return 0; +} + + +/****************************************************************************/ +static int do_calculation(GtkWidget *clist, packet_type pkt_type, void *ptrs, void *vpri, void *vpinfo) +{ + struct _info_direction *ptr=ptrs; + struct _rtp_info *pri=vpri; + packet_info *pinfo = vpinfo; + guint i; + double current_time; + double current_jitter; + guint8 *data; + gint16 tmp; + + guint16 msecs; + gchar timeStr[32]; + + struct tm *tm_tmp; + time_t then; + + then = pinfo->fd->abs_secs; + msecs = (guint16)(pinfo->fd->abs_usecs/1000); + + tm_tmp = localtime(&then); + snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d", + tm_tmp->tm_mon + 1, + tm_tmp->tm_mday, + tm_tmp->tm_year + 1900, + tm_tmp->tm_hour, + tm_tmp->tm_min, + tm_tmp->tm_sec, + msecs); + + /* store the current time and calculate the current jitter */ + current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000; + current_jitter = ptr->jitter + ( fabs (current_time - (ptr->time) - + ((double)(pri->info_timestamp)-(double)(ptr->timestamp))/8000)- ptr->jitter)/16; + ptr->delay = current_time-(ptr->time); + + /* We have 3 possibilities: + * is this the first packet we got in this direction? */ + if (pkt_type == FIRST_PACKET) { + ptr->first_packet = FALSE; + ptr->start_seq_nr = pri->info_seq_num; + ptr->start_time = current_time; + add_to_clist(clist, + pinfo->fd->num, pri->info_seq_num, 0, + pri->info_marker_set? TRUE: FALSE, TRUE, FALSE, + timeStr, pinfo->fd->pkt_len); + if (ptr->fp == NULL) { + ptr->saved = FALSE; + ptr->error_type = TAP_RTP_FILE_OPEN_ERROR; + } + else + ptr->saved = TRUE; + } + /* or is it a packet with the mark bit set? */ + else if (pkt_type == MARK_SET) { + ptr->delta_timestamp = pri->info_timestamp - ptr->timestamp; + add_to_clist(clist, + pinfo->fd->num, pri->info_seq_num, current_time - (ptr->time), + current_jitter, ptr->seq_num+1 == pri->info_seq_num? TRUE: FALSE, TRUE, + timeStr, pinfo->fd->pkt_len); + } + /* if neither then it is a "normal" packet pkt_type == NORMAL_PACKET */ + else { + if (ptr->delay > ptr->max_delay) { + ptr->max_delay = ptr->delay; + ptr->max_nr = pinfo->fd->num; + } + add_to_clist(clist, + pinfo->fd->num, pri->info_seq_num, current_time -(ptr->time), + current_jitter , ptr->seq_num+1 == pri->info_seq_num?TRUE:FALSE, FALSE, + timeStr, pinfo->fd->pkt_len); + } + + /* 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 ((pri->info_seq_num < ptr->start_seq_nr) && (ptr->under == FALSE)){ + ptr->cycles++; + ptr->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 ((pri->info_seq_num == 0) && (ptr->stop_seq_nr == 65535) && + (ptr->under == FALSE)){ + ptr->cycles++; + ptr->under = TRUE; + } + /* the whole round is over, so reset the flag */ + else if ((pri->info_seq_num > ptr->start_seq_nr) && (ptr->under != FALSE)) { + ptr->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 ( ( ptr->seq_num+1 == pri->info_seq_num) || (pkt_type == FIRST_PACKET) ) + ptr->seq_num = pri->info_seq_num; + /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */ + else if ( (ptr->seq_num == 65535) && (pri->info_seq_num == 0) ) + ptr->seq_num = pri->info_seq_num; + /* lost packets */ + else if (ptr->seq_num+1 < pri->info_seq_num) { + ptr->seq_num = pri->info_seq_num; + ptr->sequence++; + } + /* late or duplicated */ + else if (ptr->seq_num+1 > pri->info_seq_num) + ptr->sequence++; + + ptr->time = current_time; + ptr->timestamp = pri->info_timestamp; + ptr->stop_seq_nr = pri->info_seq_num; + ptr->total_nr++; + + /* save the voice information */ + /* if there was already an error, we quit */ + if (ptr->saved == FALSE) + return 0; + + /* if the captured length and packet length aren't equal, we quit + * because there is some information missing */ + if (pinfo->fd->pkt_len != pinfo->fd->cap_len) { + ptr->saved = FALSE; + ptr->error_type = TAP_RTP_WRONG_LENGTH; + return 0; + } + + /* if padding bit is set, but the padding count is bigger + * then the whole RTP data - error with padding count */ + if ( (pri->info_padding_set != FALSE) && + (pri->info_padding_count > pri->info_payload_len) ) { + ptr->saved = FALSE; + ptr->error_type = TAP_RTP_PADDING_ERROR; + return 0; + } + + /* do we need to insert some silence? */ + if ((pkt_type == MARK_SET) && + (ptr->delta_timestamp > (pri->info_payload_len - pri->info_padding_count)) ) { + /* the amount of silence should be the difference between + * the last timestamp and the current one minus x + * x should equal the amount of information in the last frame + * XXX not done yet */ + for(i=0; i < (ptr->delta_timestamp - pri->info_payload_len - + pri->info_padding_count); i++) { + tmp = (gint16 )ulaw2linear((unsigned char)(0x55)); + fwrite(&tmp, 2, 1, ptr->fp); + ptr->count++; + } + fflush(ptr->fp); + } + + /* ulaw? */ + if (pri->info_payload_type == 0) { + /* we put the pointer at the beggining of the RTP data, that is + * at the end of the current frame minus the length of the + * padding count minus length of the RTP data */ + data = cfile.pd + (pinfo->fd->pkt_len - pri->info_payload_len); + for(i=0; i < (pri->info_payload_len - pri->info_padding_count); i++, data++) { + tmp = (gint16 )ulaw2linear((unsigned char)*data); + fwrite(&tmp, 2, 1, ptr->fp); + ptr->count++; + } + fflush(ptr->fp); + ptr->saved = TRUE; + return 0; + } + + /* alaw? */ + else if (pri->info_payload_type == 8) { + data = cfile.pd + (pinfo->fd->pkt_len - pri->info_payload_len); + for(i=0; i < (pri->info_payload_len - pri->info_padding_count); i++, data++) { + tmp = (gint16 )alaw2linear((unsigned char)*data); + fwrite(&tmp, 2, 1, ptr->fp); + ptr->count++; + } + fflush(ptr->fp); + ptr->saved = TRUE; + return 0; + } + + /* unsupported codec or XXX other error */ + else { + ptr->saved = FALSE; + ptr->error_type = TAP_RTP_WRONG_CODEC; + return 0; + } + return 0; +} + + +/****************************************************************************/ +/* CALLBACKS */ + +/****************************************************************************/ +/* XXX just copied from gtk/rpc_stat.c */ +void protect_thread_critical_region(void); +void unprotect_thread_critical_region(void); + + +/****************************************************************************/ +/* close the dialog window and remove the tap listener */ +static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_) +{ + protect_thread_critical_region(); + remove_tap_listener(user_data); + unprotect_thread_critical_region(); + + if (user_data->forward.fp != NULL) + fclose(user_data->forward.fp); + if (user_data->reversed.fp != NULL) + fclose(user_data->reversed.fp); + remove(user_data->f_tempname); + remove(user_data->r_tempname); + + /* Is there a save voice window open? */ + if (user_data->dlg.save_voice_as_w != NULL) + gtk_widget_destroy(user_data->dlg.save_voice_as_w); + +#ifdef USE_CONVERSATION_GRAPH + /* Is there a graph window open? */ + if (user_data->dlg.graph_window != NULL) + gtk_widget_destroy(user_data->dlg.graph_window); +#endif + + g_free(user_data); +} + + +/****************************************************************************/ +static void on_clist_select_row(GtkCList *clist _U_, + gint row _U_, + gint column _U_, + GdkEvent *event _U_, + user_data_t *user_data _U_) +{ + user_data->dlg.selected_clist = clist; + user_data->dlg.selected_row = row; +} + + +#ifdef USE_CONVERSATION_GRAPH +/****************************************************************************/ +/* when the graph window gets destroyed */ +static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_) +{ + /* note that graph window has been destroyed */ + user_data->dlg.graph_window = NULL; +} + +/****************************************************************************/ +static void graph_selection_callback(value_pair_t vp, user_data_t *user_data) +{ + guint row; + GtkCList *clist = NULL; + if (vp.fnumber != 0) { + clist = GTK_CLIST(user_data->dlg.clist_fwd); + row = gtk_clist_find_row_from_data(clist, + GUINT_TO_POINTER(vp.fnumber)); + if (row==-1) { + clist = GTK_CLIST(user_data->dlg.clist_rev); + row = gtk_clist_find_row_from_data(clist, + GUINT_TO_POINTER(vp.fnumber)); + } + if (row!=-1) { + gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook), + (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1); + gtk_clist_select_row(clist, row, 0); + gtk_clist_moveto(clist, row, 0, 0.5, 0); + } + } +} + + +/****************************************************************************/ +static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_) +{ + gchar title1[80]; + gchar title2[80]; + GList *list = NULL; + + if (user_data->dlg.graph_window != NULL) { + /* There's already a graph window; reactivate it. */ + reactivate_window(user_data->dlg.graph_window); + return; + } + + list = g_list_append(list, &(user_data->series_fwd)); + list = g_list_append(list, &(user_data->series_rev)); + + user_data->series_fwd.color.pixel = 0; + user_data->series_fwd.color.red = 0x80ff; + user_data->series_fwd.color.green = 0xe0ff; + user_data->series_fwd.color.blue = 0xffff; + user_data->series_fwd.yvalue = 0.5; + + user_data->series_rev.color.pixel = 0; + user_data->series_rev.color.red = 0x60ff; + user_data->series_rev.color.green = 0xc0ff; + user_data->series_rev.color.blue = 0xffff; + user_data->series_rev.yvalue = -0.5; + + g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)", + ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)), + user_data->port_src_fwd, + ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)), + user_data->port_dst_fwd, + user_data->ssrc_fwd); + + g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)", + ip_to_str((ip_addr_p)&(user_data->ip_src_rev)), + user_data->port_src_rev, + ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)), + user_data->port_dst_rev, + user_data->ssrc_rev); + + user_data->dlg.graph_window = show_conversation_graph(list, title1, title2, + &graph_selection_callback, user_data); + gtk_signal_connect(GTK_OBJECT(user_data->dlg.graph_window), "destroy", + GTK_SIGNAL_FUNC(on_destroy_graph), user_data); +} +#endif /*USE_CONVERSATION_GRAPH*/ + + +/****************************************************************************/ +static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_) +{ + guint fnumber; + + if (user_data->dlg.selected_clist!=NULL) { + fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data( + GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) ); + goto_frame(&cfile, fnumber); + } +} + + +static void draw_stat(user_data_t *user_data); + +/****************************************************************************/ +/* re-dissects all packets */ +static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_) +{ + gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd)); + gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev)); + redissect_packets(&cfile); + draw_stat(user_data); +} + +/****************************************************************************/ +/* on_destroy is automatically called after that */ +static void on_close_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_) +{ + gtk_grab_remove(GTK_WIDGET(user_data->dlg.window)); + gtk_widget_destroy(GTK_WIDGET(user_data->dlg.window)); +} + + +/****************************************************************************/ +/* when we want to save the information */ +static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_) +{ + gchar *g_dest; + GtkWidget *rev, *forw, *both; + user_data_t *user_data; + + FILE *fp; + char *columnText; + int i,j; + + g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs))); + + /* Perhaps the user specified a directory instead of a file. + Check whether they did. */ + if (test_for_directory(g_dest) == EISDIR) { + /* It's a directory - set the file selection box to display it. */ + set_last_open_dir(g_dest); + g_free(g_dest); + gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir); + return; + } + + rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb"); + forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb"); + both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb"); + user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data"); + + if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) { + fp = fopen(g_dest, "w"); + + if (GTK_TOGGLE_BUTTON(both)->active) { + fprintf(fp, "Forward\n"); + } + + for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) { + if (j == 0) { + fprintf(fp,"%s",GTK_CLIST(user_data->dlg.clist_fwd)->column[j].title); + } else { + fprintf(fp,",%s",GTK_CLIST(user_data->dlg.clist_fwd)->column[j].title); + } + } + fprintf(fp,"\n"); + for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) { + for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) { + gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText); + if (j == 0) { + fprintf(fp,"%s",columnText); + } else { + fprintf(fp,",%s",columnText); + } + } + fprintf(fp,"\n"); + } + + fclose(fp); + } + + if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) { + + if (GTK_TOGGLE_BUTTON(both)->active) { + fp = fopen(g_dest, "a"); + fprintf(fp, "\nReverse\n"); + } else { + fp = fopen(g_dest, "w"); + } + for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) { + if (j == 0) { + fprintf(fp,"%s",GTK_CLIST(user_data->dlg.clist_rev)->column[j].title); + } else { + fprintf(fp,",%s",GTK_CLIST(user_data->dlg.clist_rev)->column[j].title); + } + } + fprintf(fp,"\n"); + for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) { + for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) { + gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText); + if (j == 0) { + fprintf(fp,"%s",columnText); + } else { + fprintf(fp,",%s",columnText); + } + } + fprintf(fp,"\n"); + } + fclose(fp); + } + + gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w)); +} + +static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_) +{ + user_data->dlg.save_csv_as_w = NULL; +} + +/* when the user wants to save the csv information in a file */ +static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_) +{ + GtkWidget *vertb; + GtkWidget *table1; + GtkWidget *label_format; + GtkWidget *channels_label; + GSList *channels_group = NULL; + GtkWidget *forward_rb; + GtkWidget *reversed_rb; + GtkWidget *both_rb; + GtkWidget *ok_bt; + + if (user_data->dlg.save_csv_as_w != NULL) { + /* There's already a Save CSV info dialog box; reactivate it. */ + reactivate_window(user_data->dlg.save_csv_as_w); + return; + } + + user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV"); + gtk_signal_connect(GTK_OBJECT(user_data->dlg.save_csv_as_w), "destroy", + GTK_SIGNAL_FUNC(save_csv_as_destroy_cb), user_data); + + /* Container for each row of widgets */ + vertb = gtk_vbox_new(FALSE, 0); + gtk_container_border_width(GTK_CONTAINER(vertb), 5); + gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area), + vertb, FALSE, FALSE, 0); + gtk_widget_show (vertb); + + table1 = gtk_table_new (2, 4, FALSE); + gtk_widget_show (table1); + gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (table1), 10); + gtk_table_set_row_spacings (GTK_TABLE (table1), 20); + + label_format = gtk_label_new ("Format: Comma Separated Values"); + gtk_widget_show (label_format); + gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + + channels_label = gtk_label_new ("Channels:"); + gtk_widget_show (channels_label); + gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5); + + forward_rb = gtk_radio_button_new_with_label (channels_group, "forward "); + channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb)); + gtk_widget_show (forward_rb); + gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed"); + channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb)); + gtk_widget_show (reversed_rb); + gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + both_rb = gtk_radio_button_new_with_label (channels_group, "both"); + channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb)); + gtk_widget_show (both_rb); + gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE); + + ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button; + OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb); + OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb); + OBJECT_SET_DATA(ok_bt, "both_rb", both_rb); + OBJECT_SET_DATA(ok_bt, "user_data", user_data); + + /* Connect the cancel_button to destroy the widget */ + SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, + "clicked", (GtkSignalFunc)gtk_widget_destroy, + user_data->dlg.save_csv_as_w); + + /* Catch the "key_press_event" signal in the window, so that we can catch + the ESC key being pressed and act as if the "Cancel" button had + been selected. */ + dlg_set_cancel(user_data->dlg.save_csv_as_w, GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button); + + gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked", + GTK_SIGNAL_FUNC(save_csv_as_ok_cb), user_data->dlg.save_csv_as_w); + + gtk_widget_show(user_data->dlg.save_csv_as_w); +} + + +/****************************************************************************/ +static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_) +{ + /* Note that we no longer have a Save voice info dialog box. */ + user_data->dlg.save_voice_as_w = NULL; +} + +/****************************************************************************/ +/* here we save it into a file that user specified */ +/* XXX what about endians here? could go something wrong? */ +static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data) +{ + int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten; + gint16 f_pd; + gint16 r_pd; + gchar pd[1]; + guint32 f_write_silence = 0; + guint32 r_write_silence = 0; + progdlg_t *progbar; + guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0; + gboolean stop_flag = FALSE; + + forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY); + if (forw_fd < 0) + return FALSE; + rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY); + if (rev_fd < 0) { + close(forw_fd); + return FALSE; + } + + /* open file for saving */ + to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + if (to_fd < 0) { + close(forw_fd); + close(rev_fd); + return FALSE; + } + + progbar = create_progress_dlg("Saving voice in a file", dest, "Stop", &stop_flag); + + /* First we write the .au header. XXX Hope this is endian independant */ + /* the magic word 0x2e736e64 == .snd */ + *pd = (unsigned char)0x2e; write(to_fd, pd, 1); + *pd = (unsigned char)0x73; write(to_fd, pd, 1); + *pd = (unsigned char)0x6e; write(to_fd, pd, 1); + *pd = (unsigned char)0x64; write(to_fd, pd, 1); + /* header offset == 24 bytes */ + *pd = (unsigned char)0x00; write(to_fd, pd, 1); + write(to_fd, pd, 1); + write(to_fd, pd, 1); + *pd = (unsigned char)0x18; write(to_fd, pd, 1); + /* total length, it is permited to set this to 0xffffffff */ + *pd = (unsigned char)0xff; write(to_fd, pd, 1); + write(to_fd, pd, 1); + write(to_fd, pd, 1); + write(to_fd, pd, 1); + /* encoding format == 8 bit ulaw */ + *pd = (unsigned char)0x00; write(to_fd, pd, 1); + write(to_fd, pd, 1); + write(to_fd, pd, 1); + *pd = (unsigned char)0x01; write(to_fd, pd, 1); + /* sample rate == 8000 Hz */ + *pd = (unsigned char)0x00; write(to_fd, pd, 1); + write(to_fd, pd, 1); + *pd = (unsigned char)0x1f; write(to_fd, pd, 1); + *pd = (unsigned char)0x40; write(to_fd, pd, 1); + /* channels == 1 */ + *pd = (unsigned char)0x00; write(to_fd, pd, 1); + write(to_fd, pd, 1); + write(to_fd, pd, 1); + *pd = (unsigned char)0x01; write(to_fd, pd, 1); + + switch (channels) { + /* only forward direction */ + case 1: { + progbar_count = user_data->forward.count; + progbar_quantum = user_data->forward.count/100; + while ((fread = read(forw_fd, &f_pd, 2)) > 0) { + if(stop_flag) + break; + if((count > progbar_nextstep) && (count <= progbar_count)) { + update_progress_dlg(progbar, + (gfloat) count/progbar_count, "Saving"); + progbar_nextstep = progbar_nextstep + progbar_quantum; + } + count++; + *pd = (unsigned char)linear2ulaw(f_pd); + fwritten = write(to_fd, pd, 1); + if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) { + close(forw_fd); + close(rev_fd); + close(to_fd); + destroy_progress_dlg(progbar); + return FALSE; + } + } + break; + } + /* only reversed direction */ + case 2: { + progbar_count = user_data->reversed.count; + progbar_quantum = user_data->reversed.count/100; + while ((rread = read(rev_fd, &r_pd, 2)) > 0) { + if(stop_flag) + break; + if((count > progbar_nextstep) && (count <= progbar_count)) { + update_progress_dlg(progbar, + (gfloat) count/progbar_count, "Saving"); + progbar_nextstep = progbar_nextstep + progbar_quantum; + } + count++; + *pd = (unsigned char)linear2ulaw(r_pd); + rwritten = write(to_fd, pd, 1); + if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) { + close(forw_fd); + close(rev_fd); + close(to_fd); + destroy_progress_dlg(progbar); + return FALSE; + } + } + break; + } + /* both directions */ + default: { + (user_data->forward.count > user_data->reversed.count) ? + (progbar_count = user_data->forward.count) : + (progbar_count = user_data->reversed.count); + progbar_quantum = progbar_count/100; + /* since conversation in one way can start later than in the other one, + * we have to write some silence information for one channel */ + if (user_data->forward.start_time > user_data->reversed.start_time) { + f_write_silence = + (user_data->forward.start_time-user_data->reversed.start_time)*8000; + } + else if (user_data->forward.start_time < user_data->reversed.start_time) { + r_write_silence = + (user_data->reversed.start_time-user_data->forward.start_time)*8000; + } + for(;;) { + if(stop_flag) + break; + if((count > progbar_nextstep) && (count <= progbar_count)) { + update_progress_dlg(progbar, + (gfloat) count/progbar_count, "Saving"); + progbar_nextstep = progbar_nextstep + progbar_quantum; + } + count++; + if(f_write_silence > 0) { + rread = read(rev_fd, &r_pd, 2); + f_pd = 0; + fread = 1; + f_write_silence--; + } + else if(r_write_silence > 0) { + fread = read(forw_fd, &f_pd, 2); + r_pd = 0; + rread = 1; + r_write_silence--; + } + else { + fread = read(forw_fd, &f_pd, 2); + rread = read(rev_fd, &r_pd, 2); + } + if ((rread == 0) && (fread == 0)) + break; + *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 ); + rwritten = write(to_fd, pd, 1); + if ((rwritten < 0) || (rread < 0) || (fread < 0)) { + close(forw_fd); + close(rev_fd); + close(to_fd); + destroy_progress_dlg(progbar); + return FALSE; + } + } + } + } + destroy_progress_dlg(progbar); + close(forw_fd); + close(rev_fd); + close(to_fd); + return TRUE; +} + + +/****************************************************************************/ +/* the user wants to save in a file */ +/* XXX support for different formats is currently commented out */ +static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_) +{ + gchar *g_dest; + /*GtkWidget *wav, *au, *sw;*/ + GtkWidget *rev, *forw, *both; + user_data_t *user_data; + gint channels /*, format*/; + + g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs))); + + /* Perhaps the user specified a directory instead of a file. + Check whether they did. */ + if (test_for_directory(g_dest) == EISDIR) { + /* It's a directory - set the file selection box to display it. */ + set_last_open_dir(g_dest); + g_free(g_dest); + gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir); + return; + } + + /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb"); + au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb"); + sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/ + rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb"); + forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb"); + both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb"); + user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data"); + + /* XXX user clicks the ok button, but we know we can't save the voice info because f.e. + * we don't support that codec. So we pop up a warning. Maybe it would be better to + * disable the ok button or disable the buttons for direction if only one is not ok. The + * problem is if we open the save voice dialog and then click the refresh button and maybe + * the state changes, so we can't save anymore. In this case we should be able to update + * the buttons. For now it is easier if we put the warning when the ok button is pressed. + */ + + /* we can not save in both dirctions */ + if ((user_data->forward.saved == FALSE) && (user_data->reversed.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) { + /* there are many combinations here, we just exit when first matches */ + if ((user_data->forward.error_type == TAP_RTP_WRONG_CODEC) || + (user_data->reversed.error_type == TAP_RTP_WRONG_CODEC)) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save in a file: Unsupported codec!"); + else if ((user_data->forward.error_type == TAP_RTP_WRONG_LENGTH) || + (user_data->reversed.error_type == TAP_RTP_WRONG_LENGTH)) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save in a file: Wrong length of captured packets!"); + else if ((user_data->forward.error_type == TAP_RTP_PADDING_ERROR) || + (user_data->reversed.error_type == TAP_RTP_PADDING_ERROR)) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save in a file: RTP data with padding!"); + else + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save in a file: File I/O problem!"); + return; + } + /* we can not save forward direction */ + else if ((user_data->forward.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) || + (GTK_TOGGLE_BUTTON (both)->active))) { + if (user_data->forward.error_type == TAP_RTP_WRONG_CODEC) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save forward direction in a file: Unsupported codec!"); + else if (user_data->forward.error_type == TAP_RTP_WRONG_LENGTH) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save forward direction in a file: Wrong length of captured packets!"); + else if (user_data->forward.error_type == TAP_RTP_PADDING_ERROR) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save forward direction in a file: RTP data with padding!"); + else + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save forward direction in a file: File I/O problem!"); + return; + } + /* we can not save reversed direction */ + else if ((user_data->reversed.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) || + (GTK_TOGGLE_BUTTON (both)->active))) { + if (user_data->reversed.error_type == TAP_RTP_WRONG_CODEC) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save reversed direction in a file: Unsupported codec!"); + else if (user_data->reversed.error_type == TAP_RTP_WRONG_LENGTH) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save reversed direction in a file: Wrong length of captured packets!"); + else if (user_data->reversed.error_type == TAP_RTP_PADDING_ERROR) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save reversed direction in a file: RTP data with padding!"); + else if (user_data->reversed.error_type == TAP_RTP_NO_DATA) + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save reversed direction in a file: No RTP data!"); + else + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "Can't save reversed direction in a file: File I/O problem!"); + return; + } + + /*if (GTK_TOGGLE_BUTTON (wav)->active) + format = 1; + else if (GTK_TOGGLE_BUTTON (au)->active) + format = 2; + else if (GTK_TOGGLE_BUTTON (sw)->active) + format = 3;*/ + + if (GTK_TOGGLE_BUTTON (rev)->active) + channels = 2; + else if (GTK_TOGGLE_BUTTON (both)->active) + channels = 3; + else + channels = 1; + + if(!copy_file(g_dest, channels/*, format*/, user_data)) { + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + "An error occured while saving voice in a file!"); + return; + } + + gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w)); +} + +/****************************************************************************/ +/* when the user wants to save the voice information in a file */ +/* XXX support for different formats is currently commented out */ +static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_) +{ + GtkWidget *vertb; + GtkWidget *table1; + GtkWidget *label_format; + GtkWidget *channels_label; + /*GSList *format_group = NULL;*/ + GSList *channels_group = NULL; + GtkWidget *forward_rb; + GtkWidget *reversed_rb; + GtkWidget *both_rb; + /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/ + GtkWidget *ok_bt; + + /* if we can't save in a file: wrong codec, cut packets or other errors */ + /* shold the error arise here or later when you click ok button ? + * if we do it here, then we must disable the refresh button, so we don't do it here */ + + if (user_data->dlg.save_voice_as_w != NULL) { + /* There's already a Save voice info dialog box; reactivate it. */ + reactivate_window(user_data->dlg.save_voice_as_w); + return; + } + + user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ..."); + gtk_signal_connect(GTK_OBJECT(user_data->dlg.save_voice_as_w), "destroy", + GTK_SIGNAL_FUNC(save_voice_as_destroy_cb), user_data); + + /* Container for each row of widgets */ + vertb = gtk_vbox_new(FALSE, 0); + gtk_container_border_width(GTK_CONTAINER(vertb), 5); + gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area), + vertb, FALSE, FALSE, 0); + gtk_widget_show (vertb); + + table1 = gtk_table_new (2, 4, FALSE); + gtk_widget_show (table1); + gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (table1), 10); + gtk_table_set_row_spacings (GTK_TABLE (table1), 20); + + label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) "); + gtk_widget_show (label_format); + gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + /* we support .au - ulaw*/ + /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav"); + format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb)); + gtk_widget_show (wav_rb); + gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit "); + format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb)); + gtk_widget_show (sw_rb); + gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + au_rb = gtk_radio_button_new_with_label (format_group, ".au"); + format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb)); + gtk_widget_show (au_rb); + gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + */ + + channels_label = gtk_label_new ("Channels:"); + gtk_widget_show (channels_label); + gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5); + + forward_rb = gtk_radio_button_new_with_label (channels_group, "forward "); + channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb)); + gtk_widget_show (forward_rb); + gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed"); + channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb)); + gtk_widget_show (reversed_rb); + gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + both_rb = gtk_radio_button_new_with_label (channels_group, "both"); + channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb)); + gtk_widget_show (both_rb); + gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE); + + /* if one direction is nok we don't allow saving + XXX this is not ok since the user can click the refresh button and cause changes + but we can not update this window. So we move all the decision on the time the ok + button is clicked + if (user_data->forward.saved == FALSE) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE); + gtk_widget_set_sensitive(forward_rb, FALSE); + gtk_widget_set_sensitive(both_rb, FALSE); + } + else if (user_data->reversed.saved == FALSE) { + gtk_widget_set_sensitive(reversed_rb, FALSE); + gtk_widget_set_sensitive(both_rb, FALSE); + } + */ + + ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button; + /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb); + OBJECT_SET_DATA(ok_bt, "au_rb", au_rb); + OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/ + OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb); + OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb); + OBJECT_SET_DATA(ok_bt, "both_rb", both_rb); + OBJECT_SET_DATA(ok_bt, "user_data", user_data); + + /* Connect the cancel_button to destroy the widget */ + SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, + "clicked", (GtkSignalFunc)gtk_widget_destroy, + user_data->dlg.save_voice_as_w); + + /* Catch the "key_press_event" signal in the window, so that we can catch + the ESC key being pressed and act as if the "Cancel" button had + been selected. */ + dlg_set_cancel(user_data->dlg.save_voice_as_w, GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button); + + gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked", + GTK_SIGNAL_FUNC(save_voice_as_ok_cb), user_data->dlg.save_voice_as_w); + + gtk_widget_show(user_data->dlg.save_voice_as_w); +} + + +/****************************************************************************/ +/* when we are finished with redisection, we add the label for the statistic */ +static void draw_stat(user_data_t *user_data) +{ + gchar label_max[200]; + guint32 f_expected = (user_data->forward.stop_seq_nr + user_data->forward.cycles*65536) + - user_data->forward.start_seq_nr + 1; + guint32 r_expected = (user_data->reversed.stop_seq_nr + user_data->reversed.cycles*65536) + - user_data->reversed.start_seq_nr + 1; + gint32 f_lost = f_expected - user_data->forward.total_nr; + gint32 r_lost = r_expected - user_data->reversed.total_nr; + + g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n\n" + "Total RTP packets = %u (expected %u) Lost RTP packets = %d" + " Sequence errors = %u", + user_data->forward.max_delay, user_data->forward.max_nr, user_data->forward.total_nr, + f_expected, f_lost, user_data->forward.sequence); + + gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max); + + g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n\n" + "Total RTP packets = %u (expected %u) Lost RTP packets = %d" + " Sequence errors = %u", + user_data->reversed.max_delay, user_data->reversed.max_nr, user_data->reversed.total_nr, + r_expected, r_lost, user_data->reversed.sequence); + + gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max); + + return ; +} + +/****************************************************************************/ +/* append a line to clist */ +static void add_to_clist(GtkWidget *clist, guint32 number, guint16 seq_num, + double delay, double jitter, gboolean status, gboolean marker, + gchar *timeStr, guint32 pkt_len) +{ + guint added_row; + gchar *data[8]; + gchar field[8][32]; + + data[0]=&field[0][0]; + data[1]=&field[1][0]; + data[2]=&field[2][0]; + data[3]=&field[3][0]; + data[4]=&field[4][0]; + data[5]=&field[5][0]; + data[6]=&field[6][0]; + data[7]=&field[7][0]; + + g_snprintf(field[0], 20, "%u", number); + g_snprintf(field[1], 20, "%u", seq_num); + g_snprintf(field[2], 20, "%f", delay); + g_snprintf(field[3], 20, "%f", jitter); + g_snprintf(field[4], 20, "%s", marker? "SET" : ""); + g_snprintf(field[5], 29, "%s", status? "OK" : "NOK - Wrong sequence nr."); + g_snprintf(field[6], 32, "%s", timeStr); + g_snprintf(field[7], 20, "%u", pkt_len); + + added_row = gtk_clist_append(GTK_CLIST(clist), data); + gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number)); +} + +/****************************************************************************/ +/* Create the dialog box with all widgets */ +void create_rtp_dialog(user_data_t* user_data) +{ + GtkWidget *window = NULL; + GtkWidget *clist_fwd; + GtkWidget *clist_rev; + GtkWidget *label_stats_fwd; + GtkWidget *label_stats_rev; + GtkWidget *notebook; + + GtkWidget *main_vb, *page, *page_r, *label, *label1, *label2, *label3; + GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/; + GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt; +#ifdef USE_CONVERSATION_GRAPH + GtkWidget *graph_bt; +#endif + + gchar *titles[8] = {"Packet", "Sequence", "Delay (s)", "Jitter (s)", "Marker", "Status", "Date", "Length"}; + gchar label_forward[150]; + gchar label_reverse[150]; + + gchar str_ip_src[16]; + gchar str_ip_dst[16]; + + + window = dlg_window_new("Ethereal: RTP Stream Analysis"); + gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); + gtk_signal_connect(GTK_OBJECT(window), "destroy", + GTK_SIGNAL_FUNC(on_destroy), user_data); + + /* Container for each row of widgets */ + main_vb = gtk_vbox_new(FALSE, 3); + gtk_container_border_width(GTK_CONTAINER(main_vb), 5); + gtk_container_add(GTK_CONTAINER(window), main_vb); + gtk_widget_show(main_vb); + + + /* Notebooks... */ + strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_fwd)); + strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_fwd)); + + g_snprintf(label_forward, 149, + "Analysing connection from %s port %u to %s port %u SSRC = %u\n", + str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd); + + strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_rev)); + strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_rev)); + + g_snprintf(label_reverse, 149, + "Analysing connection from %s port %u to %s port %u SSRC = %u\n", + str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev); + + /* Start a notebook for flipping between sets of changes */ + notebook = gtk_notebook_new(); + gtk_container_add(GTK_CONTAINER(main_vb), notebook); + gtk_object_set_data(GTK_OBJECT(window), "notebook", notebook); + + /* page for forward connection */ + page = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(page), 20); + + /* scrolled window */ + scrolled_window = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_usize(scrolled_window, 600, 200); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + + /* direction label */ + label1 = gtk_label_new(label_forward); + gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0); + + /* place for some statistics */ + label_stats_fwd = gtk_label_new("\n\n"); + gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 5); + + /* clist for the information */ + clist_fwd = gtk_clist_new_with_titles(8, titles); + gtk_widget_show(clist_fwd); + gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd); + gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0); + gtk_signal_connect(GTK_OBJECT (clist_fwd), "select_row", + GTK_SIGNAL_FUNC (on_clist_select_row), + user_data); + /* Hide date and length column */ + gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 6, FALSE); + gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE); + + /* label */ + label = gtk_label_new(" Forward Direction "); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); + + /* column width and justification */ + gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 80); + gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 80); + gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 80); + gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 80); + gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 40); + gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER); + + /* same page for reversed connection */ + page_r = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(page_r), 20); + scrolled_window_r = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_usize(scrolled_window_r, 600, 200); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + label3 = gtk_label_new(label_reverse); + gtk_box_pack_start(GTK_BOX(page_r), label3, FALSE, FALSE, 0); + label_stats_rev = gtk_label_new("\n\n"); + gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 5); + clist_rev = gtk_clist_new_with_titles(8, titles); + gtk_widget_show(clist_rev); + gtk_clist_set_column_visibility(GTK_CLIST(clist_rev), 6, FALSE); + gtk_clist_set_column_visibility(GTK_CLIST(clist_rev), 7, FALSE); + + gtk_signal_connect(GTK_OBJECT (clist_rev), "select_row", + GTK_SIGNAL_FUNC (on_clist_select_row), + user_data); + + gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev); + gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0); + label2 = gtk_label_new(" Reversed Direction "); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label2); + + gtk_clist_set_column_width(GTK_CLIST(clist_rev), 0, 80); + gtk_clist_set_column_width(GTK_CLIST(clist_rev), 1, 80); + gtk_clist_set_column_width(GTK_CLIST(clist_rev), 2, 80); + gtk_clist_set_column_width(GTK_CLIST(clist_rev), 3, 80); + gtk_clist_set_column_width(GTK_CLIST(clist_rev), 4, 40); + gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 0, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 1, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 2, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 3, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 4, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 5, GTK_JUSTIFY_CENTER); + + /* page for help&about or future + page_help = gtk_hbox_new(FALSE, 5); + label4 = gtk_label_new(" Future "); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label4); + frame = gtk_frame_new(""); + text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,..."); + gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT); + gtk_container_add(GTK_CONTAINER(frame), text); + gtk_container_set_border_width(GTK_CONTAINER(frame), 20); + gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0); + */ + + /* show all notebooks */ + gtk_widget_show_all(notebook); + + /* buttons */ + box4 = gtk_hbutton_box_new(); + gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(box4), 10); + gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE); + gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0); + gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0); + gtk_widget_show(box4); + + voice_bt = gtk_button_new_with_label("Save payload..."); + gtk_container_add(GTK_CONTAINER(box4), voice_bt); + gtk_widget_show(voice_bt); + gtk_signal_connect(GTK_OBJECT(voice_bt), "clicked", + GTK_SIGNAL_FUNC(on_save_bt_clicked), user_data); + + csv_bt = gtk_button_new_with_label("Save as CSV..."); + gtk_container_add(GTK_CONTAINER(box4), csv_bt); + gtk_widget_show(csv_bt); + gtk_signal_connect(GTK_OBJECT(csv_bt), "clicked", + GTK_SIGNAL_FUNC(save_csv_as_cb), user_data); + + refresh_bt = gtk_button_new_with_label("Refresh"); + gtk_container_add(GTK_CONTAINER(box4), refresh_bt); + gtk_widget_show(refresh_bt); + gtk_signal_connect(GTK_OBJECT(refresh_bt), "clicked", + GTK_SIGNAL_FUNC(on_refresh_bt_clicked), user_data); + + goto_bt = gtk_button_new_with_label("Go to frame"); + gtk_container_add(GTK_CONTAINER(box4), goto_bt); + gtk_widget_show(goto_bt); + gtk_signal_connect(GTK_OBJECT(goto_bt), "clicked", + GTK_SIGNAL_FUNC(on_goto_bt_clicked), user_data); + +#ifdef USE_CONVERSATION_GRAPH + graph_bt = gtk_button_new_with_label("Graph"); + gtk_container_add(GTK_CONTAINER(box4), graph_bt); + gtk_widget_show(graph_bt); + gtk_signal_connect(GTK_OBJECT(graph_bt), "clicked", + GTK_SIGNAL_FUNC(on_graph_bt_clicked), user_data); +#endif + + close_bt = gtk_button_new_with_label("Close"); + gtk_container_add(GTK_CONTAINER(box4), close_bt); + gtk_widget_show(close_bt); + gtk_signal_connect(GTK_OBJECT(close_bt), "clicked", + GTK_SIGNAL_FUNC(on_close_bt_clicked), user_data); + + gtk_widget_show(window); + + user_data->dlg.window = window; + user_data->dlg.clist_fwd = clist_fwd; + user_data->dlg.clist_rev = clist_rev; + user_data->dlg.label_stats_fwd = label_stats_fwd; + user_data->dlg.label_stats_rev = label_stats_rev; + user_data->dlg.notebook = notebook; + user_data->dlg.selected_clist = NULL; + user_data->dlg.selected_row = -1; +} + + +/****************************************************************************/ +static gboolean process_node(proto_item *ptree_node, header_field_info *hfinformation, + const gchar* proto_field, guint32* p_result) +{ + field_info *finfo; + proto_item *proto_sibling_node; + header_field_info *hfssrc; + ipv4_addr *ipv4; + + finfo = PITEM_FINFO(ptree_node); + + if (hfinformation==(finfo->hfinfo)) { + hfssrc = proto_registrar_get_byname((gchar*) proto_field); + if (hfssrc == NULL) + return FALSE; + for(ptree_node=g_node_first_child(ptree_node); ptree_node!=NULL; + ptree_node=g_node_next_sibling(ptree_node)) { + finfo=PITEM_FINFO(ptree_node); + if (hfssrc==finfo->hfinfo) { + if (hfinformation->type==FT_IPv4) { + ipv4 = fvalue_get(finfo->value); + *p_result = ipv4_get_net_order_addr(ipv4); + } + else { + *p_result = fvalue_get_integer(finfo->value); + } + return TRUE; + } + } + } + + proto_sibling_node = g_node_next_sibling(ptree_node); + + if (proto_sibling_node) { + return process_node(proto_sibling_node, hfinformation, proto_field, p_result); + } + else + return FALSE; +} + +/****************************************************************************/ +static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree, + const gchar* proto_name, + const gchar* proto_field, + guint32* p_result) +{ + proto_item *ptree_node; + header_field_info *hfinformation; + + hfinformation = proto_registrar_get_byname((gchar*) proto_name); + if (hfinformation == NULL) + return FALSE; + + ptree_node = g_node_first_child(protocol_tree); + if (!ptree_node) + return FALSE; + + return process_node(ptree_node, hfinformation, proto_field, p_result); +} + + +/****************************************************************************/ +/* XXX only handles RTP over IPv4, should add IPv6 support */ +void rtp_analysis( + guint32 ip_src_fwd, + guint16 port_src_fwd, + guint32 ip_dst_fwd, + guint16 port_dst_fwd, + guint32 ssrc_fwd, + guint32 ip_src_rev, + guint16 port_src_rev, + guint32 ip_dst_rev, + guint16 port_dst_rev, + guint32 ssrc_rev + ) +{ + user_data_t *user_data; + gchar filter_text[256]; + dfilter_t *sfcode; + GString *error_string; + + user_data = g_malloc(sizeof(user_data_t)); + + user_data->ip_src_fwd = ip_src_fwd; + user_data->port_src_fwd = port_src_fwd; + user_data->ip_dst_fwd = ip_dst_fwd; + user_data->port_dst_fwd = port_dst_fwd; + user_data->ssrc_fwd = ssrc_fwd; + user_data->ip_src_rev = ip_src_rev; + user_data->port_src_rev = port_src_rev; + user_data->ip_dst_rev = ip_dst_rev; + user_data->port_dst_rev = port_dst_rev; + user_data->ssrc_rev = ssrc_rev; + + create_rtp_dialog(user_data); + + /* Try to compile the filter. */ + strcpy(filter_text,"rtp && ip"); + if (!dfilter_compile(filter_text, &sfcode)) { + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg); + return; + } + + sprintf(filter_text,"rtp && ip && !icmp && (( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ) || ( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ))", + ip_to_str((ip_addr_p)&ip_src_fwd), + port_src_fwd, + ip_to_str((ip_addr_p)&ip_dst_fwd), + port_dst_fwd, + ip_to_str((ip_addr_p)&ip_src_rev), + port_src_rev, + ip_to_str((ip_addr_p)&ip_dst_rev), + port_dst_rev + ); + + error_string = register_tap_listener("rtp", user_data, filter_text, + (void*)rtp_reset, (void*)rtp_packet, (void*)rtp_draw); + if (error_string != NULL) { + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str); + g_string_free(error_string, TRUE); + g_free(user_data); + return; + /*exit(1);*/ + } + + /* file names for storing sound data */ + tmpnam(user_data->f_tempname); + tmpnam(user_data->r_tempname); + user_data->forward.fp = NULL; + user_data->reversed.fp = NULL; + user_data->dlg.save_voice_as_w = NULL; + user_data->dlg.save_csv_as_w = NULL; +#ifdef USE_CONVERSATION_GRAPH + user_data->dlg.graph_window = NULL; + user_data->series_fwd.value_pairs = NULL; + user_data->series_rev.value_pairs = NULL; +#endif + + redissect_packets(&cfile); + + draw_stat(user_data); +} + +/****************************************************************************/ +/* entry point from main menu */ +void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_) +{ + guint32 ip_src_fwd; + guint16 port_src_fwd; + guint32 ip_dst_fwd; + guint16 port_dst_fwd; + guint32 ssrc_fwd = 0; + guint32 ip_src_rev; + guint16 port_src_rev; + guint32 ip_dst_rev; + guint16 port_dst_rev; + guint32 ssrc_rev = 0; + + gchar filter_text[256]; + dfilter_t *sfcode; + capture_file *cf; + epan_dissect_t *edt; + gint err; + gboolean frame_matched; + frame_data *fdata; + GList *strinfo_list; + GList *filtered_list = NULL; + rtp_stream_info_t *strinfo; + guint nfound; + + /* Try to compile the filter. */ + strcpy(filter_text,"rtp && ip"); + if (!dfilter_compile(filter_text, &sfcode)) { + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg); + return; + } + /* we load the current file into cf variable */ + cf = &cfile; + fdata = cf->current_frame; + + /* we are on the selected frame now */ + if (fdata == NULL) + return; /* if we exit here it's an error */ + + /* dissect the current frame */ + if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err)) { + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, + file_read_error_message(err), cf->filename); + return; + } + edt = epan_dissect_new(TRUE, FALSE); + epan_dissect_prime_dfilter(edt, sfcode); + epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo); + frame_matched = dfilter_apply_edt(sfcode, edt); + + /* if it is not an rtp frame, show the rtpstream dialog */ + frame_matched = dfilter_apply_edt(sfcode, edt); + if (frame_matched != 1) { + rtpstream_dlg_show(rtpstream_get_info()->strinfo_list); + return; +/* + epan_dissect_free(edt); + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "You didn't choose a RTP packet!"); + return; +*/ + } + + /* ok, it is a RTP frame, so let's get the ip and port values */ + g_memmove(&ip_src_fwd, edt->pi.src.data, 4); + g_memmove(&ip_dst_fwd, edt->pi.dst.data, 4); + port_src_fwd = edt->pi.srcport; + port_dst_fwd = edt->pi.destport; + + /* assume the inverse ip/port combination for the reverse direction */ + g_memmove(&ip_src_rev, edt->pi.dst.data, 4); + g_memmove(&ip_dst_rev, edt->pi.src.data, 4); + port_src_rev = edt->pi.destport; + port_dst_rev = edt->pi.srcport; + + /* now we need the SSRC value of the current frame */ + if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) { + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "SSRC value couldn't be found!"); + return; + } + + /* search for reversed direction in the global rtp streams list */ + nfound = 0; + strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list); + while (strinfo_list) + { + strinfo = (rtp_stream_info_t*)(strinfo_list->data); + if (strinfo->src_addr==ip_src_fwd + && strinfo->src_port==port_src_fwd + && strinfo->dest_addr==ip_dst_fwd + && strinfo->dest_port==port_dst_fwd) + { + filtered_list = g_list_prepend(filtered_list, strinfo); + } + + if (strinfo->src_addr==ip_src_rev + && strinfo->src_port==port_src_rev + && strinfo->dest_addr==ip_dst_rev + && strinfo->dest_port==port_dst_rev) + { + ++nfound; + filtered_list = g_list_append(filtered_list, strinfo); + if (ssrc_rev==0) + ssrc_rev = strinfo->ssrc; + } + + strinfo_list = g_list_next(strinfo_list); + } + + /* if more than one reverse streams found, we let the user choose the right one */ + if (nfound>1) { + rtpstream_dlg_show(filtered_list); + return; + } + else { + rtp_analysis( + ip_src_fwd, + port_src_fwd, + ip_dst_fwd, + port_dst_fwd, + ssrc_fwd, + ip_src_rev, + port_src_rev, + ip_dst_rev, + port_dst_rev, + ssrc_rev + ); + } +} + +/****************************************************************************/ +static void +rtp_analysis_init(char *dummy _U_) +{ + rtp_analysis_cb(NULL, NULL); +} + +/****************************************************************************/ +void +register_tap_listener_rtp_analysis(void) +{ + register_ethereal_tap("rtp", rtp_analysis_init); +} + +void +register_tap_menu_rtp_analysis(void) +{ + register_tap_menu_item("Statistics/RTP Streams/Analyse...", + rtp_analysis_cb, NULL, NULL); +} diff --git a/gtk/rtp_analysis.h b/gtk/rtp_analysis.h new file mode 100644 index 0000000000..ae5afc571f --- /dev/null +++ b/gtk/rtp_analysis.h @@ -0,0 +1,50 @@ +/* rtp_analysis.h + * RTP analysis addition for ethereal + * + * $Id: rtp_analysis.h,v 1.1 2003/09/24 07:48:11 guy Exp $ + * + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff <lars.ruoff@gmx.net> + * + * based on tap_rtp.c + * Copyright 2003, Iskratel, Ltd, Kranj + * By Miha Jemec <m.jemec@iskratel.si> + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef RTP_ANALYSIS_H_INCLUDED +#define RTP_ANALYSIS_H_INCLUDED + +#include <glib.h> + +void rtp_analysis( + guint32 ip_src_fwd, /* network-order IPv4 address */ + guint16 port_src_fwd, + guint32 ip_dst_fwd, /* network-order IPv4 address */ + guint16 port_dst_fwd, + guint32 ssrc_fwd, + guint32 ip_src_rev, /* network-order IPv4 address */ + guint16 port_src_rev, + guint32 ip_dst_rev, /* network-order IPv4 address */ + guint16 port_dst_rev, + guint32 ssrc_rev + ); + +#endif /*RTP_ANALYSIS_H_INCLUDED*/ diff --git a/gtk/rtp_stream.c b/gtk/rtp_stream.c new file mode 100644 index 0000000000..b662cb6db3 --- /dev/null +++ b/gtk/rtp_stream.c @@ -0,0 +1,407 @@ +/* rtp_stream.c + * RTP streams summary addition for ethereal + * + * $Id: rtp_stream.c,v 1.1 2003/09/24 07:48:11 guy Exp $ + * + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff <lars.ruoff@gmx.net> + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "rtp_stream.h" +#include "rtp_stream_dlg.h" + +#include "globals.h" + +#include "tap.h" +#include "register.h" +#include "packet-rtp.h" + +#include "simple_dialog.h" + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + + +/****************************************************************************/ +/* the one and only global rtpstream_tapinfo_t structure */ +static rtpstream_tapinfo_t the_tapinfo_struct = + {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE}; + + +/****************************************************************************/ +/* GCompareFunc style comparison function for _rtp_stream_info */ +gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb) +{ + const struct _rtp_stream_info* a = aa; + const struct _rtp_stream_info* b = bb; + + if (a==b) + return 0; + if (a==NULL || b==NULL) + return 1; + if ((a->src_addr == b->src_addr) + && (a->src_port == b->src_port) + && (a->dest_addr == b->dest_addr) + && (a->dest_port == b->dest_port) + && (a->ssrc == b->ssrc) + && (a->pt == b->pt)) + return 0; + else + return 1; +} + + +/****************************************************************************/ +/* when there is a [re]reading of packet's */ +void rtpstream_reset(rtpstream_tapinfo_t *tapinfo _U_) +{ + 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; + } + + ++(tapinfo->launch_count); + + return; +} + +/****************************************************************************/ +/* redraw the output */ +void rtpstream_draw(rtpstream_tapinfo_t *tapinfo _U_) +{ +/* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments + gtk_signal_emit_by_name(top_level, "signal_rtpstream_update"); +*/ + rtpstream_dlg_update(the_tapinfo_struct.strinfo_list); + return; +} + + +/* +* rtpdump file format +* +* The file starts with the tool to be used for playing this file, +* the multicast/unicast receive address and the port. +* +* #!rtpplay1.0 224.2.0.1/3456\n +* +* This is followed by one binary header (RD_hdr_t) and one RD_packet_t +* structure for each received packet. All fields are in network byte +* order. We don't need the source IP address since we can do mapping +* based on SSRC. This saves (a little) space, avoids non-IPv4 +* problems and privacy/security concerns. The header is followed by +* the RTP/RTCP header and (optionally) the actual payload. +*/ + +#define RTPFILE_VERSION "1.0" + +/* +* Write a header to the current output file. +* The header consists of an identifying string, followed +* by a binary structure. +*/ +static void rtp_write_header(rtp_stream_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) */ + guint16 port; /* UDP port */ + guint16 padding; /* 2 padding bytes */ + + fprintf(file, "#!rtpplay%s %s/%d\n", RTPFILE_VERSION, + ip_to_str((guint8*) &strinfo->dest_addr), + strinfo->dest_port); + + start_sec = g_htonl(strinfo->start_sec); + start_usec = g_htonl(strinfo->start_usec); + source = strinfo->src_addr; /* already is in network order */ + port = g_htons(strinfo->src_port); + padding = 0; + + fwrite(&start_sec, 4, 1, file); + fwrite(&start_usec, 4, 1, file); + fwrite(&source, 4, 1, file); + fwrite(&port, 2, 1, file); + fwrite(&padding, 2, 1, file); +} + +/* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/ +static void rtp_write_sample(rtp_sample_t* sample, 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(sample->header.frame_length + 8); + plen = g_htons(sample->header.frame_length); + offset = g_htonl(sample->header.rec_time); + + fwrite(&length, 2, 1, file); + fwrite(&plen, 2, 1, file); + fwrite(&offset, 4, 1, file); + fwrite(sample->frame, sample->header.frame_length, 1, file); +} + + +/* utility function for writing a sample to file in RAS format */ +static +void ras_write_sample(rtp_sample_t* sample, FILE* file) +{ + rtp_sample_header_t net_order_header; + + net_order_header.rec_time = g_htonl(sample->header.rec_time); + net_order_header.frame_length = g_htons(sample->header.frame_length); + + fwrite(&(net_order_header.rec_time), 1, 4, file); + fwrite(&(net_order_header.frame_length), 1, 2, file); + fwrite(sample->frame, 1, sample->header.frame_length, file); +} + + +/****************************************************************************/ +/* whenever a RTP packet is seen by the tap listener */ +int rtpstream_packet(rtpstream_tapinfo_t *tapinfo _U_, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo _U_) +{ + rtp_stream_info_t tmp_strinfo; + rtp_stream_info_t *strinfo = NULL; + GList* list; + + rtp_sample_t sample; + + /* we ignore packets that are not displayed */ +/* + if (pinfo->fd->flags.passed_dfilter == 0) + return 0; +*/ + + /* gather infos on the stream this packet is part of */ + g_memmove(&(tmp_strinfo.src_addr), pinfo->src.data, 4); + tmp_strinfo.src_port = pinfo->srcport; + g_memmove(&(tmp_strinfo.dest_addr), pinfo->dst.data, 4); + tmp_strinfo.dest_port = pinfo->destport; + tmp_strinfo.ssrc = rtpinfo->info_sync_src; + tmp_strinfo.pt = rtpinfo->info_payload_type; + + if (tapinfo->mode == TAP_ANALYSE) { + /* check wether 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(&tmp_strinfo, (rtp_stream_info_t*)(list->data))==0) + { + strinfo = (rtp_stream_info_t*)(list->data); /*found!*/ + break; + } + list = g_list_next(list); + } + + /* not in the list? then create a new entry */ + if (!strinfo) { + tmp_strinfo.npackets = 0; + tmp_strinfo.first_frame_num = pinfo->fd->num; + tmp_strinfo.start_sec = pinfo->fd->abs_secs; + tmp_strinfo.start_usec = pinfo->fd->abs_usecs; + tmp_strinfo.tag_vlan_error = 0; + tmp_strinfo.tag_diffserv_error = 0; + tmp_strinfo.vlan_id = 0; + strinfo = g_malloc(sizeof(rtp_stream_info_t)); + *strinfo = tmp_strinfo; /* memberwise copy of struct */ + tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo); + } + + /* increment the packets counter for this stream */ + ++(strinfo->npackets); + + /* increment the packets counter of all streams */ + ++(tapinfo->npackets); + } + else if (tapinfo->mode == TAP_SAVE) { + if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) { +/* sample.header.rec_time = pinfo->fd->abs_secs*1000 + pinfo->fd->abs_usecs;*/ + sample.header.rec_time = + (pinfo->fd->abs_usecs + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000 + + (pinfo->fd->abs_secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000; + sample.header.frame_length = rtpinfo->info_data_len; + sample.frame = cfile.pd + pinfo->fd->pkt_len - rtpinfo->info_data_len; + rtp_write_sample(&sample, tapinfo->save_file); +/* ras_write_sample(&sample, tapinfo->save_file);*/ + } + } + else if (tapinfo->mode == TAP_MARK) { + if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0 + || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0) + { + mark_frame(&cfile, pinfo->fd); + } + } + + return 0; +} + +/****************************************************************************/ +/* scan for RTP streams */ +void rtpstream_scan() +{ + gboolean was_registered = the_tapinfo_struct.is_registered; + if (!the_tapinfo_struct.is_registered) + register_tap_listener_rtp_stream(); + + the_tapinfo_struct.mode = TAP_ANALYSE; + redissect_packets(&cfile); + + if (!was_registered) + remove_tap_listener_rtp_stream(); +} + + +/****************************************************************************/ +/* save rtp dump of stream_fwd */ +void rtpstream_save(rtp_stream_info_t* stream, const gchar *filename) +{ + gboolean was_registered = the_tapinfo_struct.is_registered; + /* open file for saving */ + the_tapinfo_struct.save_file = fopen(filename, "wb"); + if (the_tapinfo_struct.save_file==NULL) { + return; + } + + rtp_write_header(stream, the_tapinfo_struct.save_file); + + if (!the_tapinfo_struct.is_registered) + register_tap_listener_rtp_stream(); + + the_tapinfo_struct.mode = TAP_SAVE; + the_tapinfo_struct.filter_stream_fwd = stream; + redissect_packets(&cfile); + the_tapinfo_struct.mode = TAP_ANALYSE; + + if (!was_registered) + remove_tap_listener_rtp_stream(); + + /* XXX check for error at fclose? */ + fclose(the_tapinfo_struct.save_file); +} + + +/****************************************************************************/ +/* mark packets in stream_fwd or stream_rev */ +void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev) +{ + gboolean was_registered = the_tapinfo_struct.is_registered; + if (!the_tapinfo_struct.is_registered) + register_tap_listener_rtp_stream(); + + the_tapinfo_struct.mode = TAP_MARK; + the_tapinfo_struct.filter_stream_fwd = stream_fwd; + the_tapinfo_struct.filter_stream_rev = stream_rev; + redissect_packets(&cfile); + the_tapinfo_struct.mode = TAP_ANALYSE; + + if (!was_registered) + remove_tap_listener_rtp_stream(); +} + + +/****************************************************************************/ +const rtpstream_tapinfo_t* rtpstream_get_info() +{ + return &the_tapinfo_struct; +} + + +/****************************************************************************/ +/* TAP INTERFACE */ +/****************************************************************************/ + +/****************************************************************************/ +static void +rtpstream_init_tap(char *dummy _U_) +{ + /* XXX: never called? */ +} + + +/* XXX just copied from gtk/rpc_stat.c */ +void protect_thread_critical_region(void); +void unprotect_thread_critical_region(void); + +/****************************************************************************/ +void +remove_tap_listener_rtp_stream(void) +{ + if (the_tapinfo_struct.is_registered) { + protect_thread_critical_region(); + remove_tap_listener(&the_tapinfo_struct); + unprotect_thread_critical_region(); + + the_tapinfo_struct.is_registered = FALSE; + } +} + + +/****************************************************************************/ +void +register_tap_listener_rtp_stream(void) +{ + gchar filter_text[256]; + GString *error_string; + + if (!the_tapinfo_struct.is_registered) { + register_ethereal_tap("rtp", rtpstream_init_tap); + + sprintf(filter_text, "rtp && ip && !icmp"); + + error_string = register_tap_listener("rtp", &the_tapinfo_struct, + filter_text, + (void*)rtpstream_reset, (void*)rtpstream_packet, (void*)rtpstream_draw); + + if (error_string != NULL) { + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + the_tapinfo_struct.is_registered = TRUE; + } +} diff --git a/gtk/rtp_stream.h b/gtk/rtp_stream.h new file mode 100644 index 0000000000..3817268543 --- /dev/null +++ b/gtk/rtp_stream.h @@ -0,0 +1,140 @@ +/* rtp_stream.h + * RTP streams summary addition for ethereal + * + * $Id: rtp_stream.h,v 1.1 2003/09/24 07:48:11 guy Exp $ + * + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff <lars.ruoff@gmx.net> + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef RTP_STREAM_H_INCLUDED +#define RTP_STREAM_H_INCLUDED + +#include <glib.h> +#include <stdio.h> + +/****************************************************************************/ +/* type for storing rtp frame information */ +typedef struct st_rtp_sample_header { + guint32 rec_time; /* miliseconds since start of recording */ + guint16 frame_length; /* number of bytes in *frame */ +} rtp_sample_header_t; + +/* type for storing rtp frame information */ +typedef struct st_rtp_sample { + rtp_sample_header_t header; /* date and size */ + char *frame; /* data bytes */ +} rtp_sample_t; + +typedef rtp_sample_t* rtp_sample_p; + + +/* defines an rtp stream */ +typedef struct _rtp_stream_info { + guint32 src_addr; + guint16 src_port; + guint32 dest_addr; + guint16 dest_port; + guint32 ssrc; + guint8 pt; + guint32 npackets; + + guint32 first_frame_num; /* frame number of first frame */ + /* start of recording (GMT) of this stream */ + guint32 start_sec; /* seconds */ + guint32 start_usec; /* microseconds */ + gboolean tag_vlan_error; + gboolean tag_diffserv_error; + guint16 vlan_id; + +} rtp_stream_info_t; + + +/* tapping modes */ +typedef enum +{ + TAP_ANALYSE, + TAP_SAVE, + TAP_MARK +} tap_mode_t; + + +/* structure that holds the information about all detected streams */ +/* struct holding all information of the tap */ +typedef struct _rtpstream_tapinfo { + int nstreams; /* number of streams in the list */ + GList* strinfo_list; /* list with all streams */ + int npackets; /* total number of rtp packets of all streams */ + /* used while tapping. user shouldnt modify these */ + tap_mode_t mode; + rtp_stream_info_t* filter_stream_fwd; /* used as filter in some tap modes */ + rtp_stream_info_t* filter_stream_rev; /* used as filter in some tap modes */ + FILE* save_file; + guint32 launch_count; /* number of times the tap has been run */ + gboolean is_registered; /* if the tap listener is currently registered or not */ +} rtpstream_tapinfo_t; + + +/****************************************************************************/ +/* INTERFACE */ + +/* +* Registers the rtp_streams tap listener (if not already done). +* From that point on, the RTP streams list will be updated with every redissection. +* This function is also the entry point for the initialization routine of the tap system. +* So whenever rtp_stream.c is added to the list of ETHEREAL_TAP_SRCs, the tap will be registered on startup. +* If not, it will be registered on demand by the rtp_streams and rtp_analysis functions that need it. +*/ +void register_tap_listener_rtp_stream(void); + +/* +* Removes the rtp_streams tap listener (if not already done) +* From that point on, the RTP streams list won't be updated any more. +*/ +void remove_tap_listener_rtp_stream(void); + +/* +* Retrieves a constant reference to the unique info structure of the rtp_streams tap listener. +* The user should not modify the data pointed to. +*/ +const rtpstream_tapinfo_t* rtpstream_get_info(); + +/* +* Scans all packets for RTP streams and updates the RTP streams list. +* (redissects all packets) +*/ +void rtpstream_scan(); + +/* +* Saves an RTP stream as raw data stream with timestamp information for later RTP playback. +* (redissects all packets) +*/ +void rtpstream_save(rtp_stream_info_t* stream, const gchar *filename); + +/* +* Marks all packets belonging to either of stream_fwd or stream_rev. +* (both can be NULL) +* (redissects all packets) +*/ +void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev); + + +#endif /*RTP_STREAM_H_INCLUDED*/ diff --git a/gtk/rtp_stream_dlg.c b/gtk/rtp_stream_dlg.c new file mode 100644 index 0000000000..8811cf3892 --- /dev/null +++ b/gtk/rtp_stream_dlg.c @@ -0,0 +1,806 @@ +/* rtp_stream_dlg.c + * RTP streams summary addition for ethereal + * + * $Id: rtp_stream_dlg.c,v 1.1 2003/09/24 07:48:11 guy Exp $ + * + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff <lars.ruoff@gmx.net> + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "rtp_stream_dlg.h" +#include "rtp_stream.h" +#include "rtp_analysis.h" + +#include "globals.h" +#include "epan/filesystem.h" + +#include "menu.h" +#include "dlg_utils.h" +#include "ui_util.h" +#include "main.h" +#include "compat_macros.h" + +#include <string.h> + +extern GtkWidget *main_display_filter_widget; + + +/****************************************************************************/ +/* + * RTP Payload types + * Table B.2 / H.225.0 + * Also RFC 1890, and + * + * http://www.iana.org/assignments/rtp-parameters + */ +#define PT_PCMU 0 /* RFC 1890 */ +#define PT_1016 1 /* RFC 1890 */ +#define PT_G721 2 /* RFC 1890 */ +#define PT_GSM 3 /* RFC 1890 */ +#define PT_G723 4 /* From Vineet Kumar of Intel; see the Web page */ +#define PT_DVI4_8000 5 /* RFC 1890 */ +#define PT_DVI4_16000 6 /* RFC 1890 */ +#define PT_LPC 7 /* RFC 1890 */ +#define PT_PCMA 8 /* RFC 1890 */ +#define PT_G722 9 /* RFC 1890 */ +#define PT_L16_STEREO 10 /* RFC 1890 */ +#define PT_L16_MONO 11 /* RFC 1890 */ +#define PT_QCELP 12 /* Qualcomm Code Excited Linear Predictive coding? */ +#define PT_CN 13 /* RFC 3389 */ +#define PT_MPA 14 /* RFC 1890, RFC 2250 */ +#define PT_G728 15 /* RFC 1890 */ +#define PT_DVI4_11025 16 /* from Joseph Di Pol of Sun; see the Web page */ +#define PT_DVI4_22050 17 /* from Joseph Di Pol of Sun; see the Web page */ +#define PT_G729 18 +#define PT_CELB 25 /* RFC 2029 */ +#define PT_JPEG 26 /* RFC 2435 */ +#define PT_NV 28 /* RFC 1890 */ +#define PT_H261 31 /* RFC 2032 */ +#define PT_MPV 32 /* RFC 2250 */ +#define PT_MP2T 33 /* RFC 2250 */ +#define PT_H263 34 /* from Chunrong Zhu of Intel; see the Web page */ + +static const value_string rtp_payload_type_vals[] = +{ + { PT_PCMU, "ITU-T G.711 PCMU" }, + { PT_1016, "USA Federal Standard FS-1016" }, + { PT_G721, "ITU-T G.721" }, + { PT_GSM, "GSM 06.10" }, + { PT_G723, "ITU-T G.723" }, + { PT_DVI4_8000, "DVI4 8000 samples/s" }, + { PT_DVI4_16000, "DVI4 16000 samples/s" }, + { PT_LPC, "Experimental linear predictive encoding from Xerox PARC" }, + { PT_PCMA, "ITU-T G.711 PCMA" }, + { PT_G722, "ITU-T G.722" }, + { PT_L16_STEREO, "16-bit uncompressed audio, stereo" }, + { PT_L16_MONO, "16-bit uncompressed audio, monaural" }, + { PT_QCELP, "Qualcomm Code Excited Linear Predictive coding" }, + { PT_CN, "Comfort noise" }, + { PT_MPA, "MPEG-I/II Audio"}, + { PT_G728, "ITU-T G.728" }, + { PT_DVI4_11025, "DVI4 11025 samples/s" }, + { PT_DVI4_22050, "DVI4 22050 samples/s" }, + { PT_G729, "ITU-T G.729" }, + { PT_CELB, "Sun CellB video encoding" }, + { PT_JPEG, "JPEG-compressed video" }, + { PT_NV, "'nv' program" }, + { PT_H261, "ITU-T H.261" }, + { PT_MPV, "MPEG-I/II Video"}, + { PT_MP2T, "MPEG-II transport streams"}, + { PT_H263, "ITU-T H.263" }, + { 0, NULL }, +}; + + +typedef const guint8 * ip_addr_p; + +static const gchar FWD_LABEL_TEXT[] = "Select a forward stream with left mouse button"; +static const gchar REV_LABEL_TEXT[] = "Select a reverse stream with SHIFT + left mouse button"; + +/****************************************************************************/ +/* pointer to the one and only dialog window */ +static GtkWidget *rtp_stream_dlg = NULL; + +/* save as dialog box */ +static GtkWidget *rtpstream_save_dlg = NULL; +static GtkWidget *clist = NULL; +static GtkWidget *label_fwd = NULL; +static GtkWidget *label_rev = NULL; + +static rtp_stream_info_t* selected_stream_fwd = NULL; /* current selection */ +static rtp_stream_info_t* selected_stream_rev = NULL; /* current selection for reversed */ +static GList *last_list = NULL; + + +/****************************************************************************/ +/* append a line to clist */ +static void add_to_clist(rtp_stream_info_t* strinfo) +{ + gint added_row; + gchar *data[8]; + gchar field[8][30]; + + data[0]=&field[0][0]; + data[1]=&field[1][0]; + data[2]=&field[2][0]; + data[3]=&field[3][0]; + data[4]=&field[4][0]; + data[5]=&field[5][0]; + data[6]=&field[6][0]; + data[7]=&field[7][0]; + + g_snprintf(field[0], 20, "%s", ip_to_str((const guint8*)&(strinfo->src_addr))); + g_snprintf(field[1], 20, "%u", strinfo->src_port); + g_snprintf(field[2], 20, "%s", ip_to_str((const guint8*)&(strinfo->dest_addr))); + g_snprintf(field[3], 20, "%u", strinfo->dest_port); + g_snprintf(field[4], 20, "%u", strinfo->ssrc); + g_snprintf(field[5], 30, "%s", val_to_str(strinfo->pt, rtp_payload_type_vals, + "Unknown (%u)")); + g_snprintf(field[6], 20, "%u", strinfo->npackets); + /* XXX: Comment field is not used for the moment */ +/* g_snprintf(field[7], 20, "%s", "");*/ + + added_row = gtk_clist_append(GTK_CLIST(clist), data); + + /* set data pointer of last row to point to user data for that row */ + gtk_clist_set_row_data(GTK_CLIST(clist), added_row, strinfo); +} + +/****************************************************************************/ +static void save_stream_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_) +{ + /* Note that we no longer have a Save voice info dialog box. */ + rtpstream_save_dlg = NULL; +} + +/****************************************************************************/ +/* save in a file */ +static void save_stream_ok_cb(GtkWidget *ok_bt _U_, gpointer user_data _U_) +{ + gchar *g_dest; + + if (!selected_stream_fwd) + return; + + g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (rtpstream_save_dlg))); + + /* Perhaps the user specified a directory instead of a file. + Check whether they did. */ + if (test_for_directory(g_dest) == EISDIR) { + /* It's a directory - set the file selection box to display it. */ + set_last_open_dir(g_dest); + g_free(g_dest); + gtk_file_selection_set_filename(GTK_FILE_SELECTION(rtpstream_save_dlg), last_open_dir); + return; + } + + rtpstream_save(selected_stream_fwd, g_dest); + + gtk_widget_destroy(GTK_WIDGET(rtpstream_save_dlg)); +} + + +/****************************************************************************/ +/* CALLBACKS */ +/****************************************************************************/ +static void +rtpstream_on_destroy (GtkObject *object _U_, + gpointer user_data _U_) +{ + /* Is there a save voice window open? */ + if (rtpstream_save_dlg != NULL) + gtk_widget_destroy(rtpstream_save_dlg); + + /* Note that we no longer have a "RTP Analyse" dialog box. */ + rtp_stream_dlg = NULL; +} + + +/****************************************************************************/ +static void +rtpstream_on_unselect (GtkButton *button _U_, + gpointer user_data _U_) +{ + selected_stream_fwd = NULL; + selected_stream_rev = NULL; + gtk_clist_unselect_all(GTK_CLIST(clist)); + gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT); + gtk_label_set_text(GTK_LABEL(label_rev), REV_LABEL_TEXT); +} + + +/****************************************************************************/ +/* +static void +rtpstream_on_goto (GtkButton *button _U_, + gpointer user_data _U_) +{ + if (selected_stream_fwd) + { + goto_frame(&cfile, selected_stream_fwd->first_frame_num); + } +} +*/ + + +/****************************************************************************/ +static void +rtpstream_on_save (GtkButton *button _U_, + gpointer data _U_) +{ + rtpstream_tapinfo_t* tapinfo = data; + + GtkWidget *vertb; + GtkWidget *ok_bt; + + if (!selected_stream_fwd) + return; + + if (rtpstream_save_dlg != NULL) { + /* There's already a Save dialog box; reactivate it. */ + reactivate_window(rtpstream_save_dlg); + return; + } + + rtpstream_save_dlg = gtk_file_selection_new("Ethereal: Save selected stream in rtpdump ('-F dump') format"); + gtk_signal_connect(GTK_OBJECT(rtpstream_save_dlg), "destroy", + GTK_SIGNAL_FUNC(save_stream_destroy_cb), NULL); + + /* Container for each row of widgets */ + vertb = gtk_vbox_new(FALSE, 0); + gtk_container_border_width(GTK_CONTAINER(vertb), 5); + gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(rtpstream_save_dlg)->action_area), + vertb, FALSE, FALSE, 0); + gtk_widget_show (vertb); + + ok_bt = GTK_FILE_SELECTION(rtpstream_save_dlg)->ok_button; +/* OBJECT_SET_DATA(ok_bt, "user_data", tapinfo);*/ + + /* Connect the cancel_button to destroy the widget */ + SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(rtpstream_save_dlg)->cancel_button, + "clicked", (GtkSignalFunc)gtk_widget_destroy, + rtpstream_save_dlg); + + /* Catch the "key_press_event" signal in the window, so that we can catch + the ESC key being pressed and act as if the "Cancel" button had + been selected. */ + dlg_set_cancel(rtpstream_save_dlg, GTK_FILE_SELECTION(rtpstream_save_dlg)->cancel_button); + + gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked", + GTK_SIGNAL_FUNC(save_stream_ok_cb), tapinfo); + + gtk_widget_show(rtpstream_save_dlg); +} + + +/****************************************************************************/ +static void +rtpstream_on_mark (GtkButton *button _U_, + gpointer user_data _U_) +{ + if (selected_stream_fwd==NULL && selected_stream_rev==NULL) + return; + rtpstream_mark(selected_stream_fwd, selected_stream_rev); +} + + +#define MAX_FILTER_LENGTH 320 + +/****************************************************************************/ +static void +rtpstream_on_filter (GtkButton *button _U_, + gpointer user_data _U_) +{ + gchar filter_string[MAX_FILTER_LENGTH] = ""; + gchar filter_string_rev[MAX_FILTER_LENGTH] = ""; + + if (selected_stream_fwd==NULL && selected_stream_rev==NULL) + return; + + if (selected_stream_fwd) + { + g_snprintf(filter_string, MAX_FILTER_LENGTH, + "(ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u && rtp.ssrc==%u)", + ip_to_str((const guint8*)&(selected_stream_fwd->src_addr)), + selected_stream_fwd->src_port, + ip_to_str((const guint8*)&(selected_stream_fwd->dest_addr)), + selected_stream_fwd->dest_port, + selected_stream_fwd->ssrc); + + if (selected_stream_rev) + { + strcat(filter_string, " || "); + } + } + + if (selected_stream_rev) + { + g_snprintf(filter_string_rev, MAX_FILTER_LENGTH, + "(ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u && rtp.ssrc==%u)", + ip_to_str((const guint8*)&(selected_stream_rev->src_addr)), + selected_stream_rev->src_port, + ip_to_str((const guint8*)&(selected_stream_rev->dest_addr)), + selected_stream_rev->dest_port, + selected_stream_rev->ssrc); + strcat(filter_string, filter_string_rev); + } + + gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string); +/* + filter_packets(&cfile, filter_string); + rtpstream_dlg_update(rtpstream_get_info()->strinfo_list); +*/ +} + + +/****************************************************************************/ +static void +rtpstream_on_close (GtkButton *button _U_, + gpointer user_data _U_) +{ + gtk_grab_remove(rtp_stream_dlg); + gtk_widget_destroy(rtp_stream_dlg); +} + + +/****************************************************************************/ +static void +rtpstream_on_analyse (GtkButton *button _U_, + gpointer user_data _U_) +{ + guint32 ip_src_fwd = 0; + guint16 port_src_fwd = 0; + guint32 ip_dst_fwd = 0; + guint16 port_dst_fwd = 0; + guint32 ssrc_fwd = 0; + guint32 ip_src_rev = 0; + guint16 port_src_rev = 0; + guint32 ip_dst_rev = 0; + guint16 port_dst_rev = 0; + guint32 ssrc_rev = 0; + + if (selected_stream_fwd) { + ip_src_fwd = selected_stream_fwd->src_addr; + port_src_fwd = selected_stream_fwd->src_port; + ip_dst_fwd = selected_stream_fwd->dest_addr; + port_dst_fwd = selected_stream_fwd->dest_port; + ssrc_fwd = selected_stream_fwd->ssrc; + } + + if (selected_stream_rev) { + ip_src_rev = selected_stream_rev->src_addr; + port_src_rev = selected_stream_rev->src_port; + ip_dst_rev = selected_stream_rev->dest_addr; + port_dst_rev = selected_stream_rev->dest_port; + ssrc_rev = selected_stream_rev->ssrc; + } + + rtp_analysis( + ip_src_fwd, + port_src_fwd, + ip_dst_fwd, + port_dst_fwd, + ssrc_fwd, + ip_src_rev, + port_src_rev, + ip_dst_rev, + port_dst_rev, + ssrc_rev + ); +} + + +/****************************************************************************/ +/* This should be the callback function called upon a user-defined + * event "signal_rtpstream_update", but i didn't knoow how to do with GTK +static void +rtpstream_on_update (GtkButton *button _U_, + gpointer user_data _U_) +{ + rtpstream_dlg_update(rtpstream_get_info()->strinfo_list); +} +*/ + +/****************************************************************************/ +/* when the user selects a row in the stream list */ +static void +rtpstream_on_select_row(GtkCList *clist, + gint row _U_, + gint column _U_, + GdkEventButton *event _U_, + gpointer user_data _U_) +{ + gchar label_text[80]; + + /* update the labels */ + if (event->state & GDK_SHIFT_MASK) { + selected_stream_rev = gtk_clist_get_row_data(GTK_CLIST(clist), row); + g_snprintf(label_text, 80, "Reverse: %s:%u -> %s:%u, SSRC=%u", + ip_to_str((ip_addr_p)&selected_stream_rev->src_addr), + selected_stream_rev->src_port, + ip_to_str((ip_addr_p)&selected_stream_rev->dest_addr), + selected_stream_rev->dest_port, + selected_stream_rev->ssrc + ); + gtk_label_set_text(GTK_LABEL(label_rev), label_text); + } + else { + selected_stream_fwd = gtk_clist_get_row_data(GTK_CLIST(clist), row); + g_snprintf(label_text, 80, "Forward: %s:%u -> %s:%u, SSRC=%u", + ip_to_str((ip_addr_p)&selected_stream_fwd->src_addr), + selected_stream_fwd->src_port, + ip_to_str((ip_addr_p)&selected_stream_fwd->dest_addr), + selected_stream_fwd->dest_port, + selected_stream_fwd->ssrc + ); + gtk_label_set_text(GTK_LABEL(label_fwd), label_text); + } + +/* + gtk_widget_set_sensitive(save_bt, TRUE); + gtk_widget_set_sensitive(filter_bt, TRUE); + gtk_widget_set_sensitive(mark_bt, TRUE); +*/ + /* TODO: activate other buttons when implemented */ +} + + +/****************************************************************************/ +/* INTERFACE */ +/****************************************************************************/ + +static void rtpstream_dlg_create (void) +{ + /* these are global static now: + GtkWidget *clist = NULL; + GtkWidget *label_fwd = NULL; + GtkWidget *label_rev = NULL; + */ + GtkWidget *rtpstream_dlg_w; + GtkWidget *dialog_vbox1; + GtkWidget *vbox1; + GtkWidget *label10; + GtkWidget *scrolledwindow1; + GtkWidget *label2; + GtkWidget *label3; + GtkWidget *label4; + GtkWidget *label5; + GtkWidget *label6; + GtkWidget *label7; + GtkWidget *label8; +/* GtkWidget *label9;*/ + GtkWidget *dialog_action_area1; + GtkWidget *hbuttonbox2; +/* GtkWidget *bt_goto;*/ + GtkWidget *bt_unselect; + GtkWidget *bt_save; + GtkWidget *bt_frames; + GtkWidget *bt_filter; + GtkWidget *bt_analyse; + GtkWidget *bt_close; + + rtpstream_dlg_w = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (rtpstream_dlg_w), "Ethereal: RTP Streams"); + + dialog_vbox1 = GTK_DIALOG (rtpstream_dlg_w)->vbox; + gtk_widget_show (dialog_vbox1); + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_widget_ref (vbox1); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "vbox1", vbox1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (vbox1); + gtk_box_pack_start (GTK_BOX (dialog_vbox1), vbox1, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), 8); + + label10 = gtk_label_new ("Detected RTP streams. Choose one for forward and reverse direction for analysis"); + gtk_widget_ref (label10); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label10", label10, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label10); + gtk_box_pack_start (GTK_BOX (vbox1), label10, FALSE, FALSE, 0); + gtk_widget_set_usize (label10, -2, 32); + + scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_ref (scrolledwindow1); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "scrolledwindow1", scrolledwindow1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (scrolledwindow1); + gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow1, TRUE, TRUE, 0); + + clist = gtk_clist_new (7); /* defines number of columns */ + gtk_widget_ref (clist); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "clist", clist, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (clist); + gtk_container_add (GTK_CONTAINER (scrolledwindow1), clist); + gtk_widget_set_usize (clist, 640, 200); + gtk_clist_set_column_width (GTK_CLIST (clist), 0, 100); + gtk_clist_set_column_width (GTK_CLIST (clist), 1, 50); + gtk_clist_set_column_width (GTK_CLIST (clist), 2, 100); + gtk_clist_set_column_width (GTK_CLIST (clist), 3, 50); + gtk_clist_set_column_width (GTK_CLIST (clist), 4, 80); + gtk_clist_set_column_width (GTK_CLIST (clist), 5, 118); + gtk_clist_set_column_width (GTK_CLIST (clist), 6, 60); +/* gtk_clist_set_column_width (GTK_CLIST (clist), 7, 51);*/ + gtk_clist_column_titles_show (GTK_CLIST (clist)); + + label2 = gtk_label_new ("Src IP addr"); + gtk_widget_ref (label2); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label2", label2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label2); + gtk_clist_set_column_widget (GTK_CLIST (clist), 0, label2); + + label3 = gtk_label_new ("Src port"); + gtk_widget_ref (label3); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label3", label3, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label3); + gtk_clist_set_column_widget (GTK_CLIST (clist), 1, label3); + + label4 = gtk_label_new ("Dest IP addr"); + gtk_widget_ref (label4); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label4", label4, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label4); + gtk_clist_set_column_widget (GTK_CLIST (clist), 2, label4); + + label5 = gtk_label_new ("Dest port"); + gtk_widget_ref (label5); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label5", label5, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label5); + gtk_clist_set_column_widget (GTK_CLIST (clist), 3, label5); + + label6 = gtk_label_new ("SSRC"); + gtk_widget_ref (label6); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label6", label6, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label6); + gtk_clist_set_column_widget (GTK_CLIST (clist), 4, label6); + gtk_widget_set_usize (label6, 80, -2); + + label7 = gtk_label_new ("Payload"); + gtk_widget_ref (label7); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label7", label7, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label7); + gtk_clist_set_column_widget (GTK_CLIST (clist), 5, label7); + + label8 = gtk_label_new ("Packets"); + gtk_widget_ref (label8); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label8", label8, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label8); + gtk_clist_set_column_widget (GTK_CLIST (clist), 6, label8); +/* + label9 = gtk_label_new ("Comment"); + gtk_widget_ref (label9); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label9", label9, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label9); + gtk_clist_set_column_widget (GTK_CLIST (clist), 7, label9); +*/ + label_fwd = gtk_label_new (FWD_LABEL_TEXT); + gtk_widget_ref (label_fwd); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label_fwd", label_fwd, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label_fwd); + gtk_box_pack_start (GTK_BOX (vbox1), label_fwd, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label_fwd), GTK_JUSTIFY_LEFT); + + label_rev = gtk_label_new (REV_LABEL_TEXT); + gtk_widget_ref (label_rev); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label_rev", label_rev, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (label_rev); + gtk_box_pack_start (GTK_BOX (vbox1), label_rev, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label_rev), GTK_JUSTIFY_LEFT); + + dialog_action_area1 = GTK_DIALOG (rtpstream_dlg_w)->action_area; + gtk_widget_show (dialog_action_area1); + gtk_container_set_border_width (GTK_CONTAINER (dialog_action_area1), 10); + + hbuttonbox2 = gtk_hbutton_box_new (); + gtk_widget_ref (hbuttonbox2); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "hbuttonbox2", hbuttonbox2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (hbuttonbox2); + gtk_box_pack_start (GTK_BOX (dialog_action_area1), hbuttonbox2, FALSE, FALSE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox2), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox2), 0); + + bt_unselect = gtk_button_new_with_label ("Unselect"); + gtk_widget_ref (bt_unselect); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_unselect", bt_unselect, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (bt_unselect); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_unselect); + GTK_WIDGET_SET_FLAGS (bt_unselect, GTK_CAN_DEFAULT); +/* + bt_goto = gtk_button_new_with_label ("Go to Frame"); + gtk_widget_ref (bt_goto); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_goto", bt_goto, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (bt_goto); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_goto); + GTK_WIDGET_SET_FLAGS (bt_goto, GTK_CAN_DEFAULT); +*/ + bt_save = gtk_button_new_with_label ("Save as..."); + gtk_widget_ref (bt_save); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_save", bt_save, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (bt_save); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_save); + GTK_WIDGET_SET_FLAGS (bt_save, GTK_CAN_DEFAULT); + + bt_frames = gtk_button_new_with_label ("Mark frames"); + gtk_widget_ref (bt_frames); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_frames", bt_frames, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (bt_frames); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_frames); + GTK_WIDGET_SET_FLAGS (bt_frames, GTK_CAN_DEFAULT); + + bt_filter = gtk_button_new_with_label ("Set filter"); + gtk_widget_ref (bt_filter); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_filter", bt_filter, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (bt_filter); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_filter); + GTK_WIDGET_SET_FLAGS (bt_filter, GTK_CAN_DEFAULT); + + bt_analyse = gtk_button_new_with_label ("Analyse"); + gtk_widget_ref (bt_analyse); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_analyse", bt_analyse, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (bt_analyse); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_analyse); + GTK_WIDGET_SET_FLAGS (bt_analyse, GTK_CAN_DEFAULT); + + bt_close = gtk_button_new_with_label ("Close"); + gtk_widget_ref (bt_close); + gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_close", bt_close, + (GtkDestroyNotify) gtk_widget_unref); + gtk_widget_show (bt_close); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_close); + GTK_WIDGET_SET_FLAGS (bt_close, GTK_CAN_DEFAULT); + + gtk_signal_connect (GTK_OBJECT (rtpstream_dlg_w), "destroy", + GTK_SIGNAL_FUNC (rtpstream_on_destroy), + NULL); + gtk_signal_connect (GTK_OBJECT (clist), "select_row", + GTK_SIGNAL_FUNC (rtpstream_on_select_row), + NULL); + gtk_signal_connect (GTK_OBJECT (bt_unselect), "clicked", + GTK_SIGNAL_FUNC (rtpstream_on_unselect), + NULL); +/* + gtk_signal_connect (GTK_OBJECT (bt_goto), "clicked", + GTK_SIGNAL_FUNC (rtpstream_on_goto), + NULL); +*/ + gtk_signal_connect (GTK_OBJECT (bt_save), "clicked", + GTK_SIGNAL_FUNC (rtpstream_on_save), + NULL); + gtk_signal_connect (GTK_OBJECT (bt_frames), "clicked", + GTK_SIGNAL_FUNC (rtpstream_on_mark), + NULL); + gtk_signal_connect (GTK_OBJECT (bt_filter), "clicked", + GTK_SIGNAL_FUNC (rtpstream_on_filter), + NULL); + gtk_signal_connect (GTK_OBJECT (bt_analyse), "clicked", + GTK_SIGNAL_FUNC (rtpstream_on_analyse), + NULL); + gtk_signal_connect (GTK_OBJECT (bt_close), "clicked", + GTK_SIGNAL_FUNC (rtpstream_on_close), + NULL); +/* XXX: see rtpstream_on_update for comment + gtk_signal_connect (GTK_OBJECT (top_level), "signal_rtpstream_update", + GTK_SIGNAL_FUNC (rtpstream_on_update), + NULL); +*/ + + if (clist) { + gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 2, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 3, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 4, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 5, GTK_JUSTIFY_LEFT); + gtk_clist_set_column_justification(GTK_CLIST(clist), 6, GTK_JUSTIFY_RIGHT); +/* gtk_clist_set_column_justification(GTK_CLIST(clist), 7, GTK_JUSTIFY_CENTER);*/ + } + + rtpstream_on_unselect(NULL, NULL); + + rtp_stream_dlg = rtpstream_dlg_w; +} + + +/****************************************************************************/ +/* PUBLIC */ +/****************************************************************************/ + +/****************************************************************************/ +/* update the contents of the dialog box clist */ +/* list: pointer to list of rtp_stream_info_t* */ +void rtpstream_dlg_update(GList *list) +{ + if (rtp_stream_dlg != NULL) { + gtk_clist_clear(GTK_CLIST(clist)); + + list = g_list_first(list); + while (list) + { + add_to_clist((rtp_stream_info_t*)(list->data)); + list = g_list_next(list); + } + + rtpstream_on_unselect(NULL, NULL); + } + + last_list = list; +} + + +/****************************************************************************/ +/* update the contents of the dialog box clist */ +/* list: pointer to list of rtp_stream_info_t* */ +void rtpstream_dlg_show(GList *list) +{ + if (rtp_stream_dlg != NULL) { + /* There's already a dialog box; reactivate it. */ + reactivate_window(rtp_stream_dlg); + /* Another list since last call? */ + if (list != last_list) { + rtpstream_dlg_update(list); + } + } + else { + /* Create and show the dialog box */ + rtpstream_dlg_create(); + rtpstream_dlg_update(list); + gtk_widget_show(rtp_stream_dlg); + } +} + + +/****************************************************************************/ +/* entry point when called via the GTK menu */ +void rtpstream_launch(GtkWidget *w _U_, gpointer data _U_) +{ + /* Show the dialog box */ + rtpstream_dlg_show(rtpstream_get_info()->strinfo_list); +} + +/****************************************************************************/ +void +register_tap_menu_rtp_stream(void) +{ + register_tap_menu_item("Statistics/RTP Streams/Show All...", + rtpstream_launch, NULL, NULL); +} diff --git a/gtk/rtp_stream_dlg.h b/gtk/rtp_stream_dlg.h new file mode 100644 index 0000000000..69f7dd9cee --- /dev/null +++ b/gtk/rtp_stream_dlg.h @@ -0,0 +1,45 @@ +/* rtp_stream_dlg.h + * RTP streams summary addition for ethereal + * + * $Id: rtp_stream_dlg.h,v 1.1 2003/09/24 07:48:11 guy Exp $ + * + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff <lars.ruoff@gmx.net> + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef RTP_STREAM_DLG_H_INCLUDED +#define RTP_STREAM_DLG_H_INCLUDED + +#include <gtk/gtk.h> + +/* +* Create or reactivate the rtp streams dialog box. +* list: pointer to list of rtp_stream_info_t* +*/ +void rtpstream_dlg_show(GList *list); + +/* +* Update the contents of the dialog box clist with that of list. +* list: pointer to list of rtp_stream_info_t* +*/ +void rtpstream_dlg_update(GList *list); + +#endif /*RTP_STREAM_DLG_H_INCLUDED*/ diff --git a/gtk/tap_rtp.c b/gtk/tap_rtp.c deleted file mode 100644 index 0682b5400f..0000000000 --- a/gtk/tap_rtp.c +++ /dev/null @@ -1,1761 +0,0 @@ -/* - * tap_rtp.c - * - * $Id: tap_rtp.c,v 1.17 2003/09/19 07:24:39 guy Exp $ - * - * RTP analysing addition for ethereal - * - * Copyright 2003, Iskratel, Ltd, Kranj - * By Miha Jemec <m.jemec@iskratel.si> - * - * Ethereal - Network traffic analyzer - * By Gerald Combs <gerald@ethereal.com> - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * This tap works as follows: - * When the user clicks on the RTP analisys button, we first check if it is a RTP packet. - * If yes we store the SSRC, ip, and port values. Then the tap is registered and the - * redissect_packets() routine is called. So we go through all the RTP packets and search - * for SSRC of reversed connection (it has inversed socket parameters). If more than one - * is found a window is displayed where the user can select the appropriate from the list. - * Rigth now we have the information about the converstion we are looking for (both SSRC). - * The redissect_packets() routine is called again. This time whenever a RTP packet with - * matching SSRC values arrives, we store all the information we need (number, sequence - * number, arrival time, ...) and compute the delay, jitter and wrong sequence number. - * We add this values to CList. If the RTP packet carries voice in g711 alaw or ulaw, we - * also store this voice information in a temp file. Window is displayed. - * Then three buttons are available: Close, Refresh and Save voice. - * The Refresh button calls the redissect_packets() routine again. It goes through the packets - * again and does all the calculation again (if capturing in real time this means that some - * more packets could come and can be computed in statistic). It also writes the sound - * data again. - * The Save voice button opens the dialog where we can choose the file name, format (not yet) - * and direction we want to save. Currently it works only with g711 alaw and ulaw, and if the - * length of captured packets is equal the length of packets on wire - * - * To do: - * - Support for saving voice in more different formats and with more different codecs: - * Since this should be portable to all OS, there is only possibility to save the - * voice in a file and not play it directly through the sound card. There are enough - * players on all platforms, that are doing right this. What about the format? - * Currently there is only support for saving as an .au file (ulaw, 8000 Hz, 8bit) - * There are many players for this format on all platforms (for example Windows Media Player - * under Windows, command play under Linux). Support will be added for wav format and - * possibility to save with two channels (separate channel for each direction) - * - * - Support for more codecs. Now you can save voice only if the codec is g.711 alaw or ulaw. - * - * - right now, the reversed connection must have the same (only inversed) ip and port numbers. - * I think that there is no reason that in special cases the reversed connection would not use - * some different port or even the IP combination (please correct me if I am wrong). - * So this will be added soon. - * - * - some more statistics (delay and jitter distribution) - * - * - GTK2 implementation - * - * - grammar correction - * - * - some more testing (other OS) - * - * XXX Problems: - * - * - problem with statistics for lost (late, duplicated) packets. How to make the statistic - * more resistant to special (bizarre) arrival of sequence numbers - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <stdio.h> - -#ifdef HAVE_SYS_TYPES_H -# include <sys/types.h> -#endif - -#include <gtk/gtk.h> -#include "globals.h" -#include <string.h> -#include "epan/packet_info.h" -#include <epan/epan_dissect.h> -#include <epan/filesystem.h> -#include "../tap.h" -#include "../register.h" -#include "../packet-rtp.h" -#include "file_dlg.h" -#include "dlg_utils.h" -#include "ui_util.h" -#include "simple_dialog.h" -#include "menu.h" -#include "main.h" -#include <math.h> -#include "progress_dlg.h" -#include "compat_macros.h" -#include "../g711.h" -#include "../util.h" -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#include <fcntl.h> - -#ifdef HAVE_IO_H -#include <io.h> /* open/close on win32 */ -#endif - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -static GtkWidget *rtp_w = NULL; -static GtkWidget *save_voice_as_w = NULL; -static GtkWidget *save_csv_as_w = NULL; -static GtkWidget *main_vb; -static GtkWidget *clist; -static GtkWidget *clist_r; -static GtkWidget *max; -static GtkWidget *max_r; - -static char f_tempname[128+1], r_tempname[128+1]; - -/* type of error when saving voice in a file didn't succeed */ -typedef enum { - TAP_RTP_WRONG_CODEC, - TAP_RTP_WRONG_LENGTH, - TAP_RTP_PADDING_ERROR, - TAP_RTP_FILE_OPEN_ERROR, - TAP_RTP_NO_DATA -} error_type_t; - -typedef enum { - FIRST_PACKET, - MARK_SET, - NORMAL_PACKET -} packet_type; - -/* structure that holds the information about the forward and reversed connection */ -struct _info_direction { - gboolean first_packet; - guint16 seq_num; - guint32 timestamp; - guint32 delta_timestamp; - double delay; - double jitter; - double time; - double start_time; - double max_delay; - guint32 max_nr; - guint16 start_seq_nr; - guint16 stop_seq_nr; - guint32 total_nr; - guint32 sequence; - gboolean under; - gint cycles; - FILE *fp; - guint32 count; - error_type_t error_type; - gboolean saved; -}; - -/* structure that holds general information about the connection - * and structures for both directions */ -typedef struct _info_stat { - gchar source[16]; - gchar destination[16]; - guint16 srcport; - guint16 dstport; - guint32 ssrc_forward; - guint32 ssrc_reversed; - guint32 *ssrc_tmp; - gboolean search_ssrc; - guint reversed_ip; - guint reversed_ip_and_port; - struct _info_direction forward; - struct _info_direction reversed; -} info_stat; - -int do_calculation(gboolean direc, packet_type pkt_type, void *ptrs, void *vpri, void *vpinfo); -static gboolean copy_file(gchar *, /*gint,*/ gint, void *); - -/* when there is a [re]reading of packet's */ -static void -rtp_reset(void *prs) -{ - info_stat *rs=prs; - - rs->forward.first_packet = TRUE; - rs->reversed.first_packet = TRUE; - rs->forward.max_delay = 0; - rs->reversed.max_delay = 0; - rs->forward.delay = 0; - rs->reversed.delay = 0; - rs->forward.jitter = 0; - rs->reversed.jitter = 0; - rs->forward.timestamp = 0; - rs->reversed.timestamp = 0; - rs->forward.max_nr = 0; - rs->reversed.max_nr = 0; - rs->forward.total_nr = 0; - rs->reversed.total_nr = 0; - rs->forward.sequence = 0; - rs->reversed.sequence = 0; - rs->forward.start_seq_nr = 0; - rs->reversed.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */ - rs->forward.stop_seq_nr = 0; - rs->reversed.stop_seq_nr = 0; - rs->forward.cycles = 0; - rs->reversed.cycles = 0; - rs->forward.under = FALSE; - rs->reversed.under = FALSE; - rs->forward.saved = FALSE; - rs->reversed.saved = FALSE; - rs->forward.start_time = 0; - rs->reversed.start_time = 0; - rs->forward.time = 0; - rs->reversed.time = 0; - rs->forward.count = 0; - rs->reversed.count = 0; - /* XXX check for error at fclose? */ - rs->forward.fp = freopen(f_tempname, "wb", rs->forward.fp); - if (rs->forward.fp == NULL) - rs->forward.error_type = TAP_RTP_FILE_OPEN_ERROR; - rs->reversed.fp = freopen(r_tempname, "wb", rs->reversed.fp); - if (rs->reversed.fp == NULL) - rs->reversed.error_type = TAP_RTP_FILE_OPEN_ERROR; - return; -} - -/* here we can redraw the output */ -/* not used yet */ -static void rtp_draw(void *prs _U_) -{ - return; -} - -/* when we are finished with redisection, we add the label for the statistic */ -static void draw_stat(void *prs) -{ - info_stat *rs=prs; - gchar label_max[200]; - guint32 f_expected = (rs->forward.stop_seq_nr + rs->forward.cycles*65536) - - rs->forward.start_seq_nr + 1; - guint32 r_expected = (rs->reversed.stop_seq_nr + rs->reversed.cycles*65536) - - rs->reversed.start_seq_nr + 1; - gint32 f_lost = f_expected - rs->forward.total_nr; - gint32 r_lost = r_expected - rs->reversed.total_nr; - - g_snprintf(label_max, 199, "Max delay = %f sec at packet nr. %u \n\n" - "Total RTP packets = %u (expected %u) Lost RTP packets = %d" - " Sequence error = %u", - rs->forward.max_delay, rs->forward.max_nr, rs->forward.total_nr, - f_expected, f_lost, rs->forward.sequence); - - gtk_label_set_text(GTK_LABEL(max), label_max); - - g_snprintf(label_max, 199, "Max delay = %f sec at packet nr. %u \n\n" - "Total RTP packets = %u (expected %u) Lost RTP packets = %d" - " Sequence error = %u", - rs->reversed.max_delay, rs->reversed.max_nr, rs->reversed.total_nr, - r_expected, r_lost, rs->reversed.sequence); - - gtk_label_set_text(GTK_LABEL(max_r), label_max); - - /* could be done somewhere else, but can be here as well */ - /* if this is true, then we don't have any reversed connection, so the error type - * will be no data. This applies only the reversed connection */ - if (rs->reversed_ip_and_port == 0) - rs->reversed.error_type = TAP_RTP_NO_DATA; - - return ; -} - -/* append a line to clist */ -/* XXX is there a nicer way to make these assignements? */ -static void add_to_clist(gboolean forward, guint32 number, guint16 seq_num, - double delay, double jitter, gboolean status, gboolean marker, gchar *timeStr, guint32 pkt_len) -{ - gchar *data[8]; - gchar field[8][30]; - - data[0]=&field[0][0]; - data[1]=&field[1][0]; - data[2]=&field[2][0]; - data[3]=&field[3][0]; - data[4]=&field[4][0]; - data[5]=&field[5][0]; - data[6]=&field[6][0]; - data[7]=&field[7][0]; - - g_snprintf(field[0], 20, "%u", number); - g_snprintf(field[1], 20, "%u", seq_num); - g_snprintf(field[2], 20, "%f", delay); - g_snprintf(field[3], 20, "%f", jitter); - g_snprintf(field[4], 20, "%s", marker? "SET" : ""); - g_snprintf(field[5], 29, "%s", status? "OK" : "NOK - Wrong sequence nr."); - g_snprintf(field[6], 32, "%s", timeStr); - g_snprintf(field[7], 20, "%u", pkt_len); - - gtk_clist_append(GTK_CLIST(forward? clist : clist_r), data); - -} - -/* whenever a RTP packet is seen by the tap listener */ -/* this function works as follows: - * 1) packets that are not displayed are ignored - * return - * 2) are we searching what could be the reversed connection (looking for reversed SSRC) - * if yes, do the parameters match (inversed IP and port combination from the forward one)? - * if yes, do we already have this SSRC stored - * if not store it - * 3) if not, is current packet matching the forward direction - * if yes, call the function that does the calculation and saves the voice info - * 4) if not, is current packet matching the reversed connection - * if yes, call the function that does the calculation and saves the voice info - */ -static int rtp_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, void *vpri) -{ - info_stat *rs=prs; - struct _rtp_info *pri=vpri; - guint i; - - /* we ignore packets that are not displayed */ - if (pinfo->fd->flags.passed_dfilter == 0) - return 0; - - /* are we looking for the SSRC of the reversed connection? */ - if (rs->search_ssrc != FALSE) { - /* XXX what should be the rules for reversed connection? - * 1. It should should have same inversed IP and port numbers - * 2. If none are found, only inversed IP's - is this possible? - * 3. If none are found, there isn't any reversed connection - * XXX is it possible that the conversation is not P2P? - * Curretly it works only if it matches the number 1. */ - - /* have we found inverse parameters? */ - if ( strcmp(ip_to_str(pinfo->src.data), rs->destination) == 0 && - strcmp( ip_to_str(pinfo->dst.data), rs->source) == 0 ) { - - /* do the ports also match? */ - if ((rs->srcport == pinfo->destport) && (rs->dstport == pinfo->srcport)) { - /* ok, the ip and port combination does match - * do we already have this ssrc stored */ - for(i=0; i< rs->reversed_ip_and_port; i++) { - if (pri->info_sync_src == *(rs->ssrc_tmp+i) ) - return 0; - } - - /* no, we found new ssrc, let's store it */ - rs->ssrc_tmp = (guint32*)g_realloc(rs->ssrc_tmp, - (i+1)*sizeof(guint32)); - *(rs->ssrc_tmp+i) = pri->info_sync_src; - rs->reversed_ip_and_port++; - return 0; - } - /* no, only ip addresses match */ - /* XXX not implemented yet */ - else { - rs->reversed_ip++; - return 0; - } - - } - } - - /* ok, we are not looking for SSRC of the reversed connection */ - /* is it the forward direction? */ - else if (rs->ssrc_forward == pri->info_sync_src) { - if (rs->forward.first_packet != FALSE) - /* first argument is the direction TRUE == forward */ - return do_calculation(TRUE, FIRST_PACKET, &rs->forward, pri, pinfo); - else if (pri->info_marker_set != FALSE) - return do_calculation(TRUE, MARK_SET, &rs->forward, pri, pinfo); - else - return do_calculation(TRUE, NORMAL_PACKET, &rs->forward, pri, pinfo); - } - /* is it the reversed direction? */ - else if (rs->ssrc_reversed == pri->info_sync_src) { - if (rs->reversed.first_packet != FALSE) - return do_calculation(FALSE, FIRST_PACKET, &rs->reversed, pri, pinfo); - else if (pri->info_marker_set != FALSE) - return do_calculation(FALSE, MARK_SET, &rs->reversed, pri, pinfo); - else - return do_calculation(FALSE, NORMAL_PACKET, &rs->reversed, pri, pinfo); - } - - return 0; -} - - -int do_calculation(gboolean direc, packet_type pkt_type, void *ptrs, void *vpri, void *vpinfo) { - - struct _info_direction *ptr=ptrs; - struct _rtp_info *pri=vpri; - packet_info *pinfo = vpinfo; - guint i; - double current_time; - double current_jitter; - guint8 *data; - gint16 tmp; - guint16 msecs; - gchar timeStr[32]; - - struct tm *tm_tmp; - time_t then; - - then = pinfo->fd->abs_secs; - msecs = (guint16)(pinfo->fd->abs_usecs/1000); - - tm_tmp = localtime(&then); - snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d", - tm_tmp->tm_mon + 1, - tm_tmp->tm_mday, - tm_tmp->tm_year + 1900, - tm_tmp->tm_hour, - tm_tmp->tm_min, - tm_tmp->tm_sec, - msecs); - /* store the current time and calculate the current jitter */ - current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000; - current_jitter = ptr->jitter + ( fabs (current_time - (ptr->time) - - ((double)(pri->info_timestamp)-(double)(ptr->timestamp))/8000)- ptr->jitter)/16; - ptr->delay = current_time-(ptr->time); - - /* We have 3 possibilities: - * is this the first packet we got in this direction? */ - if (pkt_type == FIRST_PACKET) { - ptr->first_packet = FALSE; - ptr->start_seq_nr = pri->info_seq_num; - ptr->start_time = current_time; - add_to_clist(direc, pinfo->fd->num, pri->info_seq_num, 0, - pri->info_marker_set? TRUE: FALSE, TRUE, FALSE, - timeStr, pinfo->fd->pkt_len); - if (ptr->fp == NULL) { - ptr->saved = FALSE; - ptr->error_type = TAP_RTP_FILE_OPEN_ERROR; - } - else - ptr->saved = TRUE; - } - /* or is it a packet with the mark bit set? */ - else if (pkt_type == MARK_SET) { - ptr->delta_timestamp = pri->info_timestamp - ptr->timestamp; - add_to_clist(direc, pinfo->fd->num, pri->info_seq_num, current_time - (ptr->time), - current_jitter, ptr->seq_num+1 == pri->info_seq_num? TRUE: FALSE, TRUE, timeStr, pinfo->fd->pkt_len); - } - /* if neither then it is a "normal" packet pkt_type == NORMAL_PACKET */ - else { - if (ptr->delay > ptr->max_delay) { - ptr->max_delay = ptr->delay; - ptr->max_nr = pinfo->fd->num; - } - add_to_clist(direc, pinfo->fd->num, pri->info_seq_num, current_time -(ptr->time), - current_jitter , ptr->seq_num+1 == pri->info_seq_num?TRUE:FALSE, FALSE, timeStr, pinfo->fd->pkt_len); - } - - /* 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 ((pri->info_seq_num < ptr->start_seq_nr) && (ptr->under == FALSE)){ - ptr->cycles++; - ptr->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 ((pri->info_seq_num == 0) && (ptr->stop_seq_nr == 65535) && - (ptr->under == FALSE)){ - ptr->cycles++; - ptr->under = TRUE; - } - /* the whole round is over, so reset the flag */ - else if ((pri->info_seq_num > ptr->start_seq_nr) && (ptr->under != FALSE)) { - ptr->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 ( ( ptr->seq_num+1 == pri->info_seq_num) || (pkt_type == FIRST_PACKET) ) - ptr->seq_num = pri->info_seq_num; - /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */ - else if ( (ptr->seq_num == 65535) && (pri->info_seq_num == 0) ) - ptr->seq_num = pri->info_seq_num; - /* lost packets */ - else if (ptr->seq_num+1 < pri->info_seq_num) { - ptr->seq_num = pri->info_seq_num; - ptr->sequence++; - } - /* late or duplicated */ - else if (ptr->seq_num+1 > pri->info_seq_num) - ptr->sequence++; - - ptr->time = current_time; - ptr->timestamp = pri->info_timestamp; - ptr->stop_seq_nr = pri->info_seq_num; - ptr->total_nr++; - - /* save the voice information */ - /* if there was already an error, we quit */ - if (ptr->saved == FALSE) - return 0; - - /* if the captured length and packet length aren't equal, we quit - * because there is some information missing */ - if (pinfo->fd->pkt_len != pinfo->fd->cap_len) { - ptr->saved = FALSE; - ptr->error_type = TAP_RTP_WRONG_LENGTH; - return 0; - } - - /* if padding bit is set, but the padding count is bigger - * then the whole RTP data - error with padding count */ - if ( (pri->info_padding_set != FALSE) && - (pri->info_padding_count > pri->info_payload_len) ) { - ptr->saved = FALSE; - ptr->error_type = TAP_RTP_PADDING_ERROR; - return 0; - } - - /* do we need to insert some silence? */ - if (pkt_type == MARK_SET) { - /* the amount of silence should be the difference between - * the last timestamp and the current one minus x - * x should equal the amount of information in the last frame - * XXX not done yet */ - for(i=0; i < (ptr->delta_timestamp - pri->info_payload_len - - pri->info_padding_count); i++) { - tmp = (gint16 )ulaw2linear((unsigned char)(0x55)); - fwrite(&tmp, 2, 1, ptr->fp); - ptr->count++; - } - fflush(ptr->fp); - } - - /* is it the ulaw? */ - if (pri->info_payload_type == 0) { - /* we put the pointer at the beggining of the RTP data, that is - * at the end of the current frame minus the length of the - * padding count minus length of the RTP data */ - data = cfile.pd + (pinfo->fd->pkt_len - pri->info_payload_len); - for(i=0; i < (pri->info_payload_len - pri->info_padding_count); i++, data++) { - tmp = (gint16 )ulaw2linear((unsigned char)*data); - fwrite(&tmp, 2, 1, ptr->fp); - ptr->count++; - } - fflush(ptr->fp); - ptr->saved = TRUE; - return 0; - } - - /* alaw? */ - else if (pri->info_payload_type == 8) { - data = cfile.pd + (pinfo->fd->pkt_len - pri->info_payload_len); - for(i=0; i < (pri->info_payload_len - pri->info_padding_count); i++, data++) { - tmp = (gint16 )alaw2linear((unsigned char)*data); - fwrite(&tmp, 2, 1, ptr->fp); - ptr->count++; - } - fflush(ptr->fp); - ptr->saved = TRUE; - return 0; - } - - /* unsupported codec or XXX other error */ - else { - ptr->saved = FALSE; - ptr->error_type = TAP_RTP_WRONG_CODEC; - return 0; - } -} - -/* XXX just copied from gtk/rpc_stat.c */ -void protect_thread_critical_region(void); -void unprotect_thread_critical_region(void); - -/* here we close the rtp analysis dialog window and remove the tap listener */ -static void rtp_destroy_cb(GtkWidget *win _U_, gpointer data _U_) -{ - info_stat *rs=(info_stat *)data; - - protect_thread_critical_region(); - remove_tap_listener(rs); - unprotect_thread_critical_region(); - - /* xxx is this enough? */ - g_free(rs->ssrc_tmp); - g_free(rs); - - if (rs->forward.fp != NULL) - fclose(rs->forward.fp); - if (rs->reversed.fp != NULL) - fclose(rs->reversed.fp); - remove(f_tempname); - remove(r_tempname); - - /* Is there a save voice window open? */ - if (save_voice_as_w != NULL) - gtk_widget_destroy(save_voice_as_w); - - /* Note that we no longer have a "RTP Analyse" dialog box. */ - rtp_w = NULL; -} - -/* when the close button in rtp window was clicked */ -/* it seems to me that rtp_destroy_cb is automatically called, so we don't - * need to do the g_free... and rtp_w = NULL ... */ -static void rtp_destroy (GtkWidget *close_bt _U_, gpointer parent_w) -{ - gtk_grab_remove(GTK_WIDGET(parent_w)); - gtk_widget_destroy(GTK_WIDGET(parent_w)); -} - -/* we search the rtp.ssrc node here (thanks to Guy Harris - code here is magic for me */ -static guint32 process_node(proto_item *ptree_node, header_field_info *hfinformation) -{ - field_info *finfo; - proto_item *proto_sibling_node; - header_field_info *hfssrc; - guint32 ssrc; - - finfo = PITEM_FINFO(ptree_node); - - if (hfinformation==(finfo->hfinfo)) { - hfssrc = proto_registrar_get_byname("rtp.ssrc"); - if (hfssrc == NULL) - return 0; - for(ptree_node=g_node_first_child(ptree_node); ptree_node!=NULL; - ptree_node=g_node_next_sibling(ptree_node)) { - finfo=PITEM_FINFO(ptree_node); - if (hfssrc==finfo->hfinfo) { - ssrc = fvalue_get_integer(finfo->value); - return ssrc; - } - } - } - - proto_sibling_node = g_node_next_sibling(ptree_node); - - if (proto_sibling_node) { - ssrc = process_node(proto_sibling_node, hfinformation); - return ssrc; - } - else - return 0; -} - -/* here we search the rtp protocol */ -static guint32 process_tree(proto_tree *protocol_tree) -{ - proto_item *ptree_node; - header_field_info *hfinformation; - - hfinformation = proto_registrar_get_byname("rtp"); - if (hfinformation == NULL) - return 0; - - ptree_node = g_node_first_child(protocol_tree); - if (!ptree_node) - return 0; - - return process_node(ptree_node, hfinformation); -} - -/* when we want to update the information */ -static void refresh_cb(GtkWidget *w _U_, void *pri) -{ - info_stat *rs=pri; - - gtk_clist_clear(GTK_CLIST(clist)); - gtk_clist_clear(GTK_CLIST(clist_r)); - redissect_packets(&cfile); - draw_stat(rs); -} - -/* when we want to save the information */ -static void save_csv_as_ok_cb(GtkWidget *ok_bt, gpointer fs) -{ - gchar *g_dest; - GtkWidget *rev, *forw, *both; - info_stat *rs; - - FILE *fp; - char *columnText; - int i,j; - - g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs))); - - /* Perhaps the user specified a directory instead of a file. - Check whether they did. */ - if (test_for_directory(g_dest) == EISDIR) { - /* It's a directory - set the file selection box to display it. */ - set_last_open_dir(g_dest); - g_free(g_dest); - gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir); - return; - } - - rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb"); - forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb"); - both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb"); - rs = (info_stat *)OBJECT_GET_DATA(ok_bt, "info_stat"); - - if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) { - fp = fopen(g_dest, "w"); - - if (GTK_TOGGLE_BUTTON(both)->active) { - fprintf(fp, "Forward\n"); - } - - for(j = 0; j < GTK_CLIST(clist)->columns; j++) { - if (j == 0) { - fprintf(fp,"%s",GTK_CLIST(clist)->column[j].title); - } else { - fprintf(fp,",%s",GTK_CLIST(clist)->column[j].title); - } - } - fprintf(fp,"\n"); - for (i = 0; i < GTK_CLIST(clist)->rows; i++) { - for(j = 0; j < GTK_CLIST(clist)->columns; j++) { - gtk_clist_get_text(GTK_CLIST(clist),i,j,&columnText); - if (j == 0) { - fprintf(fp,"%s",columnText); - } else { - fprintf(fp,",%s",columnText); - } - } - fprintf(fp,"\n"); - } - - fclose(fp); - } - - if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) { - - if (GTK_TOGGLE_BUTTON(both)->active) { - fp = fopen(g_dest, "a"); - fprintf(fp, "\nReverse\n"); - } else { - fp = fopen(g_dest, "w"); - } - for(j = 0; j < GTK_CLIST(clist_r)->columns; j++) { - if (j == 0) { - fprintf(fp,"%s",GTK_CLIST(clist_r)->column[j].title); - } else { - fprintf(fp,",%s",GTK_CLIST(clist_r)->column[j].title); - } - } - fprintf(fp,"\n"); - for (i = 0; i < GTK_CLIST(clist_r)->rows; i++) { - for(j = 0; j < GTK_CLIST(clist_r)->columns; j++) { - gtk_clist_get_text(GTK_CLIST(clist_r),i,j,&columnText); - if (j == 0) { - fprintf(fp,"%s",columnText); - } else { - fprintf(fp,",%s",columnText); - } - } - fprintf(fp,"\n"); - } - fclose(fp); - } - /* XXX I get GTK warning (sometimes?)!!! */ - gtk_widget_destroy(GTK_WIDGET(save_csv_as_w)); -} - -static void save_csv_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_) -{ - save_csv_as_w = NULL; -} - -/* when the user wants to save the csv information in a file */ -static void save_csv_as_cb(GtkWidget *w _U_, gpointer data) -{ - info_stat *rs=(info_stat *)data; - - GtkWidget *vertb; - GtkWidget *table1; - GtkWidget *label_format; - GtkWidget *channels_label; - GSList *channels_group = NULL; - GtkWidget *forward_rb; - GtkWidget *reversed_rb; - GtkWidget *both_rb; - GtkWidget *ok_bt; - - if (save_csv_as_w != NULL) { - /* There's already a Save CSV info dialog box; reactivate it. */ - reactivate_window(save_csv_as_w); - return; - } - - save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV"); - gtk_signal_connect(GTK_OBJECT(save_csv_as_w), "destroy", - GTK_SIGNAL_FUNC(save_csv_as_destroy_cb), NULL); - - /* Container for each row of widgets */ - vertb = gtk_vbox_new(FALSE, 0); - gtk_container_border_width(GTK_CONTAINER(vertb), 5); - gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(save_csv_as_w)->action_area), - vertb, FALSE, FALSE, 0); - gtk_widget_show (vertb); - - table1 = gtk_table_new (2, 4, FALSE); - gtk_widget_show (table1); - gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0); - gtk_container_set_border_width (GTK_CONTAINER (table1), 10); - gtk_table_set_row_spacings (GTK_TABLE (table1), 20); - - label_format = gtk_label_new ("Format: Comma Separated Values"); - gtk_widget_show (label_format); - gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - - channels_label = gtk_label_new ("Channels:"); - gtk_widget_show (channels_label); - gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5); - - forward_rb = gtk_radio_button_new_with_label (channels_group, "forward "); - channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb)); - gtk_widget_show (forward_rb); - gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed"); - channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb)); - gtk_widget_show (reversed_rb); - gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - both_rb = gtk_radio_button_new_with_label (channels_group, "both"); - channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb)); - gtk_widget_show (both_rb); - gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE); - - ok_bt = GTK_FILE_SELECTION(save_csv_as_w)->ok_button; - OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb); - OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb); - OBJECT_SET_DATA(ok_bt, "both_rb", both_rb); - OBJECT_SET_DATA(ok_bt, "info_stat", rs); - - /* Connect the cancel_button to destroy the widget */ - SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(save_csv_as_w)->cancel_button, - "clicked", (GtkSignalFunc)gtk_widget_destroy, - save_csv_as_w); - - /* Catch the "key_press_event" signal in the window, so that we can catch - the ESC key being pressed and act as if the "Cancel" button had - been selected. */ - dlg_set_cancel(save_csv_as_w, GTK_FILE_SELECTION(save_csv_as_w)->cancel_button); - - gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked", - GTK_SIGNAL_FUNC(save_csv_as_ok_cb), save_csv_as_w); - - gtk_widget_show(save_csv_as_w); -} - -static void save_voice_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_) -{ - /* Note that we no longer have a Save voice info dialog box. */ - save_voice_as_w = NULL; -} - -/* the user wants to save in a file */ -/* XXX support for different formats is currently commented out */ -static void save_voice_as_ok_cb(GtkWidget *ok_bt, gpointer fs) -{ - gchar *g_dest; - /*GtkWidget *wav, *au, *sw;*/ - GtkWidget *rev, *forw, *both; - info_stat *rs; - gint channels /*, format*/; - - g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs))); - - /* Perhaps the user specified a directory instead of a file. - Check whether they did. */ - if (test_for_directory(g_dest) == EISDIR) { - /* It's a directory - set the file selection box to display it. */ - set_last_open_dir(g_dest); - g_free(g_dest); - gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir); - return; - } - - /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb"); - au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb"); - sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/ - rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb"); - forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb"); - both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb"); - rs = (info_stat *)OBJECT_GET_DATA(ok_bt, "info_stat"); - - /* XXX user clicks the ok button, but we know we can't save the voice info because f.e. - * we don't support that codec. So we pop up a warning. Maybe it would be better to - * disable the ok button or disable the buttons for direction if only one is not ok. The - * problem is if we open the save voice dialog and then click the refresh button and maybe - * the state changes, so we can't save anymore. In this case we should be able to update - * the buttons. For now it is easier if we put the warning when the ok button is pressed. - */ - - /* we can not save in both dirctions */ - if ((rs->forward.saved == FALSE) && (rs->reversed.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) { - /* there are many combinations here, we just exit when first matches */ - if ((rs->forward.error_type == TAP_RTP_WRONG_CODEC) || - (rs->reversed.error_type == TAP_RTP_WRONG_CODEC)) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save in a file: Unsupported codec!"); - else if ((rs->forward.error_type == TAP_RTP_WRONG_LENGTH) || - (rs->reversed.error_type == TAP_RTP_WRONG_LENGTH)) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save in a file: Wrong length of captured packets!"); - else if ((rs->forward.error_type == TAP_RTP_PADDING_ERROR) || - (rs->reversed.error_type == TAP_RTP_PADDING_ERROR)) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save in a file: RTP data with padding!"); - else - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save in a file: File I/O problem!"); - return; - } - /* we can not save forward direction */ - else if ((rs->forward.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) || - (GTK_TOGGLE_BUTTON (both)->active))) { - if (rs->forward.error_type == TAP_RTP_WRONG_CODEC) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save forward direction in a file: Unsupported codec!"); - else if (rs->forward.error_type == TAP_RTP_WRONG_LENGTH) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save forward direction in a file: Wrong length of captured packets!"); - else if (rs->forward.error_type == TAP_RTP_PADDING_ERROR) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save forward direction in a file: RTP data with padding!"); - else - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save forward direction in a file: File I/O problem!"); - return; - } - /* we can not save reversed direction */ - else if ((rs->reversed.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) || - (GTK_TOGGLE_BUTTON (both)->active))) { - if (rs->reversed.error_type == TAP_RTP_WRONG_CODEC) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save reversed direction in a file: Unsupported codec!"); - else if (rs->reversed.error_type == TAP_RTP_WRONG_LENGTH) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save reversed direction in a file: Wrong length of captured packets!"); - else if (rs->reversed.error_type == TAP_RTP_PADDING_ERROR) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save reversed direction in a file: RTP data with padding!"); - else if (rs->reversed.error_type == TAP_RTP_NO_DATA) - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save reversed direction in a file: No RTP data!"); - else - simple_dialog(ESD_TYPE_CRIT, NULL, - "Can't save reversed direction in a file: File I/O problem!"); - return; - } - - /*if (GTK_TOGGLE_BUTTON (wav)->active) - format = 1; - else if (GTK_TOGGLE_BUTTON (au)->active) - format = 2; - else if (GTK_TOGGLE_BUTTON (sw)->active) - format = 3;*/ - - if (GTK_TOGGLE_BUTTON (rev)->active) - channels = 2; - else if (GTK_TOGGLE_BUTTON (both)->active) - channels = 3; - else - channels = 1; - - if(!copy_file(g_dest, channels/*, format*/, rs)) { - simple_dialog(ESD_TYPE_CRIT, NULL, "An error occured while saving voice in a file!"); - return; - } - - /* XXX I get GTK warning (sometimes?)!!! */ - gtk_widget_destroy(GTK_WIDGET(save_voice_as_w)); -} - -/* when the user wants to save the voice information in a file */ -/* XXX support for different formats is currently commented out */ -static void save_voice_as_cb(GtkWidget *w _U_, gpointer data) -{ - info_stat *rs=(info_stat *)data; - - GtkWidget *vertb; - GtkWidget *table1; - GtkWidget *label_format; - GtkWidget *channels_label; - /*GSList *format_group = NULL;*/ - GSList *channels_group = NULL; - GtkWidget *forward_rb; - GtkWidget *reversed_rb; - GtkWidget *both_rb; - /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/ - GtkWidget *ok_bt; - - /* if we can't save in a file: wrong codec, cut packets or other errors */ - /* shold the error arise here or later when you click ok button ? - * if we do it here, then we must disable the refresh button, so we don't do it here */ - - if (save_voice_as_w != NULL) { - /* There's already a Save voice info dialog box; reactivate it. */ - reactivate_window(save_voice_as_w); - return; - } - - save_voice_as_w = gtk_file_selection_new("Ethereal: Save Voice Data As"); - gtk_signal_connect(GTK_OBJECT(save_voice_as_w), "destroy", - GTK_SIGNAL_FUNC(save_voice_as_destroy_cb), NULL); - - /* Container for each row of widgets */ - vertb = gtk_vbox_new(FALSE, 0); - gtk_container_border_width(GTK_CONTAINER(vertb), 5); - gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(save_voice_as_w)->action_area), - vertb, FALSE, FALSE, 0); - gtk_widget_show (vertb); - - table1 = gtk_table_new (2, 4, FALSE); - gtk_widget_show (table1); - gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0); - gtk_container_set_border_width (GTK_CONTAINER (table1), 10); - gtk_table_set_row_spacings (GTK_TABLE (table1), 20); - - label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) "); - gtk_widget_show (label_format); - gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - /* we support .au - ulaw*/ -/* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav"); - format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb)); - gtk_widget_show (wav_rb); - gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit "); - format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb)); - gtk_widget_show (sw_rb); - gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - au_rb = gtk_radio_button_new_with_label (format_group, ".au"); - format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb)); - gtk_widget_show (au_rb); - gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - */ - - channels_label = gtk_label_new ("Channels:"); - gtk_widget_show (channels_label); - gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5); - - forward_rb = gtk_radio_button_new_with_label (channels_group, "forward "); - channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb)); - gtk_widget_show (forward_rb); - gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed"); - channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb)); - gtk_widget_show (reversed_rb); - gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - both_rb = gtk_radio_button_new_with_label (channels_group, "both"); - channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb)); - gtk_widget_show (both_rb); - gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE); - - /* if one direction is nok we don't allow saving - XXX this is not ok since the user can click the refresh button and cause changes - but we can not update this window. So we move all the decision on the time the ok - button is clicked - if (rs->forward.saved == FALSE) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE); - gtk_widget_set_sensitive(forward_rb, FALSE); - gtk_widget_set_sensitive(both_rb, FALSE); - } - else if (rs->reversed.saved == FALSE) { - gtk_widget_set_sensitive(reversed_rb, FALSE); - gtk_widget_set_sensitive(both_rb, FALSE); - } - */ - - ok_bt = GTK_FILE_SELECTION(save_voice_as_w)->ok_button; - /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb); - OBJECT_SET_DATA(ok_bt, "au_rb", au_rb); - OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/ - OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb); - OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb); - OBJECT_SET_DATA(ok_bt, "both_rb", both_rb); - OBJECT_SET_DATA(ok_bt, "info_stat", rs); - - /* Connect the cancel_button to destroy the widget */ - SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(save_voice_as_w)->cancel_button, - "clicked", (GtkSignalFunc)gtk_widget_destroy, - save_voice_as_w); - - /* Catch the "key_press_event" signal in the window, so that we can catch - the ESC key being pressed and act as if the "Cancel" button had - been selected. */ - dlg_set_cancel(save_voice_as_w, GTK_FILE_SELECTION(save_voice_as_w)->cancel_button); - - gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked", - GTK_SIGNAL_FUNC(save_voice_as_ok_cb), save_voice_as_w); - - gtk_widget_show(save_voice_as_w); -} - -/* all the graphics on the window is done here */ -static void add_rtp_notebook(void *pri) -{ - info_stat *rs=pri; - - GtkWidget *notebook, *page, *page_r, *label, *label1, *label2, *label3; - GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/; - GtkWidget *box4, *voice_bt, *refresh_bt, *close_bn, *csv_bt; - - gchar *titles[8] = {"Packet nr.", "Sequence", "Delay (s)", "Jitter (s)", "Marker", "Status", "Date", "Length"}; - gchar label_forward[150]; - gchar label_reverse[150]; - - g_snprintf(label_forward, 149, - "Analysing connection from %s port %u to %s port %u SSRC = %u\n", - rs->source, rs->srcport, rs->destination, rs->dstport, rs->ssrc_forward); - g_snprintf(label_reverse, 149, - "Analysing connection from %s port %u to %s port %u SSRC = %u\n", - rs->destination, rs->dstport, rs->source, rs->srcport, rs->ssrc_reversed); - - gtk_widget_destroy(main_vb); - main_vb = gtk_vbox_new(FALSE, 3); - gtk_container_border_width(GTK_CONTAINER(main_vb), 5); - gtk_container_add(GTK_CONTAINER(rtp_w), main_vb); - gtk_widget_show(main_vb); - - /* Start a nootbook for flipping between sets of changes */ - notebook = gtk_notebook_new(); - gtk_container_add(GTK_CONTAINER(main_vb), notebook); - gtk_object_set_data(GTK_OBJECT(rtp_w), "notebook", notebook); - - /* page for forward connection */ - page = gtk_vbox_new(FALSE, 5); - gtk_container_set_border_width(GTK_CONTAINER(page), 20); - - /* scrolled window */ - scrolled_window = gtk_scrolled_window_new(NULL, NULL); - gtk_widget_set_usize(scrolled_window, 600, 200); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - - /* direction label */ - label1 = gtk_label_new(label_forward); - gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0); - - /* place for some statistics */ - max = gtk_label_new("\n\n"); - gtk_box_pack_end(GTK_BOX(page), max, FALSE, FALSE, 5); - - /* clist for the information */ - clist = gtk_clist_new_with_titles(8, titles); - gtk_widget_show(clist); - gtk_container_add(GTK_CONTAINER(scrolled_window), clist); - gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0); - - /* and the label */ - label = gtk_label_new(" Forward Direction "); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); - - /* Hide date and length column */ - gtk_clist_set_column_visibility(GTK_CLIST(clist), 6, FALSE); - gtk_clist_set_column_visibility(GTK_CLIST(clist), 7, FALSE); - - /* column width and justification */ - gtk_clist_set_column_width(GTK_CLIST(clist), 0, 80); - gtk_clist_set_column_width(GTK_CLIST(clist), 1, 80); - gtk_clist_set_column_width(GTK_CLIST(clist), 2, 80); - gtk_clist_set_column_width(GTK_CLIST(clist), 3, 80); - gtk_clist_set_column_width(GTK_CLIST(clist), 4, 40); - gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist), 2, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist), 3, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist), 4, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist), 5, GTK_JUSTIFY_CENTER); - - /* same page for reversed connection */ - page_r = gtk_vbox_new(FALSE, 5); - gtk_container_set_border_width(GTK_CONTAINER(page_r), 20); - scrolled_window_r = gtk_scrolled_window_new(NULL, NULL); - gtk_widget_set_usize(scrolled_window_r, 600, 200); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - label3 = gtk_label_new(label_reverse); - gtk_box_pack_start(GTK_BOX(page_r), label3, FALSE, FALSE, 0); - max_r = gtk_label_new("\n\n"); - gtk_box_pack_end(GTK_BOX(page_r), max_r, FALSE, FALSE, 5); - clist_r = gtk_clist_new_with_titles(8, titles); - gtk_widget_show(clist_r); - gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_r); - gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0); - label2 = gtk_label_new(" Reversed Direction "); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label2); - - /* Hide date and length column */ - gtk_clist_set_column_visibility(GTK_CLIST(clist_r), 6, FALSE); - gtk_clist_set_column_visibility(GTK_CLIST(clist_r), 7, FALSE); - - /* column width and justification */ - gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80); - gtk_clist_set_column_width(GTK_CLIST(clist_r), 1, 80); - gtk_clist_set_column_width(GTK_CLIST(clist_r), 2, 80); - gtk_clist_set_column_width(GTK_CLIST(clist_r), 3, 80); - gtk_clist_set_column_width(GTK_CLIST(clist_r), 4, 40); - gtk_clist_set_column_justification(GTK_CLIST(clist_r), 0, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist_r), 1, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist_r), 2, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist_r), 3, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist_r), 4, GTK_JUSTIFY_CENTER); - gtk_clist_set_column_justification(GTK_CLIST(clist_r), 5, GTK_JUSTIFY_CENTER); - - /* page for help&about or future - page_help = gtk_hbox_new(FALSE, 5); - label4 = gtk_label_new(" Future "); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label4); - frame = gtk_frame_new(""); - text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,..."); - gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT); - gtk_container_add(GTK_CONTAINER(frame), text); - gtk_container_set_border_width(GTK_CONTAINER(frame), 20); - gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0); -*/ - /* show all notebooks */ - gtk_widget_show_all(notebook); - - /* and the buttons */ - box4 = gtk_hbutton_box_new(); - gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, TRUE, 0); - gtk_container_set_border_width(GTK_CONTAINER(box4), 10); - gtk_button_box_set_layout(GTK_BUTTON_BOX(box4), GTK_BUTTONBOX_SPREAD); - gtk_widget_show(box4); - - voice_bt = gtk_button_new_with_label("Save voice data as..."); - gtk_container_add(GTK_CONTAINER(box4), voice_bt); - gtk_widget_show(voice_bt); - gtk_signal_connect(GTK_OBJECT(voice_bt), "clicked", - GTK_SIGNAL_FUNC(save_voice_as_cb), rs); - - refresh_bt = gtk_button_new_with_label("Refresh"); - gtk_container_add(GTK_CONTAINER(box4), refresh_bt); - gtk_widget_show(refresh_bt); - gtk_signal_connect(GTK_OBJECT(refresh_bt), "clicked", - GTK_SIGNAL_FUNC(refresh_cb), rs); - - csv_bt = gtk_button_new_with_label("Save As CSV..."); - gtk_container_add(GTK_CONTAINER(box4), csv_bt); - gtk_widget_show(csv_bt); - gtk_signal_connect(GTK_OBJECT(csv_bt), "clicked", - GTK_SIGNAL_FUNC(save_csv_as_cb), rs); - - close_bn = gtk_button_new_with_label("Close"); - gtk_container_add(GTK_CONTAINER(box4), close_bn); - gtk_widget_show(close_bn); - gtk_signal_connect(GTK_OBJECT(close_bn), "clicked", - GTK_SIGNAL_FUNC(rtp_destroy), GTK_OBJECT(rtp_w)); - - redissect_packets(&cfile); - - draw_stat(rs); -} - - -/* when we click on the selected row it copies that ssrc value into ssrc_reversed */ -static void get_selected_ssrc(GtkWidget *clist_r, gint row, gint column, - GdkEventButton *event _U_, gpointer data) -{ - info_stat *rs=(info_stat *)data; - gchar *text; - - gtk_clist_get_text(GTK_CLIST(clist_r), row, column, &text); - /* XXX is this strtoul portable for guint32? */ - rs->ssrc_reversed = strtoul(text, (char **)NULL, 10); - return; -} - -/* when we click apply button in ssrc reversed dialog */ -static void apply_selected_ssrc(GtkWidget *w _U_, gpointer data) -{ - info_stat *rs=(info_stat *)data; - add_rtp_notebook(rs); -} - -/* this function goes through all the packets that have the same ip and port combination - * (only inversed) as the forward direction (XXX what if the reversed direction doesn't use - * the same ports???) and looks for different SSRC values. This can happen if you capture - * two RTP conversations one after another from the same pair of phones (PC's). - * Both have same IP's and can also have same port numbers, so they (should) differ only - * in SSRC values. In such case we get a list of ssrc values and we have to choose the right - * one from the list. If there is only one or none, we do it automatically */ -static void get_reversed_ssrc(void *prs) -{ - info_stat *ri = prs; - GtkWidget *scroll_r, *clist_r, *ok_bt, *label, *label2, *label1, *main_hbnbox; - gchar temp[150]; - guint i; - - switch(ri->reversed_ip_and_port) - { - /* in case we haven't found any reversed ssrc */ - /* XXX in this case we could look for the inversed IP only */ - case 0: { - ri->ssrc_reversed = 0; - ri->search_ssrc = FALSE; - add_rtp_notebook(ri); - return; - } - /* in case we found exactly one matching ssrc for reversed connection */ - case 1: { - ri->ssrc_reversed = ri->ssrc_tmp[0]; - ri->search_ssrc = FALSE; - add_rtp_notebook(ri); - return; - } - /* there is more then one matching ssrc, so we have to choose between them */ - default: { - ri->search_ssrc = FALSE; - /* let's draw the window */ - label = gtk_label_new("Found more SSRC values for the reversed\n" - "connection with following parameters:\n"); - g_snprintf(temp, 149, "Source %s port %u Destination %s port %u", - ri->destination, ri->dstport, ri->source, ri->srcport); - label2 = gtk_label_new(temp); - gtk_box_pack_start(GTK_BOX(main_vb), label, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(main_vb), label2, FALSE, FALSE, 0); - scroll_r = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_r), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - clist_r = gtk_clist_new(1); - gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80); - gtk_container_add(GTK_CONTAINER(scroll_r), clist_r); - gtk_box_pack_start(GTK_BOX(main_vb), scroll_r, TRUE, TRUE, 0); - label1 = gtk_label_new("Select one value and click apply"); - gtk_box_pack_start(GTK_BOX(main_vb), label1, FALSE, FALSE, 0); - - main_hbnbox = gtk_hbutton_box_new(); - gtk_box_pack_start(GTK_BOX(main_vb), main_hbnbox, FALSE, TRUE, 0); - gtk_container_set_border_width(GTK_CONTAINER(main_hbnbox), 10); - gtk_button_box_set_layout(GTK_BUTTON_BOX(main_hbnbox), - GTK_BUTTONBOX_SPREAD); - gtk_widget_show(main_hbnbox); - - ok_bt = gtk_button_new_with_label("Apply"); - gtk_container_add(GTK_CONTAINER(main_hbnbox), ok_bt); - gtk_signal_connect(GTK_OBJECT(clist_r), "select_row", - GTK_SIGNAL_FUNC(get_selected_ssrc), ri); - gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked", - GTK_SIGNAL_FUNC(apply_selected_ssrc), ri); - - /* add all the ssrc values in the clist */ - /* XXX I'm sure the tmp variable could be avoided here - * i tried to assign guint32 from ri->ssrc_tmp somehow to gchar **text - * but gave up. So if you can do this, just go ahead */ - for (i=0; i < ri->reversed_ip_and_port; i++) { - gchar *text[1]; - gchar tmp[20]; - g_snprintf(tmp, 20, "%u", ri->ssrc_tmp[i]); - text[0] = (gchar *)&tmp; - gtk_clist_append(GTK_CLIST(clist_r), text); - } - - gtk_clist_select_row(GTK_CLIST(clist_r), 0, 0); - - gtk_widget_show(label); - gtk_widget_show(label1); - gtk_widget_show(label2); - gtk_widget_show(ok_bt); - gtk_widget_show(clist_r); - gtk_widget_show(scroll_r); - } - } -} - -/* XXX only handles RTP over IPv4, should add IPv6 support */ -/* when the user clicks the RTP dialog button */ -static void rtp_analyse_cb(GtkWidget *w _U_, gpointer data _U_) -{ - info_stat *rs; - gchar filter_text[256]; - dfilter_t *sfcode; - capture_file *cf; - epan_dissect_t *edt; - gint err; - gboolean frame_matched; - frame_data *fdata; - GString *error_string; - int fd; - - /* There's already a "Display Options" dialog box; reactivate it. */ - if (rtp_w != NULL) { - reactivate_window(rtp_w); - return; - } - - /* Try to compile the filter. */ - strcpy(filter_text,"rtp && ip"); - if (!dfilter_compile(filter_text, &sfcode)) { - simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg); - return; - } - /* we load the current file into cf variable */ - cf = &cfile; - fdata = cf->current_frame; - - /* we are on the selected frame now */ - if (fdata == NULL) - return; /* if we exit here it's an error */ - - /* XXX instead of looking for RTP protocol like this, we could do the process_node() staff */ - /* dissect the current frame */ - if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err)) { - simple_dialog(ESD_TYPE_CRIT, NULL, - file_read_error_message(err), cf->filename); - return; - } - edt = epan_dissect_new(TRUE, FALSE); - epan_dissect_prime_dfilter(edt, sfcode); - epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo); - frame_matched = dfilter_apply_edt(sfcode, edt); - - /* if it is not an rtp frame, exit */ - frame_matched = dfilter_apply_edt(sfcode, edt); - if (frame_matched != 1) { - epan_dissect_free(edt); - simple_dialog(ESD_TYPE_CRIT, NULL, "You didn't choose a RTP packet!"); - return; - } - - /* in rs we put all the info */ - rs=g_malloc(sizeof(info_stat)); - - /* ok, it is a RTP frame, so let's get the ip and port values */ - rs->srcport = edt->pi.srcport; - rs->dstport = edt->pi.destport; - strncpy(rs->source, ip_to_str(edt->pi.src.data), 16); - strncpy(rs->destination, ip_to_str(edt->pi.dst.data), 16); - - /* now we need the SSRC value of the current frame */ - rs->ssrc_forward = process_tree(edt->tree); - if (rs->ssrc_forward == 0) { - simple_dialog(ESD_TYPE_CRIT, NULL, "SSRC value couldn't be found!"); - return; - } - - /* now we have all the information about the forwarding connection - * we need to go through all the packets and search for reversed connection - */ - rs->search_ssrc = TRUE; - rs->ssrc_reversed = 0; - rs->reversed_ip = 0; - rs->reversed_ip_and_port = 0; - rs->ssrc_tmp = NULL; - - sprintf(filter_text,"rtp && ip && !icmp && (( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ) || ( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ))", - ip_to_str(edt->pi.src.data), - edt->pi.srcport, - ip_to_str(edt->pi.dst.data), - edt->pi.destport, - ip_to_str(edt->pi.dst.data), - edt->pi.destport, - ip_to_str(edt->pi.src.data), - edt->pi.srcport - ); -/* XXX compiler warning:passing arg 5 of `register_tap_listener' from incompatible pointer type */ - error_string = register_tap_listener("rtp", rs, filter_text, rtp_reset, rtp_packet, rtp_draw); - if (error_string != NULL) { - simple_dialog(ESD_TYPE_WARN, NULL, error_string->str); - /* XXX is this enough or do I have to free anything else? */ - g_string_free(error_string, TRUE); - g_free(rs); - exit(1); - } - - /* let's draw the window */ - rtp_w = dlg_window_new("Ethereal: RTP Analyse"); - gtk_window_set_position (GTK_WINDOW (rtp_w), GTK_WIN_POS_CENTER); - gtk_signal_connect(GTK_OBJECT(rtp_w), "destroy", - GTK_SIGNAL_FUNC(rtp_destroy_cb), rs); - - /* Container for each row of widgets */ - main_vb = gtk_vbox_new(FALSE, 3); - gtk_container_border_width(GTK_CONTAINER(main_vb), 5); - gtk_container_add(GTK_CONTAINER(rtp_w), main_vb); - gtk_widget_show(main_vb); - - /* file names for storing sound data */ - fd = create_tempfile(f_tempname, sizeof(f_tempname), "ethereal_rtp_fwd"); - rs->forward.fp = fdopen(fd, "wb"); - fd = create_tempfile(r_tempname, sizeof(f_tempname), "ethereal_rtp_rev"); - rs->reversed.fp = fdopen(fd, "wb"); - - redissect_packets(cf); - - /* so how many reversed connection we have ? */ - get_reversed_ssrc(rs); - - /* and finally display this window */ - gtk_widget_show(rtp_w); -} - -static void -rtp_analyse_init(char *dummy _U_) -{ - rtp_analyse_cb(NULL, NULL); -} - -void -register_tap_listener_gtkrtp(void) -{ - register_ethereal_tap("rtp", rtp_analyse_init); -} - -void -register_tap_menu_gtkrtp(void) -{ - register_tap_menu_item("Statistics/RTP Analysis...", rtp_analyse_cb, - NULL, NULL); -} - - -/* here we save it into a file that user specified */ -/* XXX what about endians here? could go something wrong? */ -static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ void *data) -{ - info_stat *rs=(info_stat *)data; - int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten; - gint16 f_pd; - gint16 r_pd; - gchar pd[1]; - guint32 f_write_silence = 0; - guint32 r_write_silence = 0; - progdlg_t *progbar; - guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0; - gboolean stop_flag = FALSE; - - forw_fd = open(f_tempname, O_RDONLY | O_BINARY); - if (forw_fd < 0) - return FALSE; - rev_fd = open(r_tempname, O_RDONLY | O_BINARY); - if (rev_fd < 0) { - close(forw_fd); - return FALSE; - } - - /* open file for saving */ - to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); - if (to_fd < 0) { - close(forw_fd); - close(rev_fd); - return FALSE; - } - - progbar = create_progress_dlg("Saving voice in a file", dest, "Stop", &stop_flag); - - /* First we write the .au header. XXX Hope this is endian independant */ - /* the magic word 0x2e736e64 == .snd */ - *pd = (unsigned char)0x2e; write(to_fd, pd, 1); - *pd = (unsigned char)0x73; write(to_fd, pd, 1); - *pd = (unsigned char)0x6e; write(to_fd, pd, 1); - *pd = (unsigned char)0x64; write(to_fd, pd, 1); - /* header offset == 24 bytes */ - *pd = (unsigned char)0x00; write(to_fd, pd, 1); - write(to_fd, pd, 1); - write(to_fd, pd, 1); - *pd = (unsigned char)0x18; write(to_fd, pd, 1); - /* total length, it is permited to set this to 0xffffffff */ - *pd = (unsigned char)0xff; write(to_fd, pd, 1); - write(to_fd, pd, 1); - write(to_fd, pd, 1); - write(to_fd, pd, 1); - /* encoding format == 8 bit ulaw */ - *pd = (unsigned char)0x00; write(to_fd, pd, 1); - write(to_fd, pd, 1); - write(to_fd, pd, 1); - *pd = (unsigned char)0x01; write(to_fd, pd, 1); - /* sample rate == 8000 Hz */ - *pd = (unsigned char)0x00; write(to_fd, pd, 1); - write(to_fd, pd, 1); - *pd = (unsigned char)0x1f; write(to_fd, pd, 1); - *pd = (unsigned char)0x40; write(to_fd, pd, 1); - /* channels == 1 */ - *pd = (unsigned char)0x00; write(to_fd, pd, 1); - write(to_fd, pd, 1); - write(to_fd, pd, 1); - *pd = (unsigned char)0x01; write(to_fd, pd, 1); - - switch (channels) { - /* only forward direction */ - case 1: { - progbar_count = rs->forward.count; - progbar_quantum = rs->forward.count/100; - while ((fread = read(forw_fd, &f_pd, 2)) > 0) { - if(stop_flag) - break; - if((count > progbar_nextstep) && (count <= progbar_count)) { - update_progress_dlg(progbar, - (gfloat) count/progbar_count, "Saving"); - progbar_nextstep = progbar_nextstep + progbar_quantum; - } - count++; - *pd = (unsigned char)linear2ulaw(f_pd); - fwritten = write(to_fd, pd, 1); - if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) { - close(forw_fd); - close(rev_fd); - close(to_fd); - destroy_progress_dlg(progbar); - return FALSE; - } - } - break; - } - /* only reversed direction */ - case 2: { - progbar_count = rs->reversed.count; - progbar_quantum = rs->reversed.count/100; - while ((rread = read(rev_fd, &r_pd, 2)) > 0) { - if(stop_flag) - break; - if((count > progbar_nextstep) && (count <= progbar_count)) { - update_progress_dlg(progbar, - (gfloat) count/progbar_count, "Saving"); - progbar_nextstep = progbar_nextstep + progbar_quantum; - } - count++; - *pd = (unsigned char)linear2ulaw(r_pd); - rwritten = write(to_fd, pd, 1); - if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) { - close(forw_fd); - close(rev_fd); - close(to_fd); - destroy_progress_dlg(progbar); - return FALSE; - } - } - break; - } - /* both directions */ - default: { - (rs->forward.count > rs->reversed.count) ? - (progbar_count = rs->forward.count) : - (progbar_count = rs->reversed.count); - progbar_quantum = progbar_count/100; - /* since conversation in one way can start later than in the other one, - * we have to write some silence information for one channel */ - if (rs->forward.start_time > rs->reversed.start_time) { - f_write_silence = - (rs->forward.start_time-rs->reversed.start_time)*8000; - } - else if (rs->forward.start_time < rs->reversed.start_time) { - r_write_silence = - (rs->reversed.start_time-rs->forward.start_time)*8000; - } - for(;;) { - if(stop_flag) - break; - if((count > progbar_nextstep) && (count <= progbar_count)) { - update_progress_dlg(progbar, - (gfloat) count/progbar_count, "Saving"); - progbar_nextstep = progbar_nextstep + progbar_quantum; - } - count++; - if(f_write_silence > 0) { - rread = read(rev_fd, &r_pd, 2); - f_pd = 0; - fread = 1; - f_write_silence--; - } - else if(r_write_silence > 0) { - fread = read(forw_fd, &f_pd, 2); - r_pd = 0; - rread = 1; - r_write_silence--; - } - else { - fread = read(forw_fd, &f_pd, 2); - rread = read(rev_fd, &r_pd, 2); - } - if ((rread == 0) && (fread == 0)) - break; - *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 ); - rwritten = write(to_fd, pd, 1); - if ((rwritten < 0) || (rread < 0) || (fread < 0)) { - close(forw_fd); - close(rev_fd); - close(to_fd); - destroy_progress_dlg(progbar); - return FALSE; - } - } - } - } - destroy_progress_dlg(progbar); - close(forw_fd); - close(rev_fd); - close(to_fd); - return TRUE; - -} |