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"
61 /* used to keep track of the statistics for an entire program interface */
62 typedef struct _rpcstat_t {
64 srt_stat_table srt_table;
73 uuid_equal(e_uuid_t *uuid1, e_uuid_t *uuid2)
75 if( (uuid1->Data1!=uuid2->Data1)
76 ||(uuid1->Data2!=uuid2->Data2)
77 ||(uuid1->Data3!=uuid2->Data3)
78 ||(uuid1->Data4[0]!=uuid2->Data4[0])
79 ||(uuid1->Data4[1]!=uuid2->Data4[1])
80 ||(uuid1->Data4[2]!=uuid2->Data4[2])
81 ||(uuid1->Data4[3]!=uuid2->Data4[3])
82 ||(uuid1->Data4[4]!=uuid2->Data4[4])
83 ||(uuid1->Data4[5]!=uuid2->Data4[5])
84 ||(uuid1->Data4[6]!=uuid2->Data4[6])
85 ||(uuid1->Data4[7]!=uuid2->Data4[7]) ){
92 dcerpcstat_gen_title(rpcstat_t *rs)
96 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));
101 dcerpcstat_set_title(rpcstat_t *rs)
105 title = dcerpcstat_gen_title(rs);
106 gtk_window_set_title(GTK_WINDOW(rs->win), title);
111 dcerpcstat_reset(void *rs_arg)
113 rpcstat_t *rs = rs_arg;
115 reset_srt_table_data(&rs->srt_table);
116 dcerpcstat_set_title(rs);
121 dcerpcstat_packet(void *rs_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ri_arg)
123 rpcstat_t *rs = rs_arg;
124 const dcerpc_info *ri = ri_arg;
129 if(!ri->call_data->req_frame){
130 /* we have not seen the request so we dont know the delta*/
133 if(ri->call_data->opnum>=rs->num_procedures){
134 /* dont handle this since its outside of known table */
138 /* we are only interested in reply packets */
139 if(ri->ptype != PDU_RESP){
143 /* we are only interested in certain program/versions */
144 if( (!uuid_equal( (&ri->call_data->uuid), (&rs->uuid)))
145 ||(ri->call_data->ver!=rs->ver)){
150 add_srt_table_data(&rs->srt_table, ri->call_data->opnum, &ri->call_data->req_time, pinfo);
157 dcerpcstat_draw(void *rs_arg)
159 rpcstat_t *rs = rs_arg;
161 draw_srt_table_data(&rs->srt_table);
165 /* since the gtk2 implementation of tap is multithreaded we must protect
166 * remove_tap_listener() from modifying the list while draw_tap_listener()
167 * is running. the other protected block is in main.c
169 * there should not be any other critical regions in gtk2
172 win_destroy_cb(GtkWindow *win _U_, gpointer data)
174 rpcstat_t *rs=(rpcstat_t *)data;
176 protect_thread_critical_region();
177 remove_tap_listener(rs);
178 unprotect_thread_critical_region();
180 free_srt_table_data(&rs->srt_table);
186 /* When called, this function will create a new instance of gtk-dcerpcstat.
189 gtk_dcerpcstat_init(const char *optarg, void* userdata _U_)
192 guint32 i, max_procs;
196 GtkWidget *stat_label;
197 GtkWidget *filter_label;
200 dcerpc_sub_dissector *procs;
202 guint d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47;
206 const char *filter=NULL;
207 GString *error_string;
211 * XXX - DCE RPC statistics are maintained only by major version,
212 * not by major and minor version, so the minor version number is
215 * Should we just stop supporting minor version numbers here?
216 * Or should we allow it to be omitted? Or should we keep
217 * separate statistics for different minor version numbers,
218 * and allow the minor version number to be omitted, and
219 * report aggregate statistics for all minor version numbers
222 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){
240 fprintf(stderr, "wireshark: invalid \"-z dcerpc,srt,<uuid>,<major version>.<minor version>[,<filter>]\" argument\n");
243 if (major < 0 || major > 65535) {
244 fprintf(stderr,"wireshark: dcerpcstat_init() Major version number %d is invalid - must be positive and <= 65535\n", major);
247 if (minor < 0 || minor > 65535) {
248 fprintf(stderr,"wireshark: dcerpcstat_init() Minor version number %d is invalid - must be positive and <= 65535\n", minor);
253 rs=g_malloc(sizeof(rpcstat_t));
254 rs->prog=dcerpc_get_proto_name(&uuid, ver);
257 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);
260 hf_opnum=dcerpc_get_proto_hf_opnum(&uuid, ver);
261 procs=dcerpc_get_proto_sub_dissector(&uuid, ver);
265 rs->win=window_new(GTK_WINDOW_TOPLEVEL, "dcerpc-stat");
266 dcerpcstat_set_title(rs);
267 gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
269 vbox=gtk_vbox_new(FALSE, 3);
270 gtk_container_add(GTK_CONTAINER(rs->win), vbox);
271 gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
273 title_string=dcerpcstat_gen_title(rs);
274 stat_label=gtk_label_new(title_string);
275 g_free(title_string);
276 gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
278 filter_string = g_strdup_printf("Filter: %s",filter ? filter : "");
279 filter_label=gtk_label_new(filter_string);
280 g_free(filter_string);
281 gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
282 gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
284 for(i=0,max_procs=0;procs[i].name;i++){
285 if(procs[i].num>max_procs){
286 max_procs=procs[i].num;
289 rs->num_procedures=max_procs+1;
291 /* We must display TOP LEVEL Widget before calling init_srt_table() */
292 gtk_widget_show_all(rs->win);
295 init_srt_table(&rs->srt_table, max_procs+1, vbox, proto_registrar_get_nth(hf_opnum)->abbrev);
297 init_srt_table(&rs->srt_table, max_procs+1, vbox, NULL);
300 for(i=0;i<(max_procs+1);i++){
302 const char *proc_name;
305 for(j=0;procs[j].name;j++){
307 proc_name=procs[j].name;
311 init_srt_table_row(&rs->srt_table, i, proc_name);
315 error_string=register_tap_listener("dcerpc", rs, filter, dcerpcstat_reset, dcerpcstat_packet, dcerpcstat_draw);
317 /* error, we failed to attach to the tap. clean up */
318 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
319 g_string_free(error_string, TRUE);
320 free_srt_table_data(&rs->srt_table);
326 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
327 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
329 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
330 window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
332 g_signal_connect(rs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
333 g_signal_connect(rs->win, "destroy", G_CALLBACK(win_destroy_cb), rs);
335 gtk_widget_show_all(rs->win);
336 window_present(rs->win);
338 cf_retap_packets(&cfile, FALSE);
339 gdk_window_raise(rs->win->window);
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_append_printf(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_append_printf(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 g_signal_connect(menu_item, "activate", G_CALLBACK(dcerpcstat_version_select),
407 (gpointer)((long)k->ver));
408 gtk_widget_show(menu_item);
409 gtk_menu_shell_append(GTK_MENU_SHELL(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_shell_append(GTK_MENU_SHELL(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 g_signal_connect(program_menu_item, "activate", G_CALLBACK(dcerpcstat_program_select), k);
478 gtk_widget_show(program_menu_item);
479 gtk_menu_shell_append(GTK_MENU_SHELL(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_set_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=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
631 g_signal_connect(filter_bt, "clicked", G_CALLBACK(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 g_signal_connect(filter_entry, "changed", G_CALLBACK(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 colorize_filter_te_as_empty(filter_entry);
645 gtk_widget_show(filter_entry);
647 gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
648 gtk_widget_show(filter_box);
650 g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_entry);
653 bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
654 gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
655 gtk_widget_show(bbox);
657 start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
658 g_signal_connect_swapped(start_button, "clicked",
659 G_CALLBACK(dcerpcstat_start_button_clicked), NULL);
661 cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
662 window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
664 g_signal_connect(dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
665 g_signal_connect(dlg, "destroy", dlg_destroy_cb, NULL);
667 /* Catch the "activate" signal on the filter text entry, so that
668 if the user types Return there, we act as if the "Create Stat"
669 button had been selected, as happens if Return is typed if some
670 widget that *doesn't* handle the Return key has the input
672 dlg_set_activate(filter_entry, start_button);
674 gtk_widget_grab_default(start_button );
676 /* Give the initial focus to the "Filter" entry box. */
677 gtk_widget_grab_focus(filter_entry);
679 gtk_widget_show_all(dlg);
684 register_tap_listener_gtkdcerpcstat(void)
686 register_stat_cmd_arg("dcerpc,srt,", gtk_dcerpcstat_init,NULL);
688 register_stat_menu_item("DCE-RPC...", REGISTER_STAT_GROUP_RESPONSE_TIME,
689 gtk_dcerpcstat_cb, NULL, NULL, NULL);