2 * dcerpc_stat 2002 Ronnie Sahlberg
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 /* This module provides rpc call/reply SRT statistics to ethereal,
26 * and displays them graphically.
27 * It is only used by ethereal and not tethereal
29 * It serves as an example on how to use the tap api.
40 #include <epan/packet_info.h>
41 #include <epan/epan.h>
44 #include "simple_dialog.h"
45 #include "dlg_utils.h"
48 #include "../register.h"
49 #include <epan/dissectors/packet-dcerpc.h>
50 #include "../globals.h"
51 #include "filter_dlg.h"
52 #include "compat_macros.h"
53 #include "service_response_time_table.h"
54 #include "gtkglobals.h"
57 /* used to keep track of the statistics for an entire program interface */
58 typedef struct _rpcstat_t {
60 srt_stat_table srt_table;
69 uuid_equal(e_uuid_t *uuid1, e_uuid_t *uuid2)
71 if( (uuid1->Data1!=uuid2->Data1)
72 ||(uuid1->Data2!=uuid2->Data2)
73 ||(uuid1->Data3!=uuid2->Data3)
74 ||(uuid1->Data4[0]!=uuid2->Data4[0])
75 ||(uuid1->Data4[1]!=uuid2->Data4[1])
76 ||(uuid1->Data4[2]!=uuid2->Data4[2])
77 ||(uuid1->Data4[3]!=uuid2->Data4[3])
78 ||(uuid1->Data4[4]!=uuid2->Data4[4])
79 ||(uuid1->Data4[5]!=uuid2->Data4[5])
80 ||(uuid1->Data4[6]!=uuid2->Data4[6])
81 ||(uuid1->Data4[7]!=uuid2->Data4[7]) ){
88 dcerpcstat_gen_title(rpcstat_t *rs)
92 title = g_strdup_printf("DCE-RPC Service Response Time statistics for %s major version %u: %s", rs->prog, rs->ver, cf_get_display_name(&cfile));
97 dcerpcstat_set_title(rpcstat_t *rs)
101 title = dcerpcstat_gen_title(rs);
102 gtk_window_set_title(GTK_WINDOW(rs->win), title);
107 dcerpcstat_reset(void *rs_arg)
109 rpcstat_t *rs = rs_arg;
111 reset_srt_table_data(&rs->srt_table);
112 dcerpcstat_set_title(rs);
117 dcerpcstat_packet(void *rs_arg, packet_info *pinfo, epan_dissect_t *edt _U_, void *ri_arg)
119 rpcstat_t *rs = rs_arg;
120 dcerpc_info *ri = ri_arg;
125 if(!ri->call_data->req_frame){
126 /* we have not seen the request so we dont know the delta*/
129 if(ri->call_data->opnum>=rs->num_procedures){
130 /* dont handle this since its outside of known table */
134 /* we are only interested in reply packets */
135 if(ri->ptype != PDU_RESP){
139 /* we are only interested in certain program/versions */
140 if( (!uuid_equal( (&ri->call_data->uuid), (&rs->uuid)))
141 ||(ri->call_data->ver!=rs->ver)){
146 add_srt_table_data(&rs->srt_table, ri->call_data->opnum, &ri->call_data->req_time, pinfo);
153 dcerpcstat_draw(void *rs_arg)
155 rpcstat_t *rs = rs_arg;
157 draw_srt_table_data(&rs->srt_table);
161 /* since the gtk2 implementation of tap is multithreaded we must protect
162 * remove_tap_listener() from modifying the list while draw_tap_listener()
163 * is running. the other protected block is in main.c
165 * there should not be any other critical regions in gtk2
167 void protect_thread_critical_region(void);
168 void unprotect_thread_critical_region(void);
170 win_destroy_cb(GtkWindow *win _U_, gpointer data)
172 rpcstat_t *rs=(rpcstat_t *)data;
174 protect_thread_critical_region();
175 remove_tap_listener(rs);
176 unprotect_thread_critical_region();
178 free_srt_table_data(&rs->srt_table);
184 /* When called, this function will create a new instance of gtk-dcerpcstat.
187 gtk_dcerpcstat_init(char *optarg)
190 guint32 i, max_procs;
192 char filter_string[256];
194 GtkWidget *stat_label;
195 GtkWidget *filter_label;
198 dcerpc_sub_dissector *procs;
200 guint d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47;
205 GString *error_string;
209 * XXX - DCE RPC statistics are maintained only by major version,
210 * not by major and minor version, so the minor version number is
213 * Should we just stop supporting minor version numbers here?
214 * Or should we allow it to be omitted? Or should we keep
215 * separate statistics for different minor version numbers,
216 * and allow the minor version number to be omitted, and
217 * report aggregate statistics for all minor version numbers
220 if(sscanf(optarg,"dcerpc,srt,%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d,%n", &d1,&d2,&d3,&d40,&d41,&d42,&d43,&d44,&d45,&d46,&d47,&major,&minor,&pos)==13){
238 fprintf(stderr, "ethereal: invalid \"-z dcerpc,srt,<uuid>,<major version>.<minor version>[,<filter>]\" argument\n");
241 if (major < 0 || major > 65535) {
242 fprintf(stderr,"ethereal: dcerpcstat_init() Major version number %d is invalid - must be positive and <= 65535\n", major);
245 if (minor < 0 || minor > 65535) {
246 fprintf(stderr,"ethereal: dcerpcstat_init() Minor version number %d is invalid - must be positive and <= 65535\n", minor);
251 rs=g_malloc(sizeof(rpcstat_t));
252 rs->prog=dcerpc_get_proto_name(&uuid, ver);
255 fprintf(stderr,"ethereal: dcerpcstat_init() Protocol with uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x v%u not supported\n",uuid.Data1,uuid.Data2,uuid.Data3,uuid.Data4[0],uuid.Data4[1],uuid.Data4[2],uuid.Data4[3],uuid.Data4[4],uuid.Data4[5],uuid.Data4[6],uuid.Data4[7],ver);
258 hf_opnum=dcerpc_get_proto_hf_opnum(&uuid, ver);
259 procs=dcerpc_get_proto_sub_dissector(&uuid, ver);
263 rs->win=window_new(GTK_WINDOW_TOPLEVEL, "dcerpc-stat");
264 dcerpcstat_set_title(rs);
265 gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
267 vbox=gtk_vbox_new(FALSE, 3);
268 gtk_container_add(GTK_CONTAINER(rs->win), vbox);
269 gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
271 title_string=dcerpcstat_gen_title(rs);
272 stat_label=gtk_label_new(title_string);
273 g_free(title_string);
274 gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
276 g_snprintf(filter_string,255,"Filter:%s",filter?filter:"");
277 filter_label=gtk_label_new(filter_string);
278 gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
280 for(i=0,max_procs=0;procs[i].name;i++){
281 if(procs[i].num>max_procs){
282 max_procs=procs[i].num;
285 rs->num_procedures=max_procs+1;
287 /* We must display TOP LEVEL Widget before calling init_srt_table() */
288 gtk_widget_show_all(rs->win);
291 init_srt_table(&rs->srt_table, max_procs+1, vbox, proto_registrar_get_nth(hf_opnum)->abbrev);
293 init_srt_table(&rs->srt_table, max_procs+1, vbox, NULL);
296 for(i=0;i<(max_procs+1);i++){
301 for(j=0;procs[j].name;j++){
303 proc_name=procs[j].name;
307 init_srt_table_row(&rs->srt_table, i, proc_name);
311 error_string=register_tap_listener("dcerpc", rs, filter, dcerpcstat_reset, dcerpcstat_packet, dcerpcstat_draw);
313 /* error, we failed to attach to the tap. clean up */
314 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
315 g_string_free(error_string, TRUE);
316 free_srt_table_data(&rs->srt_table);
322 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
323 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
325 close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
326 window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
328 SIGNAL_CONNECT(rs->win, "delete_event", window_delete_event_cb, NULL);
329 SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
331 gtk_widget_show_all(rs->win);
332 window_present(rs->win);
334 retap_packets(&cfile);
339 static e_uuid_t *dcerpc_uuid_program=NULL;
340 static guint16 dcerpc_version;
341 static GtkWidget *dlg=NULL;
342 static GtkWidget *prog_menu;
343 static GtkWidget *vers_opt, *vers_menu;
344 static GtkWidget *filter_entry;
345 static dcerpc_uuid_key *current_uuid_key=NULL;
346 static dcerpc_uuid_value *current_uuid_value=NULL;
347 static dcerpc_uuid_key *new_uuid_key=NULL;
348 static dcerpc_uuid_value *new_uuid_value=NULL;
352 dcerpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
357 str = g_string_new("dcerpc,srt");
358 g_string_sprintfa(str,
359 ",%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%u.%u",
360 dcerpc_uuid_program->Data1, dcerpc_uuid_program->Data2,
361 dcerpc_uuid_program->Data3,
362 dcerpc_uuid_program->Data4[0], dcerpc_uuid_program->Data4[1],
363 dcerpc_uuid_program->Data4[2], dcerpc_uuid_program->Data4[3],
364 dcerpc_uuid_program->Data4[4], dcerpc_uuid_program->Data4[5],
365 dcerpc_uuid_program->Data4[6], dcerpc_uuid_program->Data4[7],
367 filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
369 g_string_sprintfa(str, ",%s", filter);
372 gtk_dcerpcstat_init(str->str);
373 g_string_free(str, TRUE);
378 dcerpcstat_version_select(GtkWidget *item _U_, gpointer key)
389 dcerpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
391 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
392 GtkWidget *menu_item;
395 if(!uuid_equal((&k->uuid), dcerpc_uuid_program)){
399 g_snprintf(vs, 5, "%u",k->ver);
400 menu_item=gtk_menu_item_new_with_label(vs);
401 SIGNAL_CONNECT(menu_item, "activate", dcerpcstat_version_select,
403 gtk_widget_show(menu_item);
404 gtk_menu_append(GTK_MENU(vers_menu), menu_item);
406 if(dcerpc_version==0xffff){
407 dcerpc_version=k->ver;
415 dcerpcstat_program_select(GtkWidget *item _U_, gpointer key)
417 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
419 dcerpc_uuid_program=&k->uuid;
421 /* change version menu */
422 dcerpc_version=0xffff;
423 gtk_object_destroy(GTK_OBJECT(vers_menu));
424 vers_menu=gtk_menu_new();
425 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
426 gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
430 static GtkWidget *program_submenu_menu;
431 static GtkWidget *program_submenu_item;
432 static GtkWidget *program_submenu_label;
433 static int program_subitem_index;
434 static char *first_menu_name;
436 dcerpcstat_add_program_to_menu(dcerpc_uuid_key *k, dcerpc_uuid_value *v)
438 GtkWidget *program_menu_item;
442 switch(program_subitem_index%15){
445 first_menu_name=v->name;
446 g_snprintf(str,63,"%s ...",v->name);
447 program_submenu_item=gtk_menu_item_new();
448 box=gtk_hbox_new(TRUE,0);
449 gtk_container_add(GTK_CONTAINER(program_submenu_item), box);
451 program_submenu_label=gtk_label_new(str);
452 gtk_box_pack_start(GTK_BOX(box), program_submenu_label, TRUE, TRUE, 0);
453 gtk_widget_show(program_submenu_label);
454 gtk_widget_show(box);
456 gtk_menu_append(GTK_MENU(prog_menu), program_submenu_item);
457 gtk_widget_show(program_submenu_item);
459 program_submenu_menu=gtk_menu_new();
460 gtk_menu_item_set_submenu(GTK_MENU_ITEM(program_submenu_item), program_submenu_menu);
463 g_snprintf(str,63,"%s - %s",first_menu_name,v->name);
464 gtk_label_set_text(GTK_LABEL(program_submenu_label), str);
468 program_subitem_index++;
470 program_menu_item=gtk_menu_item_new_with_label(v->name);
471 SIGNAL_CONNECT(program_menu_item, "activate", dcerpcstat_program_select, k);
473 gtk_widget_show(program_menu_item);
474 gtk_menu_append(GTK_MENU(program_submenu_menu), program_menu_item);
476 if(!dcerpc_uuid_program){
477 dcerpc_uuid_program=&k->uuid;
484 dcerpcstat_find_next_program(gpointer *key, gpointer *value, gpointer *user_data _U_)
486 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
487 dcerpc_uuid_value *v=(dcerpc_uuid_value *)value;
489 /* first time called, just set new_uuid to this one */
490 if((current_uuid_key==NULL)&&(new_uuid_key==NULL)){
496 /* if we havent got a current one yet, just check the new
497 and scan for the first one alphabetically */
498 if(current_uuid_key==NULL){
499 if(strcmp(new_uuid_value->name, v->name)>0){
507 /* searching for the next one we are only interested in those
508 that sorts alphabetically after the current one */
509 if(strcmp(current_uuid_value->name, v->name)>=0){
510 /* this one doesnt so just skip it */
514 /* is it the first potential new entry? */
515 if(new_uuid_key==NULL){
521 /* does it sort before the current new one? */
522 if(strcmp(new_uuid_value->name, v->name)>0){
540 gtk_dcerpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
543 GtkWidget *prog_box, *prog_label, *prog_opt;
544 GtkWidget *vers_label;
545 GtkWidget *filter_box, *filter_bt;
546 GtkWidget *bbox, *start_button, *cancel_button;
548 static construct_args_t args = {
549 "Service Response Time Statistics Filter",
554 /* if the window is already open, bring it to front and
555 un-minimize it, as necessary */
557 reactivate_window(dlg);
561 dlg=dlg_window_new("Ethereal: Compute DCE-RPC SRT statistics");
562 gtk_window_set_default_size(GTK_WINDOW(dlg), 400, -1);
564 dlg_box=gtk_vbox_new(FALSE, 10);
565 gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
566 gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
567 gtk_widget_show(dlg_box);
570 prog_box=gtk_hbox_new(FALSE, 3);
573 gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
574 prog_label=gtk_label_new("Program:");
575 gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
576 gtk_widget_show(prog_label);
579 prog_opt=gtk_option_menu_new();
580 prog_menu=gtk_menu_new();
581 current_uuid_key=NULL;
582 current_uuid_value=NULL;
584 program_submenu_item=NULL;
585 program_submenu_menu=NULL;
586 program_subitem_index=0;
590 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_next_program, NULL);
592 dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value);
594 current_uuid_key=new_uuid_key;
595 current_uuid_value=new_uuid_value;
596 } while(new_uuid_key!=NULL);
598 gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
599 gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
600 gtk_widget_show(prog_opt);
603 gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
604 vers_label=gtk_label_new("Version:");
605 gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
606 gtk_widget_show(vers_label);
609 vers_opt=gtk_option_menu_new();
610 vers_menu=gtk_menu_new();
611 dcerpc_version=0xffff;
612 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
613 gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
614 gtk_box_pack_start(GTK_BOX(prog_box), vers_opt, TRUE, TRUE, 0);
615 gtk_widget_show(vers_opt);
617 gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
618 gtk_widget_show(prog_box);
621 filter_box=gtk_hbox_new(FALSE, 3);
624 filter_bt=BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
625 SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
626 gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
627 gtk_widget_show(filter_bt);
630 filter_entry=gtk_entry_new();
631 SIGNAL_CONNECT(filter_entry, "changed", filter_te_syntax_check_cb, NULL);
632 gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
633 filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
635 gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
637 gtk_widget_show(filter_entry);
639 gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
640 gtk_widget_show(filter_box);
642 OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_entry);
645 bbox = dlg_button_row_new(ETHEREAL_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
646 gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
647 gtk_widget_show(bbox);
649 start_button = OBJECT_GET_DATA(bbox, ETHEREAL_STOCK_CREATE_STAT);
650 SIGNAL_CONNECT_OBJECT(start_button, "clicked",
651 dcerpcstat_start_button_clicked, NULL);
653 cancel_button = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
654 window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
656 SIGNAL_CONNECT(dlg, "delete_event", window_delete_event_cb, NULL);
657 SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
659 /* Catch the "activate" signal on the filter text entry, so that
660 if the user types Return there, we act as if the "Create Stat"
661 button had been selected, as happens if Return is typed if some
662 widget that *doesn't* handle the Return key has the input
664 dlg_set_activate(filter_entry, start_button);
666 gtk_widget_grab_default(start_button );
668 /* Give the initial focus to the "Filter" entry box. */
669 gtk_widget_grab_focus(filter_entry);
671 gtk_widget_show_all(dlg);
676 register_tap_listener_gtkdcerpcstat(void)
678 register_ethereal_tap("dcerpc,srt,", gtk_dcerpcstat_init);
680 register_tap_menu_item("DCE-RPC...", REGISTER_TAP_GROUP_RESPONSE_TIME,
681 gtk_dcerpcstat_cb, NULL, NULL, NULL);