diff options
Diffstat (limited to 'gtk/rtp_analysis.c')
-rw-r--r-- | gtk/rtp_analysis.c | 1895 |
1 files changed, 1895 insertions, 0 deletions
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); +} |