Updated the DCERPC service response time to also offer the menu to
[obnox/wireshark/wip.git] / gtk / dcerpc_stat.c
1 /* dcerpc_stat.c
2  * dcerpc_stat   2002 Ronnie Sahlberg
3  *
4  * $Id: dcerpc_stat.c,v 1.25 2003/10/10 11:11:37 sahlberg Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  * 
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.
14  * 
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.
19  * 
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.
23  */
24
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
28  *
29  * It serves as an example on how to use the tap api.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <string.h>
37 #include <gtk/gtk.h>
38 #include "epan/packet_info.h"
39 #include "epan/epan.h"
40 #include "menu.h"
41 #include "simple_dialog.h"
42 #include "dlg_utils.h"
43 #include "ui_util.h"
44 #include "tap.h"
45 #include "../register.h"
46 #include "packet-dcerpc.h"
47 #include "../globals.h"
48 #include "compat_macros.h"
49 #include "service_response_time_table.h"
50
51
52 extern GtkWidget   *main_display_filter_widget;
53
54 /* used to keep track of the statistics for an entire program interface */
55 typedef struct _rpcstat_t {
56         GtkWidget *win;
57         srt_stat_table srt_table;
58         char *prog;
59         e_uuid_t uuid;
60         guint16 ver;
61         int num_procedures;
62 } rpcstat_t;
63
64
65 static int
66 uuid_equal(e_uuid_t *uuid1, e_uuid_t *uuid2)
67 {
68         if( (uuid1->Data1!=uuid2->Data1)
69           ||(uuid1->Data2!=uuid2->Data2)
70           ||(uuid1->Data3!=uuid2->Data3)
71           ||(uuid1->Data4[0]!=uuid2->Data4[0])
72           ||(uuid1->Data4[1]!=uuid2->Data4[1])
73           ||(uuid1->Data4[2]!=uuid2->Data4[2])
74           ||(uuid1->Data4[3]!=uuid2->Data4[3])
75           ||(uuid1->Data4[4]!=uuid2->Data4[4])
76           ||(uuid1->Data4[5]!=uuid2->Data4[5])
77           ||(uuid1->Data4[6]!=uuid2->Data4[6])
78           ||(uuid1->Data4[7]!=uuid2->Data4[7]) ){
79                 return 0;
80         }
81         return 1;
82 }
83
84 static void
85 dcerpcstat_set_title(rpcstat_t *rs)
86 {
87         char *title;
88
89         title = g_strdup_printf("DCE-RPC Service Response Time statistics for %s version %d.%d: %s",
90             rs->prog, rs->ver&0xff, rs->ver>>8, cf_get_display_name(&cfile));
91         gtk_window_set_title(GTK_WINDOW(rs->win), title);
92         g_free(title);
93 }
94
95 static void
96 dcerpcstat_reset(rpcstat_t *rs)
97 {
98         reset_srt_table_data(&rs->srt_table);
99         dcerpcstat_set_title(rs);
100 }
101
102
103 static int
104 dcerpcstat_packet(rpcstat_t *rs, packet_info *pinfo, epan_dissect_t *edt _U_, dcerpc_info *ri)
105 {
106         if(!ri->call_data){
107                 return 0;
108         }
109         if(!ri->call_data->req_frame){
110                 /* we have not seen the request so we dont know the delta*/
111                 return 0;
112         }
113         if(ri->call_data->opnum>=rs->num_procedures){
114                 /* dont handle this since its outside of known table */
115                 return 0;
116         }
117
118         /* we are only interested in reply packets */
119         if(ri->request){
120                 return 0;
121         }
122
123         /* we are only interested in certain program/versions */
124         if( (!uuid_equal( (&ri->call_data->uuid), (&rs->uuid)))
125           ||(ri->call_data->ver!=rs->ver)){
126                 return 0;
127         }
128
129
130         add_srt_table_data(&rs->srt_table, ri->call_data->opnum, &ri->call_data->req_time, pinfo);
131
132
133         return 1;
134 }
135
136 static void
137 dcerpcstat_draw(rpcstat_t *rs)
138 {
139         draw_srt_table_data(&rs->srt_table);
140 }
141
142
143 /* since the gtk2 implementation of tap is multithreaded we must protect
144  * remove_tap_listener() from modifying the list while draw_tap_listener()
145  * is running.  the other protected block is in main.c
146  *
147  * there should not be any other critical regions in gtk2
148  */
149 void protect_thread_critical_region(void);
150 void unprotect_thread_critical_region(void);
151 static void
152 win_destroy_cb(GtkWindow *win _U_, gpointer data)
153 {
154         rpcstat_t *rs=(rpcstat_t *)data;
155
156         protect_thread_critical_region();
157         remove_tap_listener(rs);
158         unprotect_thread_critical_region();
159
160         free_srt_table_data(&rs->srt_table);
161         g_free(rs);
162 }
163
164
165
166 /* When called, this function will create a new instance of gtk-dcerpcstat.
167  */
168 static void
169 gtk_dcerpcstat_init(char *optarg)
170 {
171         rpcstat_t *rs;
172         guint32 i, max_procs;
173         char title_string[256];
174         char filter_string[256];
175         GtkWidget *vbox;
176         GtkWidget *stat_label;
177         GtkWidget *filter_label;
178         dcerpc_sub_dissector *procs;
179         e_uuid_t uuid;
180         int d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47;
181         int major, minor;
182         int pos=0;
183         char *filter=NULL;
184         GString *error_string;
185         int hf_opnum;
186
187         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){
188                 uuid.Data1=d1;
189                 uuid.Data2=d2;
190                 uuid.Data3=d3;
191                 uuid.Data4[0]=d40;
192                 uuid.Data4[1]=d41;
193                 uuid.Data4[2]=d42;
194                 uuid.Data4[3]=d43;
195                 uuid.Data4[4]=d44;
196                 uuid.Data4[5]=d45;
197                 uuid.Data4[6]=d46;
198                 uuid.Data4[7]=d47;
199                 if(pos){
200                         filter=optarg+pos;
201                 } else {
202                         filter=NULL;
203                 }
204         } else {
205                 fprintf(stderr, "ethereal: invalid \"-z dcerpc,srt,<uuid>,<major version>.<minor version>[,<filter>]\" argument\n");
206                 exit(1);
207         }
208
209
210         rs=g_malloc(sizeof(rpcstat_t));
211         rs->prog=dcerpc_get_proto_name(&uuid, (minor<<8)|(major&0xff) );
212         hf_opnum=dcerpc_get_proto_hf_opnum(&uuid, (minor<<8)|(major&0xff) );
213         if(!rs->prog){
214                 g_free(rs);
215                 fprintf(stderr,"ethereal: dcerpcstat_init() Protocol with uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x v%d.%d 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],major,minor);
216                 exit(1);
217         }
218         procs=dcerpc_get_proto_sub_dissector(&uuid, (minor<<8)|(major&0xff) );
219         rs->uuid=uuid;
220         rs->ver=(minor<<8)|(major&0xff);
221
222         rs->win=gtk_window_new(GTK_WINDOW_TOPLEVEL);
223         gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
224         dcerpcstat_set_title(rs);
225         SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
226
227         vbox=gtk_vbox_new(FALSE, 0);
228         gtk_container_add(GTK_CONTAINER(rs->win), vbox);
229         gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
230         gtk_widget_show(vbox);
231
232         stat_label=gtk_label_new(title_string);
233         gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
234         gtk_widget_show(stat_label);
235
236         snprintf(filter_string,255,"Filter:%s",filter?filter:"");
237         filter_label=gtk_label_new(filter_string);
238         gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
239         gtk_widget_show(filter_label);
240
241         for(i=0,max_procs=0;procs[i].name;i++){
242                 if(procs[i].num>max_procs){
243                         max_procs=procs[i].num;
244                 }
245         }
246         rs->num_procedures=max_procs+1;
247
248         /* We must display TOP LEVEL Widget before calling init_srt_table() */
249         gtk_widget_show(rs->win);
250
251         if(hf_opnum!=-1){
252                 init_srt_table(&rs->srt_table, max_procs+1, vbox, proto_registrar_get_nth(hf_opnum)->abbrev);
253         } else {
254                 init_srt_table(&rs->srt_table, max_procs+1, vbox, NULL);
255         }
256
257         for(i=0;i<(max_procs+1);i++){
258                 int j;
259                 char *proc_name;
260
261                 proc_name="unknown";
262                 for(j=0;procs[j].name;j++){
263                         if(procs[j].num==i){
264                                 proc_name=procs[j].name;
265                         }
266                 }
267
268                 init_srt_table_row(&rs->srt_table, i, proc_name);
269         }
270
271
272         error_string=register_tap_listener("dcerpc", rs, filter, (void*)dcerpcstat_reset, (void*)dcerpcstat_packet, (void*)dcerpcstat_draw);
273         if(error_string){
274                 /* error, we failed to attach to the tap. clean up */
275                 simple_dialog(ESD_TYPE_WARN, NULL, error_string->str);
276                 g_string_free(error_string, TRUE);
277                 free_srt_table_data(&rs->srt_table);
278                 g_free(rs);
279                 return;
280         }
281
282
283         gtk_widget_show_all(rs->win);
284         redissect_packets(&cfile);
285 }
286
287
288
289 static e_uuid_t *dcerpc_uuid_program=NULL;
290 static guint16 dcerpc_version;
291 static GtkWidget *dlg=NULL;
292 static GtkWidget *prog_menu;
293 static GtkWidget *vers_opt, *vers_menu;
294 static GtkWidget *filter_entry;
295 static dcerpc_uuid_key *current_uuid_key=NULL;
296 static dcerpc_uuid_value *current_uuid_value=NULL;
297 static dcerpc_uuid_key *new_uuid_key=NULL;
298 static dcerpc_uuid_value *new_uuid_value=NULL;
299
300
301 static void
302 dcerpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
303 {
304         GString *str;
305         char *filter;
306
307         str = g_string_new("dcerpc,srt");
308         g_string_sprintfa(str,
309             ",%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d",
310             dcerpc_uuid_program->Data1, dcerpc_uuid_program->Data2,
311             dcerpc_uuid_program->Data3,
312             dcerpc_uuid_program->Data4[0], dcerpc_uuid_program->Data4[1],
313             dcerpc_uuid_program->Data4[2], dcerpc_uuid_program->Data4[3],
314             dcerpc_uuid_program->Data4[4], dcerpc_uuid_program->Data4[5],
315             dcerpc_uuid_program->Data4[6], dcerpc_uuid_program->Data4[7],
316             dcerpc_version&0xff, dcerpc_version>>8);
317         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
318         if(filter[0]!=0){
319                 g_string_sprintfa(str, ",%s", filter);
320         }
321
322         gtk_dcerpcstat_init(str->str);
323         g_string_free(str, TRUE);
324 }
325
326
327 static void
328 dcerpcstat_version_select(GtkWidget *item _U_, gpointer key)
329 {
330         int vers=(int)key;
331
332         dcerpc_version=vers;
333 }
334
335
336
337
338 static void *
339 dcerpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
340 {
341         dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
342         GtkWidget *menu_item;
343         char vs[5];
344
345         if(!uuid_equal((&k->uuid), dcerpc_uuid_program)){
346                 return NULL;
347         }
348
349         sprintf(vs,"%d.%d",k->ver&0xff,k->ver>>8);
350         menu_item=gtk_menu_item_new_with_label(vs);
351         SIGNAL_CONNECT(menu_item, "activate", dcerpcstat_version_select,
352                        ((int)k->ver));
353         gtk_widget_show(menu_item);
354         gtk_menu_append(GTK_MENU(vers_menu), menu_item);
355
356         if(dcerpc_version==0xffff){
357                 dcerpc_version=k->ver;
358         }
359
360         return NULL;
361 }
362
363
364 static void
365 dcerpcstat_program_select(GtkWidget *item _U_, gpointer key)
366 {
367         dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
368
369         dcerpc_uuid_program=&k->uuid;
370
371         /* change version menu */
372         dcerpc_version=0xffff;
373         gtk_object_destroy(GTK_OBJECT(vers_menu));
374         vers_menu=gtk_menu_new();
375         g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
376         gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
377 }
378
379
380 static void 
381 dcerpcstat_add_program_to_menu(dcerpc_uuid_key *k, dcerpc_uuid_value *v)
382 {
383         GtkWidget *program_menu_item;
384
385         program_menu_item=gtk_menu_item_new_with_label(v->name);
386         SIGNAL_CONNECT(program_menu_item, "activate", dcerpcstat_program_select, k);
387
388         gtk_widget_show(program_menu_item);
389         gtk_menu_append(GTK_MENU(prog_menu), program_menu_item);
390
391         if(!dcerpc_uuid_program){
392                 dcerpc_uuid_program=&k->uuid;
393         }
394
395         return;
396 }
397
398 static void *
399 dcerpcstat_find_next_program(gpointer *key, gpointer *value, gpointer *user_data _U_)
400 {
401         dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
402         dcerpc_uuid_value *v=(dcerpc_uuid_value *)value;
403
404         /* first time called, just set new_uuid to this one */
405         if((current_uuid_key==NULL)&&(new_uuid_key==NULL)){
406                 new_uuid_key=k;
407                 new_uuid_value=v;
408                 return NULL;
409         }
410
411         /* if we havent got a current one yet, just check the new
412            and scan for the first one alphabetically  */
413         if(current_uuid_key==NULL){
414                 if(strcmp(new_uuid_value->name, v->name)>0){
415                         new_uuid_key=k;
416                         new_uuid_value=v;
417                         return NULL;
418                 }
419                 return NULL;
420         }
421
422         /* searching for the next one we are only interested in those
423            that sorts alphabetically after the current one */
424         if(strcmp(current_uuid_value->name, v->name)>=0){
425                 /* this one doesnt so just skip it */
426                 return NULL;
427         }
428
429         /* is it the first potential new entry? */
430         if(new_uuid_key==NULL){
431                 new_uuid_key=k;
432                 new_uuid_value=v;
433                 return NULL;
434         }
435
436         /* does it sort before the current new one? */
437         if(strcmp(new_uuid_value->name, v->name)>0){
438                 new_uuid_key=k;
439                 new_uuid_value=v;
440                 return NULL;
441         }
442
443         return NULL;
444 }
445
446
447 static void
448 dlg_destroy_cb(void)
449 {
450         dlg=NULL;
451 }
452
453
454 static void
455 dlg_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
456 {
457         gtk_widget_destroy(GTK_WIDGET(parent_w));
458 }
459
460
461 static void
462 gtk_dcerpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
463 {
464         GtkWidget *dlg_box;
465         GtkWidget *prog_box, *prog_label, *prog_opt;
466         GtkWidget *vers_label;
467         GtkWidget *filter_box, *filter_label;
468         GtkWidget *bbox, *start_button, *cancel_button;
469         char *filter;
470
471         /* if the window is already open, bring it to front and
472            un-minimize it, as necessary */
473         if(dlg){
474                 reactivate_window(dlg);
475                 return;
476         }
477
478         dlg=dlg_window_new("Ethereal: Compute DCE-RPC SRT statistics");
479         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
480
481         dlg_box=gtk_vbox_new(FALSE, 10);
482         gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
483         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
484         gtk_widget_show(dlg_box);
485
486         /* Program box */
487         prog_box=gtk_hbox_new(FALSE, 3);
488
489         /* Program label */
490         gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
491         prog_label=gtk_label_new("Program:");
492         gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
493         gtk_widget_show(prog_label);
494
495         /* Program menu */
496         prog_opt=gtk_option_menu_new();
497         prog_menu=gtk_menu_new();
498         current_uuid_key=NULL;
499         current_uuid_value=NULL;
500         do {
501                 new_uuid_key=NULL;
502                 new_uuid_value=NULL;
503                 g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_next_program, NULL);
504                 if(new_uuid_key){
505                         dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value);
506                 }
507                 current_uuid_key=new_uuid_key;
508                 current_uuid_value=new_uuid_value;
509         } while(new_uuid_key!=NULL);
510
511         gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
512         gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
513         gtk_widget_show(prog_opt);
514
515         /* Version label */
516         gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
517         vers_label=gtk_label_new("Version:");
518         gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
519         gtk_widget_show(vers_label);
520
521         /* Version menu */
522         vers_opt=gtk_option_menu_new();
523         vers_menu=gtk_menu_new();
524         dcerpc_version=0xffff;
525         g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
526         gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
527         gtk_box_pack_start(GTK_BOX(prog_box), vers_opt, TRUE, TRUE, 0);
528         gtk_widget_show(vers_opt);
529
530         gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
531         gtk_widget_show(prog_box);
532
533         /* Filter box */
534         filter_box=gtk_hbox_new(FALSE, 3);
535
536         /* Filter label */
537         filter_label=gtk_label_new("Filter:");
538         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
539         gtk_widget_show(filter_label);
540
541         /* Filter entry */
542         filter_entry=gtk_entry_new();
543         gtk_widget_set_usize(filter_entry, 300, -2);
544         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
545         filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
546         if(filter){
547                 gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
548         }
549         gtk_widget_show(filter_entry);
550         
551         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
552         gtk_widget_show(filter_box);
553
554         /* button box */
555         bbox=gtk_hbutton_box_new();
556         gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_DEFAULT_STYLE);
557         gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
558         gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
559         gtk_widget_show(bbox);
560
561         /* the start button */
562         start_button=gtk_button_new_with_label("Create Stat");
563         SIGNAL_CONNECT_OBJECT(start_button, "clicked", 
564                               dcerpcstat_start_button_clicked, NULL);
565         gtk_box_pack_start(GTK_BOX(bbox), start_button, TRUE, TRUE, 0);
566         GTK_WIDGET_SET_FLAGS(start_button, GTK_CAN_DEFAULT);
567         gtk_widget_grab_default(start_button);
568         gtk_widget_show(start_button);
569
570 #if GTK_MAJOR_VERSION < 2
571         cancel_button=gtk_button_new_with_label("Cancel");
572 #else
573         cancel_button=gtk_button_new_from_stock(GTK_STOCK_CANCEL);
574 #endif
575         SIGNAL_CONNECT(cancel_button, "clicked", dlg_cancel_cb, dlg);
576         GTK_WIDGET_SET_FLAGS(cancel_button, GTK_CAN_DEFAULT);
577         gtk_box_pack_start(GTK_BOX(bbox), cancel_button, TRUE, TRUE, 0);
578         gtk_widget_show(cancel_button);
579
580         /* Catch the "activate" signal on the filter text entry, so that
581            if the user types Return there, we act as if the "Create Stat"
582            button had been selected, as happens if Return is typed if some
583            widget that *doesn't* handle the Return key has the input
584            focus. */
585         dlg_set_activate(filter_entry, start_button);
586
587         /* Catch the "key_press_event" signal in the window, so that we can
588            catch the ESC key being pressed and act as if the "Cancel" button
589            had been selected. */
590         dlg_set_cancel(dlg, cancel_button);
591
592         /* Give the initial focus to the "Filter" entry box. */
593         gtk_widget_grab_focus(filter_entry);
594
595         gtk_widget_show_all(dlg);
596 }
597
598 void
599 register_tap_listener_gtkdcerpcstat(void)
600 {
601         register_ethereal_tap("dcerpc,srt,", gtk_dcerpcstat_init);
602 }
603
604 void
605 register_tap_menu_gtkdcerpcstat(void)
606 {
607         register_tap_menu_item("Statistics/Service Response Time/DCE-RPC...",
608             gtk_dcerpcstat_cb, NULL, NULL);
609 }