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