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