Get rid of -Wshadow warning - I guess we're including something that
[metze/wireshark/wip.git] / ui / cli / tap-sipstat.c
1 /* tap_sipstat.c
2  * sip message counter for wireshark
3  *
4  * $Id$
5  * Copied from ui/gtk/sip_stat.c and tap-httpstat.c
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 #include "config.h"
27
28 #include <stdio.h>
29
30 #include <string.h>
31 #include "epan/packet_info.h"
32 #include <epan/tap.h>
33 #include <epan/stat_cmd_args.h>
34 #include "epan/value_string.h"
35 #include <epan/dissectors/packet-sip.h>
36
37 /* used to keep track of the statictics for an entire program interface */
38 typedef struct _sip_stats_t {
39         char            *filter;
40         guint32         packets;        /* number of sip packets, including continuations */
41         guint32         resent_packets;
42         guint32         average_setup_time;
43         guint32         max_setup_time;
44         guint32         min_setup_time;
45         guint32         no_of_completed_calls;
46         guint64         total_setup_time;
47         GHashTable      *hash_responses;
48         GHashTable      *hash_requests;
49 } sipstat_t;
50
51 /* used to keep track of the stats for a specific response code
52  * for example it can be { 3, 404, "Not Found" ,...}
53  * which means we captured 3 reply sip/1.1 404 Not Found */
54 typedef struct _sip_response_code_t {
55         guint32  packets;               /* 3 */
56         guint            response_code;         /* 404 */
57         const gchar     *name;                  /* Not Found */
58         sipstat_t       *sp;
59 } sip_response_code_t;
60
61 /* used to keep track of the stats for a specific request string */
62 typedef struct _sip_request_method_t {
63         gchar           *response;      /* eg. : INVITE */
64         guint32          packets;
65         sipstat_t       *sp;
66 } sip_request_method_t;
67
68 /* TODO: extra codes to be added from SIP extensions? */
69 static const value_string vals_status_code[] = {
70     { 100, "Trying"},
71     { 180, "Ringing"},
72     { 181, "Call Is Being Forwarded"},
73     { 182, "Queued"},
74     { 183, "Session Progress"},
75     { 199, "Informational - Others" },
76
77     { 200, "OK"},
78     { 202, "Accepted"},
79     { 204, "No Notification"},
80     { 299, "Success - Others"}, /* used to keep track of other Success packets */
81
82     { 300, "Multiple Choices"},
83     { 301, "Moved Permanently"},
84     { 302, "Moved Temporarily"},
85     { 305, "Use Proxy"},
86     { 380, "Alternative Service"},
87     { 399, "Redirection - Others"},
88
89     { 400, "Bad Request"},
90     { 401, "Unauthorized"},
91     { 402, "Payment Required"},
92     { 403, "Forbidden"},
93     { 404, "Not Found"},
94     { 405, "Method Not Allowed"},
95     { 406, "Not Acceptable"},
96     { 407, "Proxy Authentication Required"},
97     { 408, "Request Timeout"},
98     { 410, "Gone"},
99     { 412, "Conditional Request Failed"},
100     { 413, "Request Entity Too Large"},
101     { 414, "Request-URI Too Long"},
102     { 415, "Unsupported Media Type"},
103     { 416, "Unsupported URI Scheme"},
104     { 420, "Bad Extension"},
105     { 421, "Extension Required"},
106     { 422, "Session Timer Too Small"},
107     { 423, "Interval Too Brief"},
108     { 428, "Use Identity Header"},
109     { 429, "Provide Referrer Identity"},
110     { 430, "Flow Failed"},
111     { 433, "Anonymity Disallowed"},
112     { 436, "Bad Identity-Info"},
113     { 437, "Unsupported Certificate"},
114     { 438, "Invalid Identity Header"},
115     { 439, "First Hop Lacks Outbound Support"},
116     { 440, "Max-Breadth Exceeded"},
117     { 470, "Consent Needed"},
118     { 480, "Temporarily Unavailable"},
119     { 481, "Call/Transaction Does Not Exist"},
120     { 482, "Loop Detected"},
121     { 483, "Too Many Hops"},
122     { 484, "Address Incomplete"},
123     { 485, "Ambiguous"},
124     { 486, "Busy Here"},
125     { 487, "Request Terminated"},
126     { 488, "Not Acceptable Here"},
127     { 489, "Bad Event"},
128     { 491, "Request Pending"},
129     { 493, "Undecipherable"},
130     { 494, "Security Agreement Required"},
131     { 499, "Client Error - Others"},
132
133     { 500, "Server Internal Error"},
134     { 501, "Not Implemented"},
135     { 502, "Bad Gateway"},
136     { 503, "Service Unavailable"},
137     { 504, "Server Time-out"},
138     { 505, "Version Not Supported"},
139     { 513, "Message Too Large"},
140     { 599, "Server Error - Others"},
141
142     { 600, "Busy Everywhere"},
143     { 603, "Decline"},
144     { 604, "Does Not Exist Anywhere"},
145     { 606, "Not Acceptable"},
146     { 699, "Global Failure - Others"},
147
148     { 0,        NULL}
149 };
150
151 /* Create tables for responses and requests */
152 static void
153 sip_init_hash(sipstat_t *sp)
154 {
155     int i;
156
157     /* Create responses table */
158     sp->hash_responses = g_hash_table_new(g_int_hash, g_int_equal);
159
160     /* Add all response codes */
161     for (i=0 ; vals_status_code[i].strptr ; i++)
162     {
163         gint *key = g_new (gint,1);
164         sip_response_code_t *sc = g_new (sip_response_code_t,1);
165         *key = vals_status_code[i].value;
166         sc->packets=0;
167         sc->response_code =  *key;
168         sc->name=vals_status_code[i].strptr;
169         sc->sp = sp;
170         g_hash_table_insert(sc->sp->hash_responses, key, sc);
171     }
172
173     /* Create empty requests table */
174     sp->hash_requests = g_hash_table_new(g_str_hash, g_str_equal);
175 }
176
177 static void
178 sip_draw_hash_requests( gchar *key _U_ , sip_request_method_t *data, gchar * format)
179 {
180         if (data->packets==0)
181                 return;
182         printf( format, data->response, data->packets);
183 }
184
185 static void
186 sip_draw_hash_responses( gint * key _U_ , sip_response_code_t *data, char * format)
187 {
188         if (data==NULL) {
189                 g_warning("C'est quoi ce borderl key=%d\n", *key);
190                 exit(EXIT_FAILURE);
191         }
192         if (data->packets==0)
193                 return;
194         printf(format,  data->response_code, data->name, data->packets );
195 }
196
197 /* NOT USED at this moment */
198 /*
199 static void
200 sip_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
201 {
202         g_free(key);
203         g_free(value);
204 }
205 */
206
207 static void
208 sip_reset_hash_responses(gchar *key _U_ , sip_response_code_t *data, gpointer ptr _U_ )
209 {
210         data->packets = 0;
211 }
212 static void
213 sip_reset_hash_requests(gchar *key _U_ , sip_request_method_t *data, gpointer ptr _U_ )
214 {
215         data->packets = 0;
216 }
217
218 static void
219 sipstat_reset(void *psp  )
220 {
221         sipstat_t *sp=(sipstat_t *)psp;
222         if (sp) {
223                 sp->packets = 0;
224                 sp->resent_packets = 0;
225                 sp->average_setup_time = 0;
226                 sp->max_setup_time = 0;
227                 sp->min_setup_time = 0;
228                 sp->no_of_completed_calls = 0;
229                 sp->total_setup_time = 0;
230
231                 g_hash_table_foreach( sp->hash_responses, (GHFunc)sip_reset_hash_responses, NULL);
232                 g_hash_table_foreach( sp->hash_requests, (GHFunc)sip_reset_hash_requests, NULL);
233         }
234 }
235
236
237 /* Main entry point to SIP tap */
238 static int
239 sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri)
240 {
241     const sip_info_value_t *value=(const sip_info_value_t *)pri;
242     sipstat_t *sp = (sipstat_t *)psp;
243
244     /* Total number of packets, including continuation packets */
245     sp->packets++;
246
247         /* Calculate average setup time */
248         if (value->setup_time){
249                 sp->no_of_completed_calls++;
250                 /* Check if it's the first value */
251                 if ( sp->total_setup_time == 0 ){
252                         sp->average_setup_time = value->setup_time;
253                         sp->total_setup_time = value->setup_time;
254                         sp->max_setup_time = value->setup_time;
255                         sp->min_setup_time = value->setup_time;
256                 }else{
257                         sp->total_setup_time = sp->total_setup_time + value->setup_time;
258                         if (sp->max_setup_time < value->setup_time){
259                                 sp->max_setup_time = value->setup_time;
260                         }
261                         if (sp->min_setup_time > value->setup_time){
262                                 sp->min_setup_time = value->setup_time;
263                         }
264                         /* Calculate average */
265                         sp->average_setup_time = (guint32)(sp->total_setup_time / sp->no_of_completed_calls);
266                 }
267         }
268
269     /* Update resent count if flag set */
270     if (value->resend)
271     {
272         sp->resent_packets++;
273     }
274
275
276     /* Looking at both requests and responses */
277     if (value->response_code != 0)
278     {
279         /* Responses */
280         guint *key = g_new(guint,1);
281         sip_response_code_t *sc;
282
283         /* Look up response code in hash table */
284         *key = value->response_code;
285         sc = (sip_response_code_t *)g_hash_table_lookup(sp->hash_responses, key);
286         if (sc==NULL)
287         {
288             /* Non-standard status code ; we classify it as others
289              * in the relevant category
290              * (Informational,Success,Redirection,Client Error,Server Error,Global Failure)
291              */
292             int i = value->response_code;
293             if ((i<100) || (i>=700))
294             {
295                 /* Forget about crazy values */
296                 return 0;
297             }
298             else if (i<200)
299             {
300                 *key=199;       /* Hopefully, this status code will never be used */
301             }
302             else if (i<300)
303             {
304                 *key=299;
305             }
306             else if (i<400)
307             {
308                 *key=399;
309             }
310             else if (i<500)
311             {
312                 *key=499;
313             }
314             else if (i<600)
315             {
316                 *key=599;
317             }
318             else
319             {
320                 *key = 699;
321             }
322
323             /* Now look up this fallback code to get its text description */
324             sc = (sip_response_code_t *)g_hash_table_lookup(sp->hash_responses, key);
325             if (sc==NULL)
326             {
327                 return 0;
328             }
329         }
330         sc->packets++;
331     }
332     else if (value->request_method)
333     {
334         /* Requests */
335         sip_request_method_t *sc;
336
337         /* Look up the request method in the table */
338         sc = (sip_request_method_t *)g_hash_table_lookup(sp->hash_requests, value->request_method);
339         if (sc == NULL)
340         {
341             /* First of this type. Create structure and initialise */
342             sc=g_new(sip_request_method_t,1);
343             sc->response = g_strdup(value->request_method);
344             sc->packets = 1;
345             sc->sp = sp;
346             /* Insert it into request table */
347             g_hash_table_insert(sp->hash_requests, sc->response, sc);
348         }
349         else
350         {
351             /* Already existed, just update count for that method */
352             sc->packets++;
353         }
354         /* g_free(value->request_method); */
355     }
356     else
357     {
358         /* No request method set. Just ignore */
359         return 0;
360     }
361
362     return 1;
363 }
364
365 static void
366 sipstat_draw(void *psp  )
367 {
368         sipstat_t *sp=(sipstat_t *)psp;
369         printf("\n");
370         printf("===================================================================\n");
371         if (sp->filter == NULL)
372                 printf("SIP Statistics\n");
373         else
374                 printf("SIP Statistics with filter %s\n", sp->filter);
375
376         printf("\nNumber of SIP messages: %d", sp->packets);
377         printf("\nNumber of resent SIP messages: %d\n", sp->resent_packets);
378         printf( "\n* SIP Status Codes in reply packets\n");
379         g_hash_table_foreach( sp->hash_responses, (GHFunc)sip_draw_hash_responses,
380                 (gpointer)"  SIP %3d %-15s : %5d Packets\n");
381         printf("\n* List of SIP Request methods\n");
382         g_hash_table_foreach( sp->hash_requests,  (GHFunc)sip_draw_hash_requests,
383                 (gpointer)"  %-15s : %5d Packets\n");
384         printf( "\n* Average setup time %d ms\n Min %d ms\n Max %d ms\n", sp->average_setup_time, sp->min_setup_time, sp->max_setup_time);
385         printf("===================================================================\n");
386 }
387
388 static void
389 sipstat_init(const char *opt_arg, void* userdata _U_)
390 {
391         sipstat_t *sp;
392         const char *filter=NULL;
393         GString *error_string;
394
395         if (strncmp (opt_arg, "sip,stat,", 9) == 0){
396                 filter=opt_arg+9;
397         } else {
398                 filter=NULL;
399         }
400
401         sp = g_new0(sipstat_t,1);
402         if(filter){
403                 sp->filter=g_strdup(filter);
404         } else {
405                 sp->filter=NULL;
406         }
407         /*g_hash_table_foreach( sip_status, (GHFunc)sip_reset_hash_responses, NULL);*/
408
409
410         error_string = register_tap_listener(
411                         "sip",
412                         sp,
413                         filter,
414                         0,
415                         sipstat_reset,
416                         sipstat_packet,
417                         sipstat_draw);
418         if (error_string){
419                 /* error, we failed to attach to the tap. clean up */
420                 g_free(sp->filter);
421                 g_free(sp);
422                 fprintf (stderr, "tshark: Couldn't register sip,stat tap: %s\n",
423                                 error_string->str);
424                 g_string_free(error_string, TRUE);
425                 exit(1);
426         }
427
428         sp->packets = 0;
429         sp->resent_packets = 0;
430         sip_init_hash(sp);
431 }
432
433 void
434 register_tap_listener_sipstat(void)
435 {
436         register_stat_cmd_arg("sip,stat", sipstat_init,NULL);
437 }