From Lars Roland: use the generic filter dialog for the SIP, HTTP, WSP,
[obnox/wireshark/wip.git] / gtk / sip_stat.c
1 /* sip_stat.c
2  * sip_stat   2004 Martin Mathieson
3  *
4  * $Id: sip_stat.c,v 1.2 2004/03/27 11:13:03 guy Exp $
5  * Copied from http_stat.c
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <string.h>
31
32 #include <gtk/gtk.h>
33
34 #include <epan/packet_info.h>
35 #include <epan/epan.h>
36
37 #include "tap_menu.h"
38 #include "simple_dialog.h"
39 #include "ui_util.h"
40 #include "dlg_utils.h"
41 #include "tap.h"
42 #include "../register.h"
43 #include "../packet-sip.h"
44 #include "../globals.h"
45 #include "compat_macros.h"
46 #include "../tap_dfilter_dlg.h"
47 #include "tap_dfilter_dlg.h"
48
49 #define SUM_STR_MAX     1024
50
51 /* Used to keep track of the statistics for an entire program interface */
52 typedef struct _sip_stats_t {
53     char        *filter;
54     GtkWidget   *win;
55     GHashTable  *hash_responses;
56     GHashTable  *hash_requests;
57     guint32         packets;        /* number of sip packets, including continuations */
58     GtkWidget   *packets_label;
59
60     GtkWidget   *request_box;           /* container for INVITE, ... */
61
62     GtkWidget   *informational_table;   /* Status code between 100 and 199 */
63     GtkWidget   *success_table;         /*   200 and 299 */
64     GtkWidget   *redirection_table;         /*   300 and 399 */
65     GtkWidget   *client_error_table;    /*   400 and 499 */
66     GtkWidget   *server_errors_table;   /*   500 and 599 */
67     GtkWidget   *global_failures_table; /*   600 and 699 */
68 } sipstat_t;
69
70 /* Used to keep track of the stats for a specific response code
71  * for example it can be { 3, 404, "Not Found" ,...}
72  * which means we captured 3 reply sip/1.1 404 Not Found */
73 typedef struct _sip_response_code_t {
74     guint32      packets;                   /* 3 */
75     guint                response_code;         /* 404 */
76     gchar               *name;                      /* "Not Found" */
77     GtkWidget   *widget;                    /* Label where we display it */
78     GtkWidget   *table;                     /* Table in which we put it,
79                                        e.g. client_error_table */
80     sipstat_t   *sp;                /* Pointer back to main struct */
81 } sip_response_code_t;
82
83 /* Used to keep track of the stats for a specific request string */
84 typedef struct _sip_request_method_t {
85     gchar               *response;              /* eg. : INVITE */
86     guint32              packets;
87     GtkWidget   *widget;
88     sipstat_t   *sp;                /* Pointer back to main struct */
89 } sip_request_method_t;
90
91 /* TODO: extra codes to be added from SIP extensions? */
92 static const value_string vals_status_code[] = {
93     { 100, "Trying"},
94     { 180, "Ringing"},
95     { 181, "Call Is Being Forwarded"},
96     { 182, "Queued"},
97     { 183, "Session Progress"},
98     { 199, "Informational - Others" },
99
100     { 200, "OK"},
101     { 202, "Accepted"},
102     { 299, "Success - Others"}, /* used to keep track of other Success packets */
103
104     { 300, "Multiple Choices"},
105     { 301, "Moved Permanently"},
106     { 302, "Moved Temporarily"},
107     { 305, "Use Proxy"},
108     { 380, "Alternative Service"},
109     { 399, "Redirection - Others"},
110
111     { 400, "Bad Request"},
112     { 401, "Unauthorized"},
113     { 402, "Payment Required"},
114     { 403, "Forbidden"},
115     { 404, "Not Found"},
116     { 405, "Method Not Allowed"},
117     { 406, "Not Acceptable"},
118     { 407, "Proxy Authentication Required"},
119     { 408, "Request Timeout"},
120     { 410, "Gone"},
121     { 413, "Request Entity Too Large"},
122     { 414, "Request-URI Too Long"},
123     { 415, "Unsupported Media Type"},
124     { 416, "Unsupported URI Scheme"},
125     { 420, "Bad Extension"},
126     { 421, "Extension Required"},
127     { 423, "Interval Too Brief"},
128     { 480, "Temporarily Unavailable"},
129     { 481, "Call/Transaction Does Not Exist"},
130     { 482, "Loop Detected"},
131     { 483, "Too Many Hops"},
132     { 484, "Address Incomplete"},
133     { 485, "Ambiguous"},
134     { 486, "Busy Here"},
135     { 487, "Request Terminated"},
136     { 488, "Not Acceptable Here"},
137     { 489, "Bad Event"},
138     { 491, "Request Pending"},
139     { 493, "Undecipherable"},
140     { 499, "Client Error - Others"},
141
142     { 500, "Server Internal Error"},
143     { 501, "Not Implemented"},
144     { 502, "Bad Gateway"},
145     { 503, "Service Unavailable"},
146     { 504, "Server Time-out"},
147     { 505, "Version Not Supported"},
148     { 513, "Message Too Large"},
149     { 599, "Server Error - Others"},
150
151     { 600, "Busy Everywhere"},
152     { 603, "Decline"},
153     { 604, "Does Not Exist Anywhere"},
154     { 606, "Not Acceptable"},
155     { 699, "Global Failure - Others"},
156
157     { 0,        NULL}
158 };
159
160 /* Create tables for responses and requests */
161 static void
162 sip_init_hash(sipstat_t *sp)
163 {
164     int i;
165
166     /* Create responses table */
167     sp->hash_responses = g_hash_table_new(g_int_hash, g_int_equal);
168
169     /* Add all response codes */
170     for (i=0 ; vals_status_code[i].strptr ; i++)
171     {
172         gint *key = g_malloc (sizeof(gint));
173         sip_response_code_t *sc = g_malloc (sizeof(sip_response_code_t));
174         *key = vals_status_code[i].value;
175         sc->packets=0;
176         sc->response_code =  *key;
177         sc->name=vals_status_code[i].strptr;
178         sc->widget=NULL;
179         sc->table=NULL;
180         sc->sp = sp;
181         g_hash_table_insert(sc->sp->hash_responses, key, sc);
182     }
183     
184     /* Create empty requests table */
185     sp->hash_requests = g_hash_table_new(g_str_hash, g_str_equal);
186 }
187
188 /* Draw the entry for an individual request message */
189 static void
190 sip_draw_hash_requests(gchar *key _U_ , sip_request_method_t *data, gchar * unused _U_)
191 {
192     gchar string_buff[SUM_STR_MAX];
193
194     if (data->packets==0)
195     {
196         return;
197     }
198     
199     /* Build string showing method and count */
200     g_snprintf(string_buff, sizeof(string_buff),
201                "     %-11s : %3d packets", data->response, data->packets);
202     if (data->widget==NULL)
203     {
204         /* Create new label */
205         data->widget=gtk_label_new(string_buff);
206         gtk_misc_set_alignment(GTK_MISC(data->widget), 0.0, 0.5);
207         gtk_box_pack_start(GTK_BOX(data->sp->request_box), data->widget,FALSE,FALSE, 0);
208         gtk_widget_show(data->widget);
209     }
210     else
211     {
212         /* Update existing label */
213         gtk_label_set(GTK_LABEL(data->widget), string_buff);
214     }
215 }
216
217 /* Draw an individual response entry */
218 static void
219 sip_draw_hash_responses(gint * key _U_ , sip_response_code_t *data, gchar * unused _U_)
220 {
221     gchar string_buff[SUM_STR_MAX];
222
223     if (data==NULL)
224     {
225         g_warning("C'est quoi ce borderl key=%d\n", *key);
226     }
227     if (data->packets==0)
228     {
229         return;
230     }
231
232     /* Create an entry in the relevant box of the window */
233     if (data->widget==NULL)
234     {
235         guint16 x;
236         GtkWidget *tmp;
237         guint i = data->response_code;
238
239         /* Out of valid range - ignore */
240         if ((i<100)||(i>=700))
241         {
242             return;
243         }
244
245         /* Find the table matching the code */
246         if (i<200)
247         {
248             data->table = data->sp->informational_table;
249         }
250         else if (i<300)
251         {
252             data->table = data->sp->success_table;
253         }
254         else if (i<400)
255         {
256             data->table = data->sp->redirection_table;
257         }
258         else if (i<500)
259         {
260             data->table = data->sp->client_error_table;
261         }
262         else if (i < 600)
263         {
264             data->table = data->sp->server_errors_table;
265         }
266         else
267         {
268             data->table = data->sp->global_failures_table;
269         }
270
271         /* Get number of rows in table */
272         x = GTK_TABLE(data->table)->nrows;
273
274         /* Create a new label with this response, e.g. "SIP 180 Ringing" */
275         g_snprintf(string_buff, sizeof(string_buff),
276                    "SIP %3d %s ", data->response_code, data->name);
277         tmp = gtk_label_new(string_buff);
278
279         /* Insert the label in the correct place in the table */
280         gtk_table_attach_defaults(GTK_TABLE(data->table), tmp,  0, 1, x, x+1);
281         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
282         gtk_widget_show(tmp);
283
284         /* Show number of packets */
285         g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
286         data->widget=gtk_label_new(string_buff);
287
288         /* Show this widget in the right place */
289         gtk_table_attach_defaults(GTK_TABLE(data->table), data->widget, 1, 2,x,x+1);
290         gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_RIGHT);
291         gtk_widget_show(data->widget);
292
293         gtk_table_resize(GTK_TABLE(data->table), x+1, 4);
294
295     } else
296     {
297         /* Just update the existing label string */
298         g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
299         gtk_label_set(GTK_LABEL(data->widget), string_buff);
300     }
301 }
302
303
304
305 static void
306 sip_free_hash(gpointer key, gpointer value, gpointer user_data _U_)
307 {
308     g_free(key);
309     g_free(value);
310 }
311
312 static void
313 sip_reset_hash_responses(gchar *key _U_ , sip_response_code_t *data, gpointer ptr _U_)
314 {
315     data->packets = 0;
316 }
317
318 static void
319 sip_reset_hash_requests(gchar *key _U_ , sip_request_method_t *data, gpointer ptr _U_)
320 {
321     data->packets = 0;
322 }
323
324 static void
325 sipstat_reset(void *psp)
326 {
327     sipstat_t *sp = psp;
328     if (!sp)
329     {
330         g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_reset_hash_responses, NULL);
331         g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_reset_hash_requests, NULL);
332     }
333 }
334
335 /* Main entry point to SIP tap */
336 static int
337 sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, void *pri)
338 {
339     sip_info_value_t *value=pri;
340     sipstat_t *sp = (sipstat_t *)psp;
341
342     /* Total number of packets, including continuation packets */
343     sp->packets++;
344
345     /* Looking at both requests and responses */
346     if (value->response_code != 0)
347     {
348         /* Responses */
349         guint *key = g_malloc(sizeof(guint));
350         sip_response_code_t *sc;
351
352         /* Look up response code in hash table */
353         *key = value->response_code;
354         sc = g_hash_table_lookup(sp->hash_responses, key);
355         if (sc==NULL)
356         {
357             /* Non-standard status code ; we classify it as others
358              * in the relevant category
359              * (Informational,Success,Redirection,Client Error,Server Error,Global Failure)
360              */
361             int i = value->response_code;
362             if ((i<100) || (i>=700))
363             {
364                 /* Forget about crazy values */
365                 return 0;
366             }
367             else if (i<200)
368             {
369                 *key=199;       /* Hopefully, this status code will never be used */
370             }
371             else if (i<300)
372             {
373                 *key=299;
374             }
375             else if (i<400)
376             {
377                 *key=399;
378             }
379             else if (i<500)
380             {
381                 *key=499;
382             }
383             else if (i<600)
384             {
385                 *key=599;
386             }
387             else
388             {
389                 *key = 699;
390             }
391
392             /* Now look up this fallback code to get its text description */
393             sc = g_hash_table_lookup(sp->hash_responses, key);
394             if (sc==NULL)
395             {
396                 return 0;
397             }
398         }
399         sc->packets++;
400     }
401     else if (value->request_method)
402     {
403         /* Requests */
404         sip_request_method_t *sc;
405
406         /* Look up the request method in the table */
407         sc = g_hash_table_lookup(sp->hash_requests, value->request_method);
408         if (sc == NULL)
409         {
410             /* First of this type. Create structure and initialise */
411             sc=g_malloc(sizeof(sip_request_method_t));
412             sc->response = g_strdup(value->request_method);
413             sc->packets = 1;
414             sc->widget = NULL;
415             sc->sp = sp;
416             /* Insert it into request table */
417             g_hash_table_insert(sp->hash_requests, sc->response, sc);
418         }
419         else
420         {
421             /* Already existed, just update count for that method */
422             sc->packets++;
423         }
424         /* g_free(value->request_method); */
425     }
426     else
427     {
428         /* No request method set. Just ignore */
429         return 0;
430     }
431
432     return 1;
433 }
434
435 /* Redraw the whole stats window */
436 static void
437 sipstat_draw(void *psp)
438 {
439     gchar      string_buff[SUM_STR_MAX];
440     sipstat_t *sp=psp;
441
442     /* Set summary label */
443     g_snprintf(string_buff, sizeof(string_buff),
444                 "SIP stats (%d packets)", sp->packets);
445     gtk_label_set(GTK_LABEL(sp->packets_label), string_buff);
446
447     /* Draw responses and requests from their tables */
448     g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_draw_hash_responses, NULL);
449     g_hash_table_foreach(sp->hash_requests,  (GHFunc)sip_draw_hash_requests, NULL);
450 }
451
452
453 /* since the gtk2 implementation of tap is multithreaded we must protect
454  * remove_tap_listener() from modifying the list while draw_tap_listener()
455  * is running.  the other protected block is in main.c
456  *
457  * there should not be any other critical regions in gtk2
458  */
459 void protect_thread_critical_region(void);
460 void unprotect_thread_critical_region(void);
461
462 /* When window is destroyed, clean up */
463 static void
464 win_destroy_cb(GtkWindow *win _U_, gpointer data)
465 {
466     sipstat_t *sp=(sipstat_t *)data;
467
468     protect_thread_critical_region();
469     remove_tap_listener(sp);
470     unprotect_thread_critical_region();
471
472     g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_free_hash, NULL);
473     g_hash_table_destroy(sp->hash_responses);
474     g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_free_hash, NULL);
475     g_hash_table_destroy(sp->hash_requests);
476     g_free(sp->filter);
477     g_free(sp);
478 }
479
480 /* Create a new instance of gtk_sipstat. */
481 static void
482 gtk_sipstat_init(char *optarg)
483 {
484     sipstat_t *sp;
485     char *filter = NULL;
486     GString     *error_string;
487     char *title = NULL;
488     GtkWidget  *main_vb, *separator,
489                *informational_fr, *success_fr, *redirection_fr,
490                *client_errors_fr, *server_errors_fr, *global_failures_fr,
491                *request_fr;
492
493
494     if (strncmp (optarg, "sip,stat,", 9) == 0)
495     {
496         /* Skip those characters from filter to display */
497         filter=optarg + 9;
498     }
499     else
500     {
501         /* No filter */
502         filter = NULL;
503     }
504
505     /* Create sip stats window structure */
506     sp = g_malloc(sizeof(sipstat_t));
507
508     /* Set title to include any filter given */
509     if (filter)
510     {
511         sp->filter = g_strdup(filter);
512         title = g_strdup_printf("SIP statistics with filter: %s", filter);
513     }
514     else
515     {
516         sp->filter = NULL;
517         title = g_strdup("SIP statistics");
518     }
519
520     /* Create underlying window */
521     sp->win = window_new(GTK_WINDOW_TOPLEVEL, title);
522     g_free(title);
523
524     /* Set destroy callback for underlying window */
525     SIGNAL_CONNECT(sp->win, "destroy", win_destroy_cb, sp);
526
527
528     /* Create container for all widgets */
529     main_vb = gtk_vbox_new(FALSE, 10);
530     gtk_container_border_width(GTK_CONTAINER(main_vb), 10);
531     gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
532     gtk_widget_show(main_vb);
533
534     /* Initialise & show number of packets */
535     sp->packets = 0;
536     sp->packets_label = gtk_label_new("SIP stats (0 SIP packets)");
537     gtk_container_add(GTK_CONTAINER(main_vb), sp->packets_label);
538     gtk_widget_show(sp->packets_label);
539
540
541     /* Informational response frame */
542     informational_fr = gtk_frame_new("Informational  SIP 1xx");
543     gtk_container_add(GTK_CONTAINER(main_vb), informational_fr);
544     gtk_widget_show(informational_fr);
545
546     /* Information table (within that frame) */
547     sp->informational_table = gtk_table_new(0, 2, FALSE);
548     gtk_container_add(GTK_CONTAINER(informational_fr), sp->informational_table);
549     gtk_widget_show(sp->informational_table);
550
551
552     /* Success table and frame */
553     success_fr = gtk_frame_new  ("Success         SIP 2xx");
554     gtk_container_add(GTK_CONTAINER(main_vb), success_fr);
555     gtk_widget_show(success_fr);
556
557     sp->success_table = gtk_table_new(0, 2, FALSE);
558     gtk_container_add(GTK_CONTAINER(success_fr), sp->success_table);
559     gtk_widget_show(sp->success_table);
560
561
562     /* Redirection table and frame */
563     redirection_fr = gtk_frame_new      ("Redirection     SIP 3xx");
564     gtk_container_add(GTK_CONTAINER(main_vb), redirection_fr);
565     gtk_widget_show(redirection_fr);
566
567     sp->redirection_table = gtk_table_new(0, 2, FALSE);
568     gtk_container_add(GTK_CONTAINER(redirection_fr), sp->redirection_table);
569     gtk_widget_show(sp->redirection_table);
570
571
572     /* Client Errors table and frame */
573     client_errors_fr = gtk_frame_new("Client errors  SIP 4xx");
574     gtk_container_add(GTK_CONTAINER(main_vb), client_errors_fr);
575     gtk_widget_show(client_errors_fr);
576
577     sp->client_error_table = gtk_table_new(0, 2, FALSE);
578     gtk_container_add(GTK_CONTAINER(client_errors_fr), sp->client_error_table);
579     gtk_widget_show(sp->client_error_table);
580
581
582     /* Server Errors table and frame */
583     server_errors_fr = gtk_frame_new("Server errors  SIP 5xx");
584     gtk_container_add(GTK_CONTAINER(main_vb), server_errors_fr);
585     gtk_widget_show(server_errors_fr);
586
587     sp->server_errors_table = gtk_table_new(0, 2, FALSE);
588     gtk_container_add(GTK_CONTAINER(server_errors_fr), sp->server_errors_table);
589     gtk_widget_show(sp->server_errors_table);
590
591
592     /* Global Failures table and frame */
593     global_failures_fr = gtk_frame_new("Global failures  SIP 6xx");
594     gtk_container_add(GTK_CONTAINER(main_vb), global_failures_fr);
595     gtk_widget_show(global_failures_fr);
596
597     sp->global_failures_table = gtk_table_new(0, 2, FALSE);
598     gtk_container_add(GTK_CONTAINER(global_failures_fr), sp->global_failures_table);
599     gtk_widget_show(sp->global_failures_table);
600
601
602     /* Separator between requests and responses */
603     separator = gtk_hseparator_new();
604     gtk_container_add(GTK_CONTAINER(main_vb), separator);
605
606
607     /* Request table and frame */
608     request_fr = gtk_frame_new("List of request methods");
609     gtk_container_add(GTK_CONTAINER(main_vb), request_fr);
610     gtk_container_border_width(GTK_CONTAINER(request_fr), 0);
611     gtk_widget_show(request_fr);
612
613     sp->request_box = gtk_vbox_new(FALSE, 10);
614     gtk_container_add(GTK_CONTAINER(request_fr), sp->request_box);
615     gtk_widget_show(sp->request_box);
616
617
618     /* Register this tap listener now */
619     error_string = register_tap_listener("sip",
620                                          sp,
621                                          filter,
622                                          sipstat_reset,
623                                          sipstat_packet,
624                                          sipstat_draw);
625     if (error_string)
626     {
627         /* Error.  We failed to attach to the tap. Clean up */
628         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
629         g_free(sp->filter);
630         g_free(sp);
631         g_string_free(error_string, TRUE);
632         return;
633     }
634
635     /* Display up-to-date contents */
636     gtk_widget_show_all(sp->win);
637     sip_init_hash(sp);
638     retap_packets(&cfile);
639 }
640
641 static tap_dfilter_dlg sip_stat_dlg = {
642         "SIP Packet Counter",
643         "sip,stat",
644         gtk_sipstat_init,
645         -1
646 };
647
648 /* Register this tap listener and add menu item. */
649 void
650 register_tap_listener_gtksipstat(void)
651 {
652     register_ethereal_tap("sip,stat", gtk_sipstat_init);
653
654     register_tap_menu_item("SIP", REGISTER_TAP_GROUP_NONE,
655                            gtk_tap_dfilter_dlg_cb, NULL, NULL, &(sip_stat_dlg));
656 }