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