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