/* t38_analysis.c * t38 fax analysis for ethereal * * $Id$ * * Copyright 2005 Verso Technologies Inc. * By Alejandro Vaquero * * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "graph_analysis.h" #include "globals.h" #include #include #include #include #include #include #include "../stat_menu.h" #include "main.h" #include "alert_box.h" #include "simple_dialog.h" #include "graph_analysis.h" #include "gui_stat_menu.h" #define MAX_HDLC_FRAME 1024 typedef enum { UNKNOWN, CORRECT, EARLY, /* seq_num > than expected, we assume all previous as lost */ LATE /* seq_num < than expected, late packet we drop it */ } SEQ_STATUS; typedef struct _tap_t38_stat_t { gboolean first_packet; gint32 seq_num; /* UDPTLPacket sequence number */ gint32 wrong_seq_num; /* count UDPTLPacket wron sequence number */ guint8 hdlc_data[MAX_HDLC_FRAME]; /* V21 HDLC data */ guint16 hdlc_data_index; /* V21 HDLC index */ gboolean valid_hdlc_data; guint32 other_data_num_bytes; /* num of bytes of other data (non hdlc) */ guint32 other_data_lost; /* num of packet lost (wrong seq num) of other data (non hdlc) */ guint32 other_data_max_burst_lost; /* max burst num of packet lost (wrong seq num) of other data (non hdlc) */ guint32 other_data_burst_lost; /* burst num of packet lost (wrong seq num) of other data (non hdlc) */ gint32 start_frame_other_data; /* start frame of other_data */ double start_time_other_data; /* start time of other_data */ SEQ_STATUS prev_seq_status; /*previous seq num status used to calclate the busrt error */ } tap_t38_stat_t; /* structure that holds general information about the connection * and structures for both directions */ typedef struct _user_data_t { /* tap associated data*/ address ip_src_fwd; guint16 port_src_fwd; address ip_dst_fwd; guint16 port_dst_fwd; address ip_src_rev; guint16 port_src_rev; address ip_dst_rev; guint16 port_dst_rev; tap_t38_stat_t forward; tap_t38_stat_t reverse; graph_analysis_data_t *graph_analysis_data; } user_data_t; /****************************************************************************/ /* Add a new item into the graph */ int add_to_graph_t38(user_data_t *user_data, packet_info *pinfo, const gchar *frame_label, gchar *comment, gint line_style) { graph_analysis_item_t *gai; gai = g_malloc(sizeof(graph_analysis_item_t)); gai->frame_num = pinfo->fd->num; gai->time= nstime_to_sec(&pinfo->fd->rel_ts); COPY_ADDRESS(&(gai->src_addr),&(pinfo->src)); COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst)); gai->port_src=pinfo->srcport; gai->port_dst=pinfo->destport; if (frame_label != NULL) gai->frame_label = g_strdup(frame_label); else gai->frame_label = g_strdup(""); if (comment != NULL) gai->comment = g_strdup(comment); else gai->comment = g_strdup(""); gai->conv_num=1; gai->line_style=line_style; /* 1=single line 2=dual line */ gai->display=TRUE; user_data->graph_analysis_data->graph_info->list = g_list_append(user_data->graph_analysis_data->graph_info->list, gai); return 1; } /****************************************************************************/ /* Change the frame_label and comment in a graph item if not NULL*/ /* return 0 if the frame_num is not in the graph list */ int change_frame_graph_t38(user_data_t *user_data, gint32 frame_num, const gchar *new_frame_label, const gchar *new_comment) { graph_analysis_item_t *gai; GList* list; gchar *tmp_str = NULL; gchar *tmp_str2 = NULL; if (frame_num == -1) return 0; list = g_list_first(user_data->graph_analysis_data->graph_info->list); while (list) { gai = list->data; if (gai->frame_num == (guint32) frame_num){ tmp_str = gai->frame_label; tmp_str2 = gai->comment; if (new_frame_label != NULL){ gai->frame_label = g_strdup(new_frame_label); g_free(tmp_str); } if (new_comment != NULL){ gai->comment = g_strdup(new_comment); g_free(tmp_str2); } break; } list = g_list_next (list); } if (tmp_str == NULL) return 0; /* it is not in the list */ return 1; } /* TODO: Dissect the complete t30 HDLC packets */ #if 0 #define MAX_DESC 1024 void dissect_t30_DIS_DTC(guint8 *data, guint len) { guint8 octet; int offset; gchar buf[MAX_DESC]; offset = 3; if (len == 0) return; octet = data[offset]; g_snprintf(buf, MAX_DESC, "%sStore and forward Internet fax- Simple mode (ITU-T T.37)", octet&0x80?"":"No "); g_snprintf(buf, MAX_DESC, "%sReal-time Internet fax (ITU T T.38)", octet&0x20?"":"No "); g_snprintf(buf, MAX_DESC, "%s3rd Generation Mobile Network ", octet&0x10?"":"No ") } #endif /****************************************************************************/ static const value_string t30_facsimile_control_field_vals_short[] = { { 0x01, "DIS" }, { 0x02, "CSI" }, { 0x04, "NSF" }, { 0x81, "DTC" }, { 0x82, "CIG" }, { 0x84, "NSC" }, { 0x83, "PWD" }, { 0x85, "SEP" }, { 0x86, "PSA" }, { 0x87, "CIA" }, { 0x88, "ISP" }, { 0x41, "DCS" }, { 0x42, "TSI" }, { 0x44, "NSS" }, { 0x43, "SUB" }, { 0x45, "SID" }, { 0x46, "TSA" }, { 0x47, "IRA" }, { 0x21, "CFR" }, { 0x22, "FTT" }, { 0x24, "CSA" }, { 0x71, "EOM" }, { 0x72, "MPS" }, { 0x74, "EOP" }, { 0x79, "PRI-EOM" }, { 0x7A, "PRI-MPS" }, { 0x7C, "PRI-EOP" }, { 0x78, "PRI-EOP" }, { 0x31, "MCF" }, { 0x33, "RTP" }, { 0x32, "RTN" }, { 0x35, "PIP" }, { 0x34, "PIN" }, { 0x3F, "FDM" }, { 0x5F, "DCN" }, { 0x58, "CRP" }, { 0x53, "FNV" }, { 0x57, "TNR" }, { 0x56, "TR" }, { 0, NULL } }; static const value_string t30_facsimile_control_field_vals[] = { { 0x01, "Digital Identification Signal" }, { 0x02, "Called Subscriber Identification" }, { 0x04, "Non-Standard Facilities" }, { 0x81, "Digital Transmit Command" }, { 0x82, "Calling Subscriber Identification" }, { 0x84, "Non-Standard facilities Command" }, { 0x83, "Password" }, { 0x85, "Selective Polling" }, { 0x86, "Polled Subaddress" }, { 0x87, "Calling subscriber Internet Address" }, { 0x88, "Internet Selective Polling Address" }, { 0x41, "Digital Command Signal" }, { 0x42, "Transmitting Subscriber Identification" }, { 0x44, "Non-Standard facilities Set-up" }, { 0x43, "Subaddress" }, { 0x45, "Sender Identification" }, { 0x46, "Transmitting Subscriber Internet address" }, { 0x47, "Internet Routing Address" }, { 0x21, "Confirmation To Receive" }, { 0x22, "Failure To Train" }, { 0x24, "Called Subscriber Internet Address" }, { 0x71, "End Of Message" }, { 0x72, "MultiPage Signal" }, { 0x74, "End Of Procedure" }, { 0x79, "Procedure Interrupt-End Of Message" }, { 0x7A, "Procedure Interrupt-MultiPage Signal" }, { 0x7C, "Procedure Interrupt-End Of Procedure" }, { 0x78, "Procedure Interrupt-End Of Procedure" }, { 0x31, "Message Confirmation" }, { 0x33, "Retrain Positive" }, { 0x32, "Retrain Negative" }, { 0x35, "Procedure Interrupt Positive" }, { 0x34, "Procedure Interrupt Negative" }, { 0x3F, "File Diagnostics Message" }, { 0x5F, "Disconnect" }, { 0x58, "Command Repeat" }, { 0x53, "Field Not Valid" }, { 0x57, "Transmit not ready" }, { 0x56, "Transmit ready" }, { 0, NULL } }; static const value_string data_vals[] = { { 0, "v21" }, { 1, "v27-2400" }, { 2, "v27-4800" }, { 3, "v29-7200" }, { 4, "v29-9600" }, { 5, "v17-7200" }, { 6, "v17-9600" }, { 7, "v17-12000" }, { 8, "v17-14400" }, { 9, "v8" }, { 10, "v34-pri-rate" }, { 11, "v34-CC-1200" }, { 12, "v34-pri-ch" }, { 13, "v33-12000" }, { 14, "v33-14400" }, { 0, NULL } }; void dissect_t30(tap_t38_stat_t *statinfo, user_data_t *user_data _U_, packet_info *pinfo, guint32 data_value ) { gchar *frame_label = NULL; gchar *comment = NULL; guint8 octet; octet = statinfo->hdlc_data[2]; /* TODO: Dissect the complete t30 HDLC packets */ #if 0 /* Facsimile Control Field (FCF) */ if ( ((octet&0xF0) == 0x00) || ((octet&0xF0) == 0x80) ) { /* Initial identification or Command to send */ frame_label = g_strdup_printf("%s:hdlc:%s", val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet, t30_facsimile_control_field_vals_short, "Ukn (0x%02X)")); comment = g_strdup_printf("%s:HDLC:%s",val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet, t30_facsimile_control_field_vals, "Ukn (0x%02X)")); if ( (octet == 0x01) || (octet == 0x81) ) { dissect_t30_DIS_DTC(statinfo->hdlc_data, statinfo->hdlc_data_index); } } else { /* all other values */ frame_label = g_strdup_printf("%s:hdlc:%s", val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet&0x7F, t30_facsimile_control_field_vals_short, "Ukn (0x%02X)")); comment = g_strdup_printf("%s:HDLC:%s",val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet&0x7F, t30_facsimile_control_field_vals, "Ukn (0x%02X)")); } #else frame_label = g_strdup_printf("%s:hdlc:%s", val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet&0x7F, t30_facsimile_control_field_vals_short, "Ukn (0x%02X)")); comment = g_strdup_printf("%s:HDLC:%s",val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet&0x7F, t30_facsimile_control_field_vals, "Ukn (0x%02X)")); #endif add_to_graph_t38(user_data, pinfo, frame_label, comment, 2); } /****************************************************************************/ int t38_packet_analyse(tap_t38_stat_t *statinfo, user_data_t *user_data _U_, packet_info *pinfo, const t38_packet_info *t38_info) { gchar *frame_label = NULL; gchar *comment = NULL; SEQ_STATUS seq_status = UNKNOWN; /* if it is duplicated, just return */ if (statinfo->seq_num == t38_info->seq_num) return 0; /* if it is the correct seq or first packet */ if ( (statinfo->seq_num+1 == t38_info->seq_num) || (statinfo->seq_num == -1) ) seq_status = CORRECT; /* EARLY: seq_num > than expexted */ else if (t38_info->seq_num > statinfo->seq_num+1 ) seq_status = EARLY; /* LATE: seq_num < than expexted */ else if (t38_info->seq_num < statinfo->seq_num+1 ) seq_status = LATE; if (t38_info->type_msg == 0) { /* t30-indicator */ frame_label = g_strdup_printf("t30 Ind:%s",val_to_str(t38_info->t30ind_value, t30_indicator_vals, "Ukn (0x%02X)") ); comment = g_strdup_printf("t30 Ind:%s",val_to_str(t38_info->t30ind_value, t30_indicator_vals, "Ukn (0x%02X)") ); add_to_graph_t38(user_data, pinfo, frame_label, comment, 1); /* reset other_data stats in case we never got the previos t4-non-ecm-sig-end */ statinfo->other_data_num_bytes = 0; statinfo->other_data_lost = 0; statinfo->start_frame_other_data = -1; statinfo->prev_seq_status = CORRECT; statinfo->other_data_max_burst_lost = 0; statinfo->other_data_burst_lost = 0; statinfo->start_time_other_data = 0; } else if (t38_info->type_msg == 1) { /* data */ int i; for (i=0; it38_info_data_item_index; i++) { switch(t38_info->data_type[i]){ case 0: /* hdlc-data */ /* if it is hdlc-data add it to the array */ /* check we'll not excede the array */ if (statinfo->hdlc_data_index+t38_info->data_len[i] < MAX_HDLC_FRAME) { g_memmove(&statinfo->hdlc_data[statinfo->hdlc_data_index], t38_info->data[i],t38_info->data_len[i]); statinfo->hdlc_data_index += t38_info->data_len[i]; } if (seq_status != CORRECT) statinfo->valid_hdlc_data = FALSE; break; case 2: /* hdlc-fcs-OK */ case 4: /* hdlc-fcs-OK-sig-end */ if (statinfo->valid_hdlc_data) dissect_t30(statinfo, user_data, pinfo, t38_info->data_value); else { frame_label = g_strdup_printf("%s:hdlc:not decoded",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)")); comment = g_strdup_printf("%s:HDLC:ERROR: wrong seq number in HDLC packet(s)",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)")); add_to_graph_t38(user_data, pinfo, frame_label, comment, 2); } statinfo->hdlc_data_index = 0; statinfo->valid_hdlc_data = TRUE; break; case 1: /* hdlc-sig-end */ if (statinfo->hdlc_data_index != 0) { /* if there was no fcs-OK, this is an error */ frame_label = g_strdup_printf("%s:hdlc:hdlc-sig-end",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)")); comment = g_strdup_printf("%s:HDLC:ERROR: received hdlc-sig-end without received fcs-OK or fcs-BAD",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)")); add_to_graph_t38(user_data, pinfo, frame_label, comment, 2); statinfo->hdlc_data_index = 0; } break; case 3: /* hdlc-fcs-BAD */ case 5: /* hdlc-fcs-BAD-sig-end */ frame_label = g_strdup_printf("%s:hdlc:%s",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)"),t38_info->data_type[i] == 3 ? "fcs-BAD" : "fcs-BAD-sig-end" ); comment = g_strdup_printf("WARNING: received %s:hdlc:%s", val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)"), t38_info->data_type[i] == 3 ? "fcs-BAD" : "fcs-BAD-sig-end"); add_to_graph_t38(user_data, pinfo, frame_label, comment, 2); statinfo->hdlc_data_index = 0; break; } if ( (t38_info->data_type[i] == 6) || (t38_info->data_type[i] == 7) ) { /* t4-non-ecm-data or t4-non-ecm-sig-end */ statinfo->other_data_num_bytes += t38_info->data_len[i]; if (seq_status != CORRECT) { statinfo->other_data_lost++; statinfo->other_data_burst_lost++; } else { if (statinfo->other_data_burst_lost > statinfo->other_data_max_burst_lost) statinfo->other_data_max_burst_lost = statinfo->other_data_burst_lost; statinfo->other_data_burst_lost = 0; } if (statinfo->start_frame_other_data == -1) { statinfo->start_frame_other_data = pinfo->fd->num; statinfo->start_time_other_data = nstime_to_sec(&pinfo->fd->rel_ts); } frame_label = g_strdup_printf("data:%s",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)") ); comment = g_strdup_printf("Num of bytes: %d Duration: %.2fs Wrong seq num: %d Burst pack lost: %d", statinfo->other_data_num_bytes, nstime_to_sec(&pinfo->fd->rel_ts) - statinfo->start_time_other_data, statinfo->other_data_lost, statinfo->other_data_max_burst_lost); if ( !change_frame_graph_t38(user_data, statinfo->start_frame_other_data, frame_label, comment) ) add_to_graph_t38(user_data, pinfo, frame_label, comment, 2); if (t38_info->data_type[i] == 7) { /* t4-non-ecm-sig-end reset values */ statinfo->other_data_num_bytes = 0; statinfo->other_data_lost = 0; statinfo->start_frame_other_data = -1; statinfo->prev_seq_status = CORRECT; statinfo->other_data_max_burst_lost = 0; statinfo->other_data_burst_lost = 0; } } } } if (seq_status != LATE) statinfo->seq_num = t38_info->seq_num; g_free(frame_label); g_free(comment); return 0; } /****************************************************************************/ /* whenever a T38 packet is seen by the tap listener */ static int t38_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *t38_info_arg) { user_data_t *user_data = user_data_arg; const t38_packet_info *t38_info = t38_info_arg; /* we ignore packets that are not displayed */ if (pinfo->fd->flags.passed_dfilter == 0) return 0; /* is it the forward direction? */ else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0 && user_data->port_src_fwd == pinfo->srcport && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0 && user_data->port_dst_fwd == pinfo->destport) { t38_packet_analyse(&(user_data->forward),user_data, pinfo, t38_info); } /* is it the reversed direction? */ else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0 && user_data->port_src_rev == pinfo->srcport && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0 && user_data->port_dst_rev == pinfo->destport) { t38_packet_analyse(&(user_data->reverse),user_data, pinfo, t38_info); } return 1; } /****************************************************************************/ /* reset user_data valueas and clean graph info */ static void t38_clean(user_data_t *user_data) { graph_analysis_item_t *gai; GList* list; user_data->forward.hdlc_data_index = 0; user_data->reverse.hdlc_data_index = 0; user_data->forward.wrong_seq_num = 0; user_data->reverse.wrong_seq_num = 0; user_data->forward.seq_num = -1; user_data->reverse.seq_num = -1; user_data->forward.valid_hdlc_data = TRUE; user_data->reverse.valid_hdlc_data = TRUE; user_data->forward.other_data_num_bytes = 0; user_data->reverse.other_data_num_bytes = 0; user_data->forward.other_data_lost = 0; user_data->reverse.other_data_lost = 0; user_data->forward.other_data_max_burst_lost = 0; user_data->reverse.other_data_max_burst_lost = 0; user_data->forward.other_data_burst_lost = 0; user_data->reverse.other_data_burst_lost = 0; user_data->forward.start_frame_other_data = -1; user_data->reverse.start_frame_other_data = -1; user_data->forward.start_time_other_data = 0; user_data->reverse.start_time_other_data = 0; user_data->forward.prev_seq_status = CORRECT; user_data->reverse.prev_seq_status = CORRECT; /* free the graph list */ list = g_list_first(user_data->graph_analysis_data->graph_info->list); while (list) { gai = list->data; g_free(gai->frame_label); g_free(gai->comment); g_free((void *)gai->src_addr.data); g_free((void *)gai->dst_addr.data); g_free(list->data); list = g_list_next (list); } g_list_free(user_data->graph_analysis_data->graph_info->list); user_data->graph_analysis_data->graph_info->nconv = 0; user_data->graph_analysis_data->graph_info->list = NULL; return; } /****************************************************************************/ /* when there is a [re]reading of packet's */ static void t38_reset(void *user_data_arg) { user_data_t *user_data = user_data_arg; t38_clean(user_data); /* create or refresh the graph windows */ if (user_data->graph_analysis_data->dlg.window == NULL) /* create the window */ graph_analysis_create(user_data->graph_analysis_data); else graph_analysis_update(user_data->graph_analysis_data); /* refresh it */ return; } /****************************************************************************/ static void t38_draw(void *user_data_arg) { user_data_t *user_data = user_data_arg; graph_analysis_redraw(user_data->graph_analysis_data); return; } /****************************************************************************/ /* called when the graph windows is destroyed */ static void t38_on_destroy(void *user_data_arg) { user_data_t *user_data = user_data_arg; /* remove tap listener */ protect_thread_critical_region(); remove_tap_listener(user_data); unprotect_thread_critical_region(); /* free the address */ g_free((void *)user_data->ip_src_fwd.data); g_free((void *)user_data->ip_dst_fwd.data); g_free((void *)user_data->ip_src_rev.data); g_free((void *)user_data->ip_dst_rev.data); /* clean graph info */ t38_clean(user_data); g_free(user_data->graph_analysis_data->graph_info); } /****************************************************************************/ void t38_analysis( address *ip_src_fwd, guint16 port_src_fwd, address *ip_dst_fwd, guint16 port_dst_fwd, address *ip_src_rev, guint16 port_src_rev, address *ip_dst_rev, guint16 port_dst_rev ) { user_data_t *user_data; GString *error_string; /* init */ user_data = g_malloc(sizeof(user_data_t)); user_data->graph_analysis_data = graph_analysis_init(); user_data->graph_analysis_data->graph_info = g_malloc(sizeof(graph_analysis_info_t)); user_data->graph_analysis_data->graph_info->nconv = 0; user_data->graph_analysis_data->graph_info->list = NULL; user_data->graph_analysis_data->dlg.title = g_strdup("Fax T38 analysis"); user_data->graph_analysis_data->dlg.inverse = TRUE; /* to display "calling ----> called" fax call */ user_data->graph_analysis_data->on_destroy_user_data = t38_on_destroy; user_data->graph_analysis_data->data = user_data; COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd); user_data->port_src_fwd = port_src_fwd; COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd); user_data->port_dst_fwd = port_dst_fwd; COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev); user_data->port_src_rev = port_src_rev; COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev); user_data->port_dst_rev = port_dst_rev; /* register tap listener */ error_string = register_tap_listener("t38", user_data, NULL, t38_reset, t38_packet, t38_draw); if (error_string != NULL) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str); g_string_free(error_string, TRUE); return; } /* retap all packets */ cf_retap_packets(&cfile, FALSE); } /****************************************************************************/ /* entry point from main menu */ void t38_analysis_cb(GtkWidget *w _U_, gpointer data _U_) { address ip_src_fwd; guint16 port_src_fwd; address ip_dst_fwd; guint16 port_dst_fwd; address ip_src_rev; guint16 port_src_rev; address ip_dst_rev; guint16 port_dst_rev; gchar filter_text[256]; dfilter_t *sfcode; capture_file *cf; epan_dissect_t *edt; gint err; gchar *err_info; gboolean frame_matched; frame_data *fdata; /* Try to compile the filter. */ strcpy(filter_text,"t38 && (ip || ipv6)"); if (!dfilter_compile(filter_text, &sfcode)) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, 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, &err_info)) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, cf_read_error_message(err, err_info), 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, NULL); frame_matched = dfilter_apply_edt(sfcode, edt); /* check if it is a t38 frame */ frame_matched = dfilter_apply_edt(sfcode, edt); if (frame_matched != 1) { epan_dissect_free(edt); simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't choose a T38 packet!"); return; } /* ok, it is a T38 frame, so let's get the ip and port values */ COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src)) COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst)) port_src_fwd = edt->pi.srcport; port_dst_fwd = edt->pi.destport; /* assume the inverse ip/port combination for the reverse direction */ COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst)) COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src)) port_src_rev = edt->pi.destport; port_dst_rev = edt->pi.srcport; t38_analysis( &ip_src_fwd, port_src_fwd, &ip_dst_fwd, port_dst_fwd, &ip_src_rev, port_src_rev, &ip_dst_rev, port_dst_rev ); } /****************************************************************************/ static void t38_analysis_init(const char *dummy _U_) { t38_analysis_cb(NULL, NULL); } /****************************************************************************/ void register_tap_listener_t38_analysis(void) { register_stat_cmd_arg("t38", t38_analysis_init); register_stat_menu_item("Fax T38 Analysis...", REGISTER_STAT_GROUP_TELEPHONY, t38_analysis_cb, NULL, NULL, NULL); }