2 * dcerpc_stat 2002 Ronnie Sahlberg
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
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 Wireshark,
26 * and displays them graphically.
27 * It is only used by Wireshark and not tshark
29 * It serves as an example on how to use the tap api.
40 #include <epan/packet_info.h>
41 #include <epan/epan.h>
42 #include <epan/stat_cmd_args.h>
44 #include <epan/dissectors/packet-dcerpc.h>
46 #include "../stat_menu.h"
47 #include "../simple_dialog.h"
48 #include "../register.h"
49 #include "../globals.h"
51 #include "gtk/gui_stat_menu.h"
52 #include "gtk/dlg_utils.h"
53 #include "gtk/gui_utils.h"
54 #include "gtk/filter_dlg.h"
55 #include "gtk/stock_icons.h"
56 #include "gtk/service_response_time_table.h"
57 #include "gtk/gtkglobals.h"
59 #include "gtk/filter_autocomplete.h"
62 /* used to keep track of the statistics for an entire program interface */
63 typedef struct _rpcstat_t {
65 srt_stat_table srt_table;
74 uuid_equal(e_uuid_t *uuid1, e_uuid_t *uuid2)
76 if( (uuid1->Data1!=uuid2->Data1)
77 ||(uuid1->Data2!=uuid2->Data2)
78 ||(uuid1->Data3!=uuid2->Data3)
79 ||(uuid1->Data4[0]!=uuid2->Data4[0])
80 ||(uuid1->Data4[1]!=uuid2->Data4[1])
81 ||(uuid1->Data4[2]!=uuid2->Data4[2])
82 ||(uuid1->Data4[3]!=uuid2->Data4[3])
83 ||(uuid1->Data4[4]!=uuid2->Data4[4])
84 ||(uuid1->Data4[5]!=uuid2->Data4[5])
85 ||(uuid1->Data4[6]!=uuid2->Data4[6])
86 ||(uuid1->Data4[7]!=uuid2->Data4[7]) ){
93 dcerpcstat_gen_title(rpcstat_t *rs)
97 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));
102 dcerpcstat_set_title(rpcstat_t *rs)
106 title = dcerpcstat_gen_title(rs);
107 gtk_window_set_title(GTK_WINDOW(rs->win), title);
112 dcerpcstat_reset(void *rs_arg)
114 rpcstat_t *rs = rs_arg;
116 reset_srt_table_data(&rs->srt_table);
117 dcerpcstat_set_title(rs);
122 dcerpcstat_packet(void *rs_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ri_arg)
124 rpcstat_t *rs = rs_arg;
125 const dcerpc_info *ri = ri_arg;
130 if(!ri->call_data->req_frame){
131 /* we have not seen the request so we dont know the delta*/
134 if(ri->call_data->opnum>=rs->num_procedures){
135 /* dont handle this since its outside of known table */
139 /* we are only interested in reply packets */
140 if(ri->ptype != PDU_RESP){
144 /* we are only interested in certain program/versions */
145 if( (!uuid_equal( (&ri->call_data->uuid), (&rs->uuid)))
146 ||(ri->call_data->ver!=rs->ver)){
151 add_srt_table_data(&rs->srt_table, ri->call_data->opnum, &ri->call_data->req_time, pinfo);
158 dcerpcstat_draw(void *rs_arg)
160 rpcstat_t *rs = rs_arg;
162 draw_srt_table_data(&rs->srt_table);
166 /* since the gtk2 implementation of tap is multithreaded we must protect
167 * remove_tap_listener() from modifying the list while draw_tap_listener()
168 * is running. the other protected block is in main.c
170 * there should not be any other critical regions in gtk2
173 win_destroy_cb(GtkWindow *win _U_, gpointer data)
175 rpcstat_t *rs=(rpcstat_t *)data;
177 protect_thread_critical_region();
178 remove_tap_listener(rs);
179 unprotect_thread_critical_region();
181 free_srt_table_data(&rs->srt_table);
187 /* When called, this function will create a new instance of gtk-dcerpcstat.
190 gtk_dcerpcstat_init(const char *optarg, void* userdata _U_)
193 guint32 i, max_procs;
197 GtkWidget *stat_label;
198 GtkWidget *filter_label;
201 dcerpc_sub_dissector *procs;
203 guint d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47;
207 const char *filter=NULL;
208 GString *error_string;
212 * XXX - DCE RPC statistics are maintained only by major version,
213 * not by major and minor version, so the minor version number is
216 * Should we just stop supporting minor version numbers here?
217 * Or should we allow it to be omitted? Or should we keep
218 * separate statistics for different minor version numbers,
219 * and allow the minor version number to be omitted, and
220 * report aggregate statistics for all minor version numbers
223 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){
241 fprintf(stderr, "wireshark: invalid \"-z dcerpc,srt,<uuid>,<major version>.<minor version>[,<filter>]\" argument\n");
244 if (major < 0 || major > 65535) {
245 fprintf(stderr,"wireshark: dcerpcstat_init() Major version number %d is invalid - must be positive and <= 65535\n", major);
248 if (minor < 0 || minor > 65535) {
249 fprintf(stderr,"wireshark: dcerpcstat_init() Minor version number %d is invalid - must be positive and <= 65535\n", minor);
254 rs=g_malloc(sizeof(rpcstat_t));
255 rs->prog=dcerpc_get_proto_name(&uuid, ver);
258 fprintf(stderr,"wireshark: 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);
261 hf_opnum=dcerpc_get_proto_hf_opnum(&uuid, ver);
262 procs=dcerpc_get_proto_sub_dissector(&uuid, ver);
266 rs->win = dlg_window_new("dcerpc-stat"); /* transient_for top_level */
267 gtk_window_set_destroy_with_parent (GTK_WINDOW(rs->win), TRUE);
269 dcerpcstat_set_title(rs);
270 gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
272 vbox=gtk_vbox_new(FALSE, 3);
273 gtk_container_add(GTK_CONTAINER(rs->win), vbox);
274 gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
276 title_string=dcerpcstat_gen_title(rs);
277 stat_label=gtk_label_new(title_string);
278 g_free(title_string);
279 gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
281 filter_string = g_strdup_printf("Filter: %s",filter ? filter : "");
282 filter_label=gtk_label_new(filter_string);
283 g_free(filter_string);
284 gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
285 gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
287 for(i=0,max_procs=0;procs[i].name;i++){
288 if(procs[i].num>max_procs){
289 max_procs=procs[i].num;
292 rs->num_procedures=max_procs+1;
294 /* We must display TOP LEVEL Widget before calling init_srt_table() */
295 gtk_widget_show_all(rs->win);
298 init_srt_table(&rs->srt_table, max_procs+1, vbox, proto_registrar_get_nth(hf_opnum)->abbrev);
300 init_srt_table(&rs->srt_table, max_procs+1, vbox, NULL);
303 for(i=0;i<(max_procs+1);i++){
305 const char *proc_name;
308 for(j=0;procs[j].name;j++){
310 proc_name=procs[j].name;
314 init_srt_table_row(&rs->srt_table, i, proc_name);
318 error_string=register_tap_listener("dcerpc", rs, filter, 0, dcerpcstat_reset, dcerpcstat_packet, dcerpcstat_draw);
320 /* error, we failed to attach to the tap. clean up */
321 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
322 g_string_free(error_string, TRUE);
323 free_srt_table_data(&rs->srt_table);
329 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
330 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
332 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
333 window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
335 g_signal_connect(rs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
336 g_signal_connect(rs->win, "destroy", G_CALLBACK(win_destroy_cb), rs);
338 gtk_widget_show_all(rs->win);
339 window_present(rs->win);
341 cf_retap_packets(&cfile);
342 gdk_window_raise(rs->win->window);
347 static e_uuid_t *dcerpc_uuid_program=NULL;
348 static guint16 dcerpc_version;
349 static GtkWidget *dlg=NULL;
350 static GtkWidget *prog_menu;
351 static GtkWidget *vers_opt, *vers_menu;
352 static GtkWidget *filter_entry;
353 static dcerpc_uuid_key *current_uuid_key=NULL;
354 static dcerpc_uuid_value *current_uuid_value=NULL;
355 static dcerpc_uuid_key *new_uuid_key=NULL;
356 static dcerpc_uuid_value *new_uuid_value=NULL;
360 dcerpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
365 str = g_string_new("dcerpc,srt");
366 g_string_append_printf(str,
367 ",%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%u.%u",
368 dcerpc_uuid_program->Data1, dcerpc_uuid_program->Data2,
369 dcerpc_uuid_program->Data3,
370 dcerpc_uuid_program->Data4[0], dcerpc_uuid_program->Data4[1],
371 dcerpc_uuid_program->Data4[2], dcerpc_uuid_program->Data4[3],
372 dcerpc_uuid_program->Data4[4], dcerpc_uuid_program->Data4[5],
373 dcerpc_uuid_program->Data4[6], dcerpc_uuid_program->Data4[7],
375 filter=gtk_entry_get_text(GTK_ENTRY(filter_entry));
377 g_string_append_printf(str, ",%s", filter);
380 gtk_dcerpcstat_init(str->str,NULL);
381 g_string_free(str, TRUE);
386 dcerpcstat_version_select(GtkWidget *item _U_, gpointer key)
397 dcerpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
399 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
400 GtkWidget *menu_item;
403 if(!uuid_equal((&k->uuid), dcerpc_uuid_program)){
407 g_snprintf(vs, sizeof(vs), "%u",k->ver);
408 menu_item=gtk_menu_item_new_with_label(vs);
409 g_signal_connect(menu_item, "activate", G_CALLBACK(dcerpcstat_version_select),
410 (gpointer)((long)k->ver));
411 gtk_widget_show(menu_item);
412 gtk_menu_shell_append(GTK_MENU_SHELL(vers_menu), menu_item);
414 if(dcerpc_version==0xffff){
415 dcerpc_version=k->ver;
423 dcerpcstat_program_select(GtkWidget *item _U_, gpointer key)
425 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
427 dcerpc_uuid_program=&k->uuid;
429 /* change version menu */
430 dcerpc_version=0xffff;
431 gtk_object_destroy(GTK_OBJECT(vers_menu));
432 vers_menu=gtk_menu_new();
433 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
434 gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
438 static GtkWidget *program_submenu_menu;
439 static GtkWidget *program_submenu_item;
440 static GtkWidget *program_submenu_label;
441 static int program_subitem_index;
442 static const char *first_menu_name;
444 dcerpcstat_add_program_to_menu(dcerpc_uuid_key *k, dcerpc_uuid_value *v)
446 GtkWidget *program_menu_item;
450 switch(program_subitem_index%15){
453 first_menu_name=v->name;
454 g_snprintf(str,sizeof(str),"%s ...",v->name);
455 program_submenu_item=gtk_menu_item_new();
456 box=gtk_hbox_new(TRUE,0);
457 gtk_container_add(GTK_CONTAINER(program_submenu_item), box);
459 program_submenu_label=gtk_label_new(str);
460 gtk_box_pack_start(GTK_BOX(box), program_submenu_label, TRUE, TRUE, 0);
461 gtk_widget_show(program_submenu_label);
462 gtk_widget_show(box);
464 gtk_menu_shell_append(GTK_MENU_SHELL(prog_menu), program_submenu_item);
465 gtk_widget_show(program_submenu_item);
467 program_submenu_menu=gtk_menu_new();
468 gtk_menu_item_set_submenu(GTK_MENU_ITEM(program_submenu_item), program_submenu_menu);
471 g_snprintf(str,sizeof(str),"%s - %s",first_menu_name,v->name);
472 gtk_label_set_text(GTK_LABEL(program_submenu_label), str);
476 program_subitem_index++;
478 program_menu_item=gtk_menu_item_new_with_label(v->name);
479 g_signal_connect(program_menu_item, "activate", G_CALLBACK(dcerpcstat_program_select), k);
481 gtk_widget_show(program_menu_item);
482 gtk_menu_shell_append(GTK_MENU_SHELL(program_submenu_menu), program_menu_item);
484 if(!dcerpc_uuid_program){
485 dcerpc_uuid_program=&k->uuid;
492 dcerpcstat_find_next_program(gpointer *key, gpointer *value, gpointer *user_data _U_)
494 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
495 dcerpc_uuid_value *v=(dcerpc_uuid_value *)value;
497 /* first time called, just set new_uuid to this one */
498 if((current_uuid_key==NULL)&&(new_uuid_key==NULL)){
504 /* if we havent got a current one yet, just check the new
505 and scan for the first one alphabetically */
506 if(current_uuid_key==NULL){
507 if(strcmp(new_uuid_value->name, v->name)>0){
515 /* searching for the next one we are only interested in those
516 that sorts alphabetically after the current one */
517 if(strcmp(current_uuid_value->name, v->name)>=0){
518 /* this one doesnt so just skip it */
522 /* is it the first potential new entry? */
523 if(new_uuid_key==NULL){
529 /* does it sort before the current new one? */
530 if(strcmp(new_uuid_value->name, v->name)>0){
548 gtk_dcerpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
551 GtkWidget *prog_box, *prog_label, *prog_opt;
552 GtkWidget *vers_label;
553 GtkWidget *filter_box, *filter_bt;
554 GtkWidget *bbox, *start_button, *cancel_button;
556 static construct_args_t args = {
557 "Service Response Time Statistics Filter",
563 /* if the window is already open, bring it to front and
564 un-minimize it, as necessary */
566 reactivate_window(dlg);
570 dlg=dlg_window_new("Wireshark: Compute DCE-RPC SRT statistics");
571 gtk_window_set_default_size(GTK_WINDOW(dlg), 400, -1);
573 dlg_box=gtk_vbox_new(FALSE, 10);
574 gtk_container_set_border_width(GTK_CONTAINER(dlg_box), 10);
575 gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
576 gtk_widget_show(dlg_box);
579 prog_box=gtk_hbox_new(FALSE, 3);
582 gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
583 prog_label=gtk_label_new("Program:");
584 gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
585 gtk_widget_show(prog_label);
588 prog_opt=gtk_option_menu_new();
589 prog_menu=gtk_menu_new();
590 current_uuid_key=NULL;
591 current_uuid_value=NULL;
593 program_submenu_item=NULL;
594 program_submenu_menu=NULL;
595 program_subitem_index=0;
599 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_next_program, NULL);
601 dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value);
603 current_uuid_key=new_uuid_key;
604 current_uuid_value=new_uuid_value;
605 } while(new_uuid_key!=NULL);
607 gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
608 gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
609 gtk_widget_show(prog_opt);
612 gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
613 vers_label=gtk_label_new("Version:");
614 gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
615 gtk_widget_show(vers_label);
618 vers_opt=gtk_option_menu_new();
619 vers_menu=gtk_menu_new();
620 dcerpc_version=0xffff;
621 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
622 gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
623 gtk_box_pack_start(GTK_BOX(prog_box), vers_opt, TRUE, TRUE, 0);
624 gtk_widget_show(vers_opt);
626 gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
627 gtk_widget_show(prog_box);
630 filter_box=gtk_hbox_new(FALSE, 3);
633 filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
634 g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &args);
635 gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
636 gtk_widget_show(filter_bt);
639 filter_entry=gtk_entry_new();
640 g_signal_connect(filter_entry, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
641 g_object_set_data(G_OBJECT(filter_box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
642 g_signal_connect(filter_entry, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
643 g_signal_connect(dlg, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
644 gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
645 filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
647 gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
649 colorize_filter_te_as_empty(filter_entry);
651 gtk_widget_show(filter_entry);
653 gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
654 gtk_widget_show(filter_box);
656 g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_entry);
659 bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
660 gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
661 gtk_widget_show(bbox);
663 start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
664 g_signal_connect_swapped(start_button, "clicked",
665 G_CALLBACK(dcerpcstat_start_button_clicked), NULL);
667 cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
668 window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
670 g_signal_connect(dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
671 g_signal_connect(dlg, "destroy", dlg_destroy_cb, NULL);
673 /* Catch the "activate" signal on the filter text entry, so that
674 if the user types Return there, we act as if the "Create Stat"
675 button had been selected, as happens if Return is typed if some
676 widget that *doesn't* handle the Return key has the input
678 dlg_set_activate(filter_entry, start_button);
680 gtk_widget_grab_default(start_button );
682 /* Give the initial focus to the "Filter" entry box. */
683 gtk_widget_grab_focus(filter_entry);
685 gtk_widget_show_all(dlg);
690 register_tap_listener_gtkdcerpcstat(void)
692 register_stat_cmd_arg("dcerpc,srt,", gtk_dcerpcstat_init,NULL);
694 register_stat_menu_item("DCE-RPC...", REGISTER_STAT_GROUP_RESPONSE_TIME,
695 gtk_dcerpcstat_cb, NULL, NULL, NULL);