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.
41 #include <epan/packet_info.h>
42 #include <epan/epan.h>
44 #include <epan/stat_cmd_args.h>
45 #include "../stat_menu.h"
46 #include "gui_stat_menu.h"
47 #include "simple_dialog.h"
48 #include "dlg_utils.h"
49 #include "gui_utils.h"
51 #include "../register.h"
52 #include <epan/dissectors/packet-dcerpc.h>
53 #include "../globals.h"
54 #include "filter_dlg.h"
55 #include "compat_macros.h"
56 #include "service_response_time_table.h"
57 #include "gtkglobals.h"
60 /* used to keep track of the statistics for an entire program interface */
61 typedef struct _rpcstat_t {
63 srt_stat_table srt_table;
72 uuid_equal(e_uuid_t *uuid1, e_uuid_t *uuid2)
74 if( (uuid1->Data1!=uuid2->Data1)
75 ||(uuid1->Data2!=uuid2->Data2)
76 ||(uuid1->Data3!=uuid2->Data3)
77 ||(uuid1->Data4[0]!=uuid2->Data4[0])
78 ||(uuid1->Data4[1]!=uuid2->Data4[1])
79 ||(uuid1->Data4[2]!=uuid2->Data4[2])
80 ||(uuid1->Data4[3]!=uuid2->Data4[3])
81 ||(uuid1->Data4[4]!=uuid2->Data4[4])
82 ||(uuid1->Data4[5]!=uuid2->Data4[5])
83 ||(uuid1->Data4[6]!=uuid2->Data4[6])
84 ||(uuid1->Data4[7]!=uuid2->Data4[7]) ){
91 dcerpcstat_gen_title(rpcstat_t *rs)
95 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));
100 dcerpcstat_set_title(rpcstat_t *rs)
104 title = dcerpcstat_gen_title(rs);
105 gtk_window_set_title(GTK_WINDOW(rs->win), title);
110 dcerpcstat_reset(void *rs_arg)
112 rpcstat_t *rs = rs_arg;
114 reset_srt_table_data(&rs->srt_table);
115 dcerpcstat_set_title(rs);
120 dcerpcstat_packet(void *rs_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ri_arg)
122 rpcstat_t *rs = rs_arg;
123 const dcerpc_info *ri = ri_arg;
128 if(!ri->call_data->req_frame){
129 /* we have not seen the request so we dont know the delta*/
132 if(ri->call_data->opnum>=rs->num_procedures){
133 /* dont handle this since its outside of known table */
137 /* we are only interested in reply packets */
138 if(ri->ptype != PDU_RESP){
142 /* we are only interested in certain program/versions */
143 if( (!uuid_equal( (&ri->call_data->uuid), (&rs->uuid)))
144 ||(ri->call_data->ver!=rs->ver)){
149 add_srt_table_data(&rs->srt_table, ri->call_data->opnum, &ri->call_data->req_time, pinfo);
156 dcerpcstat_draw(void *rs_arg)
158 rpcstat_t *rs = rs_arg;
160 draw_srt_table_data(&rs->srt_table);
164 /* since the gtk2 implementation of tap is multithreaded we must protect
165 * remove_tap_listener() from modifying the list while draw_tap_listener()
166 * is running. the other protected block is in main.c
168 * there should not be any other critical regions in gtk2
170 void protect_thread_critical_region(void);
171 void unprotect_thread_critical_region(void);
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, 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 SIGNAL_CONNECT(rs->win, "delete_event", window_delete_event_cb, NULL);
334 SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
336 gtk_widget_show_all(rs->win);
337 window_present(rs->win);
339 cf_retap_packets(&cfile, FALSE);
344 static e_uuid_t *dcerpc_uuid_program=NULL;
345 static guint16 dcerpc_version;
346 static GtkWidget *dlg=NULL;
347 static GtkWidget *prog_menu;
348 static GtkWidget *vers_opt, *vers_menu;
349 static GtkWidget *filter_entry;
350 static dcerpc_uuid_key *current_uuid_key=NULL;
351 static dcerpc_uuid_value *current_uuid_value=NULL;
352 static dcerpc_uuid_key *new_uuid_key=NULL;
353 static dcerpc_uuid_value *new_uuid_value=NULL;
357 dcerpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
362 str = g_string_new("dcerpc,srt");
363 g_string_sprintfa(str,
364 ",%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%u.%u",
365 dcerpc_uuid_program->Data1, dcerpc_uuid_program->Data2,
366 dcerpc_uuid_program->Data3,
367 dcerpc_uuid_program->Data4[0], dcerpc_uuid_program->Data4[1],
368 dcerpc_uuid_program->Data4[2], dcerpc_uuid_program->Data4[3],
369 dcerpc_uuid_program->Data4[4], dcerpc_uuid_program->Data4[5],
370 dcerpc_uuid_program->Data4[6], dcerpc_uuid_program->Data4[7],
372 filter=gtk_entry_get_text(GTK_ENTRY(filter_entry));
374 g_string_sprintfa(str, ",%s", filter);
377 gtk_dcerpcstat_init(str->str,NULL);
378 g_string_free(str, TRUE);
383 dcerpcstat_version_select(GtkWidget *item _U_, gpointer key)
394 dcerpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
396 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
397 GtkWidget *menu_item;
400 if(!uuid_equal((&k->uuid), dcerpc_uuid_program)){
404 g_snprintf(vs, 5, "%u",k->ver);
405 menu_item=gtk_menu_item_new_with_label(vs);
406 SIGNAL_CONNECT(menu_item, "activate", dcerpcstat_version_select,
408 gtk_widget_show(menu_item);
409 gtk_menu_append(GTK_MENU(vers_menu), menu_item);
411 if(dcerpc_version==0xffff){
412 dcerpc_version=k->ver;
420 dcerpcstat_program_select(GtkWidget *item _U_, gpointer key)
422 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
424 dcerpc_uuid_program=&k->uuid;
426 /* change version menu */
427 dcerpc_version=0xffff;
428 gtk_object_destroy(GTK_OBJECT(vers_menu));
429 vers_menu=gtk_menu_new();
430 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
431 gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
435 static GtkWidget *program_submenu_menu;
436 static GtkWidget *program_submenu_item;
437 static GtkWidget *program_submenu_label;
438 static int program_subitem_index;
439 static const char *first_menu_name;
441 dcerpcstat_add_program_to_menu(dcerpc_uuid_key *k, dcerpc_uuid_value *v)
443 GtkWidget *program_menu_item;
447 switch(program_subitem_index%15){
450 first_menu_name=v->name;
451 g_snprintf(str,63,"%s ...",v->name);
452 program_submenu_item=gtk_menu_item_new();
453 box=gtk_hbox_new(TRUE,0);
454 gtk_container_add(GTK_CONTAINER(program_submenu_item), box);
456 program_submenu_label=gtk_label_new(str);
457 gtk_box_pack_start(GTK_BOX(box), program_submenu_label, TRUE, TRUE, 0);
458 gtk_widget_show(program_submenu_label);
459 gtk_widget_show(box);
461 gtk_menu_append(GTK_MENU(prog_menu), program_submenu_item);
462 gtk_widget_show(program_submenu_item);
464 program_submenu_menu=gtk_menu_new();
465 gtk_menu_item_set_submenu(GTK_MENU_ITEM(program_submenu_item), program_submenu_menu);
468 g_snprintf(str,63,"%s - %s",first_menu_name,v->name);
469 gtk_label_set_text(GTK_LABEL(program_submenu_label), str);
473 program_subitem_index++;
475 program_menu_item=gtk_menu_item_new_with_label(v->name);
476 SIGNAL_CONNECT(program_menu_item, "activate", dcerpcstat_program_select, k);
478 gtk_widget_show(program_menu_item);
479 gtk_menu_append(GTK_MENU(program_submenu_menu), program_menu_item);
481 if(!dcerpc_uuid_program){
482 dcerpc_uuid_program=&k->uuid;
489 dcerpcstat_find_next_program(gpointer *key, gpointer *value, gpointer *user_data _U_)
491 dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
492 dcerpc_uuid_value *v=(dcerpc_uuid_value *)value;
494 /* first time called, just set new_uuid to this one */
495 if((current_uuid_key==NULL)&&(new_uuid_key==NULL)){
501 /* if we havent got a current one yet, just check the new
502 and scan for the first one alphabetically */
503 if(current_uuid_key==NULL){
504 if(strcmp(new_uuid_value->name, v->name)>0){
512 /* searching for the next one we are only interested in those
513 that sorts alphabetically after the current one */
514 if(strcmp(current_uuid_value->name, v->name)>=0){
515 /* this one doesnt so just skip it */
519 /* is it the first potential new entry? */
520 if(new_uuid_key==NULL){
526 /* does it sort before the current new one? */
527 if(strcmp(new_uuid_value->name, v->name)>0){
545 gtk_dcerpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
548 GtkWidget *prog_box, *prog_label, *prog_opt;
549 GtkWidget *vers_label;
550 GtkWidget *filter_box, *filter_bt;
551 GtkWidget *bbox, *start_button, *cancel_button;
553 static construct_args_t args = {
554 "Service Response Time Statistics Filter",
560 /* if the window is already open, bring it to front and
561 un-minimize it, as necessary */
563 reactivate_window(dlg);
567 dlg=dlg_window_new("Wireshark: Compute DCE-RPC SRT statistics");
568 gtk_window_set_default_size(GTK_WINDOW(dlg), 400, -1);
570 dlg_box=gtk_vbox_new(FALSE, 10);
571 gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
572 gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
573 gtk_widget_show(dlg_box);
576 prog_box=gtk_hbox_new(FALSE, 3);
579 gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
580 prog_label=gtk_label_new("Program:");
581 gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
582 gtk_widget_show(prog_label);
585 prog_opt=gtk_option_menu_new();
586 prog_menu=gtk_menu_new();
587 current_uuid_key=NULL;
588 current_uuid_value=NULL;
590 program_submenu_item=NULL;
591 program_submenu_menu=NULL;
592 program_subitem_index=0;
596 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_next_program, NULL);
598 dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value);
600 current_uuid_key=new_uuid_key;
601 current_uuid_value=new_uuid_value;
602 } while(new_uuid_key!=NULL);
604 gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
605 gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
606 gtk_widget_show(prog_opt);
609 gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
610 vers_label=gtk_label_new("Version:");
611 gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
612 gtk_widget_show(vers_label);
615 vers_opt=gtk_option_menu_new();
616 vers_menu=gtk_menu_new();
617 dcerpc_version=0xffff;
618 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
619 gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
620 gtk_box_pack_start(GTK_BOX(prog_box), vers_opt, TRUE, TRUE, 0);
621 gtk_widget_show(vers_opt);
623 gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
624 gtk_widget_show(prog_box);
627 filter_box=gtk_hbox_new(FALSE, 3);
630 filter_bt=BUTTON_NEW_FROM_STOCK(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
631 SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
632 gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
633 gtk_widget_show(filter_bt);
636 filter_entry=gtk_entry_new();
637 SIGNAL_CONNECT(filter_entry, "changed", filter_te_syntax_check_cb, NULL);
638 gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
639 filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
641 gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
643 gtk_widget_show(filter_entry);
645 gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
646 gtk_widget_show(filter_box);
648 g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_entry);
651 bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
652 gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
653 gtk_widget_show(bbox);
655 start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
656 g_signal_connect_swapped(start_button, "clicked",
657 G_CALLBACK(dcerpcstat_start_button_clicked), NULL);
659 cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
660 window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
662 SIGNAL_CONNECT(dlg, "delete_event", window_delete_event_cb, NULL);
663 SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
665 /* Catch the "activate" signal on the filter text entry, so that
666 if the user types Return there, we act as if the "Create Stat"
667 button had been selected, as happens if Return is typed if some
668 widget that *doesn't* handle the Return key has the input
670 dlg_set_activate(filter_entry, start_button);
672 gtk_widget_grab_default(start_button );
674 /* Give the initial focus to the "Filter" entry box. */
675 gtk_widget_grab_focus(filter_entry);
677 gtk_widget_show_all(dlg);
682 register_tap_listener_gtkdcerpcstat(void)
684 register_stat_cmd_arg("dcerpc,srt,", gtk_dcerpcstat_init,NULL);
686 register_stat_menu_item("DCE-RPC...", REGISTER_STAT_GROUP_RESPONSE_TIME,
687 gtk_dcerpcstat_cb, NULL, NULL, NULL);