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=window_new(GTK_WINDOW_TOPLEVEL, "dcerpc-stat");
267 dcerpcstat_set_title(rs);
268 gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
270 vbox=gtk_vbox_new(FALSE, 3);
271 gtk_container_add(GTK_CONTAINER(rs->win), vbox);
272 gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
274 title_string=dcerpcstat_gen_title(rs);
275 stat_label=gtk_label_new(title_string);
276 g_free(title_string);
277 gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
279 filter_string = g_strdup_printf("Filter: %s",filter ? filter : "");
280 filter_label=gtk_label_new(filter_string);
281 g_free(filter_string);
282 gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
283 gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
285 for(i=0,max_procs=0;procs[i].name;i++){
286 if(procs[i].num>max_procs){
287 max_procs=procs[i].num;
290 rs->num_procedures=max_procs+1;
292 /* We must display TOP LEVEL Widget before calling init_srt_table() */
293 gtk_widget_show_all(rs->win);
296 init_srt_table(&rs->srt_table, max_procs+1, vbox, proto_registrar_get_nth(hf_opnum)->abbrev);
298 init_srt_table(&rs->srt_table, max_procs+1, vbox, NULL);
301 for(i=0;i<(max_procs+1);i++){
303 const char *proc_name;
306 for(j=0;procs[j].name;j++){
308 proc_name=procs[j].name;
312 init_srt_table_row(&rs->srt_table, i, proc_name);
316 error_string=register_tap_listener("dcerpc", rs, filter, dcerpcstat_reset, dcerpcstat_packet, dcerpcstat_draw);
318 /* error, we failed to attach to the tap. clean up */
319 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
320 g_string_free(error_string, TRUE);
321 free_srt_table_data(&rs->srt_table);
327 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
328 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
330 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
331 window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
333 g_signal_connect(rs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
334 g_signal_connect(rs->win, "destroy", G_CALLBACK(win_destroy_cb), rs);
336 gtk_widget_show_all(rs->win);
337 window_present(rs->win);
339 cf_retap_packets(&cfile, FALSE);
340 gdk_window_raise(rs->win->window);
345 static e_uuid_t *dcerpc_uuid_program=NULL;
346 static guint16 dcerpc_version;
347 static GtkWidget *dlg=NULL;
348 static GtkWidget *prog_menu;
349 static GtkWidget *vers_opt, *vers_menu;
350 static GtkWidget *filter_entry;
351 static dcerpc_uuid_key *current_uuid_key=NULL;
352 static dcerpc_uuid_value *current_uuid_value=NULL;
353 static dcerpc_uuid_key *new_uuid_key=NULL;
354 static dcerpc_uuid_value *new_uuid_value=NULL;
358 dcerpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
363 str = g_string_new("dcerpc,srt");
364 g_string_append_printf(str,
365 ",%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%u.%u",
366 dcerpc_uuid_program->Data1, dcerpc_uuid_program->Data2,
367 dcerpc_uuid_program->Data3,
368 dcerpc_uuid_program->Data4[0], dcerpc_uuid_program->Data4[1],
369 dcerpc_uuid_program->Data4[2], dcerpc_uuid_program->Data4[3],
370 dcerpc_uuid_program->Data4[4], dcerpc_uuid_program->Data4[5],
371 dcerpc_uuid_program->Data4[6], dcerpc_uuid_program->Data4[7],
373 filter=gtk_entry_get_text(GTK_ENTRY(filter_entry));
375 g_string_append_printf(str, ",%s", filter);
378 gtk_dcerpcstat_init(str->str,NULL);
379 g_string_free(str, TRUE);
384 dcerpcstat_version_select(GtkWidget *item _U_, gpointer key)
395 dcerpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
397 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
398 GtkWidget *menu_item;
401 if(!uuid_equal((&k->uuid), dcerpc_uuid_program)){
405 g_snprintf(vs, sizeof(vs), "%u",k->ver);
406 menu_item=gtk_menu_item_new_with_label(vs);
407 g_signal_connect(menu_item, "activate", G_CALLBACK(dcerpcstat_version_select),
408 (gpointer)((long)k->ver));
409 gtk_widget_show(menu_item);
410 gtk_menu_shell_append(GTK_MENU_SHELL(vers_menu), menu_item);
412 if(dcerpc_version==0xffff){
413 dcerpc_version=k->ver;
421 dcerpcstat_program_select(GtkWidget *item _U_, gpointer key)
423 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
425 dcerpc_uuid_program=&k->uuid;
427 /* change version menu */
428 dcerpc_version=0xffff;
429 gtk_object_destroy(GTK_OBJECT(vers_menu));
430 vers_menu=gtk_menu_new();
431 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
432 gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
436 static GtkWidget *program_submenu_menu;
437 static GtkWidget *program_submenu_item;
438 static GtkWidget *program_submenu_label;
439 static int program_subitem_index;
440 static const char *first_menu_name;
442 dcerpcstat_add_program_to_menu(dcerpc_uuid_key *k, dcerpc_uuid_value *v)
444 GtkWidget *program_menu_item;
448 switch(program_subitem_index%15){
451 first_menu_name=v->name;
452 g_snprintf(str,sizeof(str),"%s ...",v->name);
453 program_submenu_item=gtk_menu_item_new();
454 box=gtk_hbox_new(TRUE,0);
455 gtk_container_add(GTK_CONTAINER(program_submenu_item), box);
457 program_submenu_label=gtk_label_new(str);
458 gtk_box_pack_start(GTK_BOX(box), program_submenu_label, TRUE, TRUE, 0);
459 gtk_widget_show(program_submenu_label);
460 gtk_widget_show(box);
462 gtk_menu_shell_append(GTK_MENU_SHELL(prog_menu), program_submenu_item);
463 gtk_widget_show(program_submenu_item);
465 program_submenu_menu=gtk_menu_new();
466 gtk_menu_item_set_submenu(GTK_MENU_ITEM(program_submenu_item), program_submenu_menu);
469 g_snprintf(str,sizeof(str),"%s - %s",first_menu_name,v->name);
470 gtk_label_set_text(GTK_LABEL(program_submenu_label), str);
474 program_subitem_index++;
476 program_menu_item=gtk_menu_item_new_with_label(v->name);
477 g_signal_connect(program_menu_item, "activate", G_CALLBACK(dcerpcstat_program_select), k);
479 gtk_widget_show(program_menu_item);
480 gtk_menu_shell_append(GTK_MENU_SHELL(program_submenu_menu), program_menu_item);
482 if(!dcerpc_uuid_program){
483 dcerpc_uuid_program=&k->uuid;
490 dcerpcstat_find_next_program(gpointer *key, gpointer *value, gpointer *user_data _U_)
492 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
493 dcerpc_uuid_value *v=(dcerpc_uuid_value *)value;
495 /* first time called, just set new_uuid to this one */
496 if((current_uuid_key==NULL)&&(new_uuid_key==NULL)){
502 /* if we havent got a current one yet, just check the new
503 and scan for the first one alphabetically */
504 if(current_uuid_key==NULL){
505 if(strcmp(new_uuid_value->name, v->name)>0){
513 /* searching for the next one we are only interested in those
514 that sorts alphabetically after the current one */
515 if(strcmp(current_uuid_value->name, v->name)>=0){
516 /* this one doesnt so just skip it */
520 /* is it the first potential new entry? */
521 if(new_uuid_key==NULL){
527 /* does it sort before the current new one? */
528 if(strcmp(new_uuid_value->name, v->name)>0){
546 gtk_dcerpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
549 GtkWidget *prog_box, *prog_label, *prog_opt;
550 GtkWidget *vers_label;
551 GtkWidget *filter_box, *filter_bt;
552 GtkWidget *bbox, *start_button, *cancel_button;
554 static construct_args_t args = {
555 "Service Response Time Statistics Filter",
561 /* if the window is already open, bring it to front and
562 un-minimize it, as necessary */
564 reactivate_window(dlg);
568 dlg=dlg_window_new("Wireshark: Compute DCE-RPC SRT statistics");
569 gtk_window_set_default_size(GTK_WINDOW(dlg), 400, -1);
571 dlg_box=gtk_vbox_new(FALSE, 10);
572 gtk_container_set_border_width(GTK_CONTAINER(dlg_box), 10);
573 gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
574 gtk_widget_show(dlg_box);
577 prog_box=gtk_hbox_new(FALSE, 3);
580 gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
581 prog_label=gtk_label_new("Program:");
582 gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
583 gtk_widget_show(prog_label);
586 prog_opt=gtk_option_menu_new();
587 prog_menu=gtk_menu_new();
588 current_uuid_key=NULL;
589 current_uuid_value=NULL;
591 program_submenu_item=NULL;
592 program_submenu_menu=NULL;
593 program_subitem_index=0;
597 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_next_program, NULL);
599 dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value);
601 current_uuid_key=new_uuid_key;
602 current_uuid_value=new_uuid_value;
603 } while(new_uuid_key!=NULL);
605 gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
606 gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
607 gtk_widget_show(prog_opt);
610 gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
611 vers_label=gtk_label_new("Version:");
612 gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
613 gtk_widget_show(vers_label);
616 vers_opt=gtk_option_menu_new();
617 vers_menu=gtk_menu_new();
618 dcerpc_version=0xffff;
619 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
620 gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
621 gtk_box_pack_start(GTK_BOX(prog_box), vers_opt, TRUE, TRUE, 0);
622 gtk_widget_show(vers_opt);
624 gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
625 gtk_widget_show(prog_box);
628 filter_box=gtk_hbox_new(FALSE, 3);
631 filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
632 g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &args);
633 gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
634 gtk_widget_show(filter_bt);
637 filter_entry=gtk_entry_new();
638 g_signal_connect(filter_entry, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
639 g_object_set_data(G_OBJECT(filter_box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
640 g_signal_connect(filter_entry, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
641 g_signal_connect(dlg, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
642 gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
643 filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
645 gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
647 colorize_filter_te_as_empty(filter_entry);
649 gtk_widget_show(filter_entry);
651 gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
652 gtk_widget_show(filter_box);
654 g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_entry);
657 bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
658 gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
659 gtk_widget_show(bbox);
661 start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
662 g_signal_connect_swapped(start_button, "clicked",
663 G_CALLBACK(dcerpcstat_start_button_clicked), NULL);
665 cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
666 window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
668 g_signal_connect(dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
669 g_signal_connect(dlg, "destroy", dlg_destroy_cb, NULL);
671 /* Catch the "activate" signal on the filter text entry, so that
672 if the user types Return there, we act as if the "Create Stat"
673 button had been selected, as happens if Return is typed if some
674 widget that *doesn't* handle the Return key has the input
676 dlg_set_activate(filter_entry, start_button);
678 gtk_widget_grab_default(start_button );
680 /* Give the initial focus to the "Filter" entry box. */
681 gtk_widget_grab_focus(filter_entry);
683 gtk_widget_show_all(dlg);
688 register_tap_listener_gtkdcerpcstat(void)
690 register_stat_cmd_arg("dcerpc,srt,", gtk_dcerpcstat_init,NULL);
692 register_stat_menu_item("DCE-RPC...", REGISTER_STAT_GROUP_RESPONSE_TIME,
693 gtk_dcerpcstat_cb, NULL, NULL, NULL);