Update:
[obnox/wireshark/wip.git] / tap-httpstat.c
1 /* tap-httpstat.c
2  * tap-httpstat   2003 Jean-Michel FAYARD
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
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 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <string.h>
31
32 #include "epan/packet_info.h"
33 #include "epan/value_string.h"
34 #include <epan/tap.h>
35 #include <epan/stat_cmd_args.h>
36 #include <epan/dissectors/packet-http.h>
37
38
39 /* used to keep track of the statictics for an entire program interface */
40 typedef struct _http_stats_t {
41         char            *filter;
42         GHashTable      *hash_responses;
43         GHashTable      *hash_requests;
44 } httpstat_t;
45
46 /* used to keep track of the stats for a specific response code
47  * for example it can be { 3, 404, "Not Found" ,...}
48  * which means we captured 3 reply http/1.1 404 Not Found */
49 typedef struct _http_response_code_t {
50         guint32          packets;               /* 3 */
51         guint            response_code;         /* 404 */
52         const gchar     *name;                  /* Not Found */
53         httpstat_t      *sp;
54 } http_response_code_t;
55
56 /* used to keep track of the stats for a specific request string */
57 typedef struct _http_request_methode_t {
58         gchar           *response;      /* eg. : GET */
59         guint32          packets;
60         httpstat_t      *sp;
61 } http_request_methode_t;
62
63
64 static const value_string vals_status_code[] = {
65         { 100, "Continue" },
66         { 101, "Switching Protocols" },
67         { 199, "Informational - Others" },
68
69         { 200, "OK"},
70         { 201, "Created"},
71         { 202, "Accepted"},
72         { 203, "Non-authoritative Information"},
73         { 204, "No Content"},
74         { 205, "Reset Content"},
75         { 206, "Partial Content"},
76         { 299, "Success - Others"},     /* used to keep track of others Success packets */
77
78         { 300, "Multiple Choices"},
79         { 301, "Moved Permanently"},
80         { 302, "Moved Temporarily"},
81         { 303, "See Other"},
82         { 304, "Not Modified"},
83         { 305, "Use Proxy"},
84         { 399, "Redirection - Others"},
85
86         { 400, "Bad Request"},
87         { 401, "Unauthorized"},
88         { 402, "Payment Required"},
89         { 403, "Forbidden"},
90         { 404, "Not Found"},
91         { 405, "Method Not Allowed"},
92         { 406, "Not Acceptable"},
93         { 407, "Proxy Authentication Required"},
94         { 408, "Request Time-out"},
95         { 409, "Conflict"},
96         { 410, "Gone"},
97         { 411, "Length Required"},
98         { 412, "Precondition Failed"},
99         { 413, "Request Entity Too Large"},
100         { 414, "Request-URI Too Large"},
101         { 415, "Unsupported Media Type"},
102         { 499, "Client Error - Others"},
103
104         { 500, "Internal Server Error"},
105         { 501, "Not Implemented"},
106         { 502, "Bad Gateway"},
107         { 503, "Service Unavailable"},
108         { 504, "Gateway Time-out"},
109         { 505, "HTTP Version not supported"},
110         { 599, "Server Error - Others"},
111
112         { 0,    NULL}
113 } ;
114
115 /* insert some entries */
116 static void
117 http_init_hash( httpstat_t *sp)
118 {
119         int i;
120
121         sp->hash_responses = g_hash_table_new( g_int_hash, g_int_equal);
122
123         for (i=0 ; vals_status_code[i].strptr ; i++ )
124         {
125                 gint *key = g_malloc (sizeof(gint));
126                 http_response_code_t *sc = g_malloc (sizeof(http_response_code_t));
127                 *key = vals_status_code[i].value;
128                 sc->packets=0;
129                 sc->response_code =  *key;
130                 sc->name=vals_status_code[i].strptr;
131                 sc->sp = sp;
132                 g_hash_table_insert( sc->sp->hash_responses, key, sc);
133         }
134         sp->hash_requests = g_hash_table_new( g_str_hash, g_str_equal);
135 }
136 static void
137 http_draw_hash_requests( gchar *key _U_ , http_request_methode_t *data, gchar * format)
138 {
139         if (data->packets==0)
140                 return;
141         printf( format, data->response, data->packets);
142 }
143
144 static void
145 http_draw_hash_responses( gint * key _U_ , http_response_code_t *data, char * format)
146 {
147         if (data==NULL) {
148                 g_warning("No data available, key=%d\n", *key);
149                 exit(EXIT_FAILURE);
150         }
151         if (data->packets==0)
152                 return;
153         /* "     HTTP %3d %-35s %9d packets", */
154         printf(format,  data->response_code, data->name, data->packets );
155 }
156
157
158
159 /* NOT USED at this moment */
160 /*
161 static void
162 http_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
163 {
164         g_free(key);
165         g_free(value);
166 }
167 */
168 static void
169 http_reset_hash_responses(gchar *key _U_ , http_response_code_t *data, gpointer ptr _U_ )
170 {
171         data->packets = 0;
172 }
173 static void
174 http_reset_hash_requests(gchar *key _U_ , http_request_methode_t *data, gpointer ptr _U_ )
175 {
176         data->packets = 0;
177 }
178
179 static void
180 httpstat_reset(void *psp  )
181 {
182         httpstat_t *sp=psp;
183
184         g_hash_table_foreach( sp->hash_responses, (GHFunc)http_reset_hash_responses, NULL);
185         g_hash_table_foreach( sp->hash_requests, (GHFunc)http_reset_hash_requests, NULL);
186
187 }
188
189 static int
190 httpstat_packet(void *psp , packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri)
191 {
192         const http_info_value_t *value=pri;
193         httpstat_t *sp=(httpstat_t *) psp;
194
195         /* We are only interested in reply packets with a status code */
196         /* Request or reply packets ? */
197         if (value->response_code!=0) {
198                 guint *key=g_malloc( sizeof(guint) );
199                 http_response_code_t *sc;
200
201                 *key=value->response_code;
202                 sc =  g_hash_table_lookup(
203                                 sp->hash_responses,
204                                 key);
205                 if (sc==NULL){
206                         /* non standard status code ; we classify it as others
207                          * in the relevant category (Informational,Success,Redirection,Client Error,Server Error)
208                          */
209                         int i = value->response_code;
210                         if ((i<100) || (i>=600)) {
211                                 return 0;
212                         }
213                         else if (i<200){
214                                 *key=199;       /* Hopefully, this status code will never be used */
215                         }
216                         else if (i<300){
217                                 *key=299;
218                         }
219                         else if (i<400){
220                                 *key=399;
221                         }
222                         else if (i<500){
223                                 *key=499;
224                         }
225                         else{
226                                 *key=599;
227                         }
228                         sc =  g_hash_table_lookup(
229                                 sp->hash_responses,
230                                 key);
231                         if (sc==NULL)
232                                 return 0;
233                 }
234                 sc->packets++;
235         }
236         else if (value->request_method){
237                 http_request_methode_t *sc;
238
239                 sc =  g_hash_table_lookup(
240                                 sp->hash_requests,
241                                 value->request_method);
242                 if (sc==NULL){
243                         sc=g_malloc( sizeof(http_request_methode_t) );
244                         sc->response=g_strdup( value->request_method );
245                         sc->packets=1;
246                         sc->sp = sp;
247                         g_hash_table_insert( sp->hash_requests, sc->response, sc);
248                 } else {
249                         sc->packets++;
250                 }
251         } else {
252                 return 0;
253         }
254         return 1;
255 }
256
257
258 static void
259 httpstat_draw(void *psp  )
260 {
261         httpstat_t *sp=psp;
262         printf("\n");
263         printf("===================================================================\n");
264         if (! sp->filter[0])
265                 printf("HTTP Statistics\n");
266         else
267                 printf("HTTP Statistics with filter %s\n", sp->filter);
268
269         printf( "* HTTP Status Codes in reply packets\n");
270         g_hash_table_foreach( sp->hash_responses, (GHFunc)http_draw_hash_responses,
271                 "    HTTP %3d %s\n");
272         printf("* List of HTTP Request methods\n");
273         g_hash_table_foreach( sp->hash_requests,  (GHFunc)http_draw_hash_requests,
274                 "    %9s %d \n");
275         printf("===================================================================\n");
276 }
277
278
279
280 /* When called, this function will create a new instance of gtk_httpstat.
281  */
282 static void
283 gtk_httpstat_init(const char *optarg,void* userdata _U_)
284 {
285         httpstat_t *sp;
286         const char *filter=NULL;
287         GString *error_string;
288
289         if (!strncmp (optarg, "http,stat,", 10)){
290                 filter=optarg+10;
291         } else {
292                 filter=NULL;
293         }
294
295         sp = g_malloc( sizeof(httpstat_t) );
296         if(filter){
297                 sp->filter=g_strdup(filter);
298         } else {
299                 sp->filter=NULL;
300         }
301         /*g_hash_table_foreach( http_status, (GHFunc)http_reset_hash_responses, NULL);*/
302
303
304         error_string = register_tap_listener(
305                         "http",
306                         sp,
307                         filter,
308                         0,
309                         httpstat_reset,
310                         httpstat_packet,
311                         httpstat_draw);
312         if (error_string){
313                 /* error, we failed to attach to the tap. clean up */
314                 g_free(sp->filter);
315                 g_free(sp);
316                 fprintf (stderr, "tshark: Couldn't register http,stat tap: %s\n",
317                                 error_string->str);
318                 g_string_free(error_string, TRUE);
319                 exit(1);
320         }
321
322         http_init_hash(sp);
323 }
324
325 void
326 register_tap_listener_gtkhttpstat(void)
327 {
328         register_stat_cmd_arg("http,stat,", gtk_httpstat_init,NULL);
329 }