Patch from Isaac Wilcox to do case-sensitive checks for HTTP methods and
[obnox/wireshark/wip.git] / packet-http.c
1 /* packet-http.c
2  * Routines for HTTP packet disassembly
3  *
4  * Guy Harris <guy@alum.mit.edu>
5  *
6  * $Id: packet-http.c,v 1.24 2000/10/19 07:38:01 guy Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@zing.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * 
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  *
27  *
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
36 #endif
37
38 #include <string.h>
39 #include <ctype.h>
40
41 #include <glib.h>
42 #include "packet.h"
43 #include "packet-ipp.h"
44 #include "strutil.h"
45
46 typedef enum _http_type {
47         HTTP_REQUEST,
48         HTTP_RESPONSE,
49         HTTP_OTHERS
50 } http_type_t;
51
52 static int proto_http = -1;
53 static int hf_http_response = -1;
54 static int hf_http_request = -1;
55
56 static gint ett_http = -1;
57
58 #define TCP_PORT_HTTP                   80
59 #define TCP_PORT_PROXY_HTTP             3128
60 #define TCP_PORT_PROXY_ADMIN_HTTP       3132
61 #define TCP_ALT_PORT_HTTP               8080
62
63 static int is_http_request_or_reply(const u_char *data, int linelen, http_type_t *type);
64
65 void dissect_http(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
66 {
67         gboolean        is_ipp = (pi.srcport == 631 || pi.destport == 631);
68         proto_item      *ti;
69         proto_tree      *http_tree;
70         const u_char    *data, *dataend;
71         const u_char    *linep, *lineend, *eol;
72         int             linelen;
73         u_char          c;
74         http_type_t     http_type = HTTP_OTHERS;
75
76         OLD_CHECK_DISPLAY_AS_DATA(proto_http, pd, offset, fd, tree);
77
78         data = &pd[offset];
79         dataend = data + END_OF_FRAME;
80
81         if (check_col(fd, COL_PROTOCOL))
82                 col_add_str(fd, COL_PROTOCOL, is_ipp ? "IPP" : "HTTP");
83         if (check_col(fd, COL_INFO)) {
84                 /*
85                  * Put the first line from the buffer into the summary,
86                  * if it's an HTTP request or reply (but leave out the
87                  * "\r\n", or whatever, at the end).
88                  * Otherwise, just call it a continuation.
89                  */
90                 lineend = find_line_end(data, dataend, &eol);
91                 linelen = eol - data;
92                 if (is_http_request_or_reply(data, linelen, &http_type))
93                         col_add_str(fd, COL_INFO, format_text(data, linelen));
94                 else
95                         col_add_str(fd, COL_INFO, "Continuation");
96         }
97
98         if (tree) {
99                 ti = proto_tree_add_item(tree, proto_http, NullTVB, offset, END_OF_FRAME, FALSE);
100                 http_tree = proto_item_add_subtree(ti, ett_http);
101
102                 while (data < dataend) {
103                         /*
104                          * Find the end of the line.
105                          */
106                         lineend = find_line_end(data, dataend, &eol);
107                         linelen = lineend - data;
108
109                         /*
110                          * OK, does it look like an HTTP request or
111                          * response?
112                          */
113                         if (is_http_request_or_reply(data, linelen, &http_type))
114                                 goto is_http;
115
116                         /*
117                          * No.  Does it look like a blank line (as would
118                          * appear at the end of an HTTP request)?
119                          */
120                         if (linelen == 1) {
121                                 if (*data == '\n')
122                                         goto is_http;
123                         }
124                         if (linelen == 2) {
125                                 if (strncmp(data, "\r\n", 2) == 0 ||
126                                     strncmp(data, "\n\r", 2) == 0)
127                                         goto is_http;
128                         }
129
130                         /*
131                          * No.  Does it look like a MIME header?
132                          */
133                         linep = data;
134                         while (linep < lineend) {
135                                 c = *linep++;
136                                 if (!isprint(c))
137                                         break;  /* not printable, not a MIME header */
138                                 switch (c) {
139
140                                 case '(':
141                                 case ')':
142                                 case '<':
143                                 case '>':
144                                 case '@':
145                                 case ',':
146                                 case ';':
147                                 case '\\':
148                                 case '"':
149                                 case '/':
150                                 case '[':
151                                 case ']':
152                                 case '?':
153                                 case '=':
154                                 case '{':
155                                 case '}':
156                                         /*
157                                          * It's a tspecial, so it's not
158                                          * part of a token, so it's not
159                                          * a field name for the beginning
160                                          * of a MIME header.
161                                          */
162                                         goto not_http;
163
164                                 case ':':
165                                         /*
166                                          * This ends the token; we consider
167                                          * this to be a MIME header.
168                                          */
169                                         goto is_http;
170                                 }
171                         }
172
173                 not_http:
174                         /*
175                          * We don't consider this part of an HTTP request or
176                          * reply, so we don't display it.
177                          * (Yeah, that means we don't display, say, a
178                          * text/http page, but you can get that from the
179                          * data pane.)
180                          */
181                         break;
182
183                 is_http:
184                         /*
185                          * Put this line.
186                          */
187                         proto_tree_add_text(http_tree, NullTVB, offset, linelen, "%s",
188                             format_text(data, linelen));
189                         offset += linelen;
190                         data = lineend;
191                 }
192
193                 switch (http_type) {
194
195                 case HTTP_RESPONSE:
196                         proto_tree_add_boolean_hidden(http_tree, 
197                                                hf_http_response, NullTVB, 0, 0, 1);
198                         break;
199
200                 case HTTP_REQUEST:
201                         proto_tree_add_boolean_hidden(http_tree, 
202                                                hf_http_request, NullTVB, 0, 0, 1);
203                         break;
204
205                 case HTTP_OTHERS:
206                 default:
207                         break;
208                 }
209
210                 if (data < dataend) {
211                         if (is_ipp)
212                                 dissect_ipp(pd, offset, fd, tree);
213                         else
214                                 old_dissect_data(&pd[offset], offset, fd, http_tree);
215                 }
216         }
217 }
218
219 /*
220  * XXX - this won't handle HTTP 0.9 replies, but they're all data
221  * anyway.
222  */
223 static int
224 is_http_request_or_reply(const u_char *data, int linelen, http_type_t *type)
225 {
226         if (linelen >= 4) {
227                 if (strncmp(data, "GET ", 4) == 0 ||
228                     strncmp(data, "PUT ", 4) == 0) {
229                         if (*type == HTTP_OTHERS)
230                                 *type = HTTP_REQUEST;
231                         return TRUE;
232                 }
233         }
234         if (linelen >= 5) {
235                 if (strncmp(data, "HEAD ", 5) == 0 ||
236                     strncmp(data, "POST ", 5) == 0) {
237                         if (*type == HTTP_OTHERS)
238                                 *type = HTTP_REQUEST;
239                         return TRUE;
240                 }
241                 if (strncmp(data, "HTTP/", 5) == 0) {
242                         if (*type == HTTP_OTHERS)
243                                 *type = HTTP_RESPONSE;
244                         return TRUE;    /* response */
245                 }
246         }
247         if (linelen >= 6) {
248                 if (strncmp(data, "TRACE ", 6) == 0) {
249                         if (*type == HTTP_OTHERS)
250                                 *type = HTTP_REQUEST;
251                         return TRUE;
252                 }
253         }
254         if (linelen >= 7) {
255                 if (strncmp(data, "DELETE ", 7) == 0) {
256                         if (*type == HTTP_OTHERS)
257                                 *type = HTTP_REQUEST;
258                         return TRUE;
259                 }
260         }
261         if (linelen >= 8) {
262                 if (strncmp(data, "OPTIONS ", 8) == 0 ||
263                     strncmp(data, "CONNECT ", 8) == 0) {
264                         if (*type == HTTP_OTHERS)
265                                 *type = HTTP_REQUEST;
266                         return TRUE;
267                 }
268         }
269         return FALSE;
270 }
271
272 void
273 proto_register_http(void)
274 {
275
276   static hf_register_info hf[] = {
277     { &hf_http_response,
278       { "Response",             "http.response",  
279         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
280         "TRUE if HTTP response" }},
281     { &hf_http_request,
282       { "Request",              "http.request",
283         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
284         "TRUE if HTTP request" }},
285   };
286   static gint *ett[] = {
287     &ett_http,
288   };
289
290   proto_http = proto_register_protocol("Hypertext Transfer Protocol", "http");
291   proto_register_field_array(proto_http, hf, array_length(hf));
292   proto_register_subtree_array(ett, array_length(ett));
293 }
294
295 void
296 proto_reg_handoff_http(void)
297 {
298   old_dissector_add("tcp.port", TCP_PORT_HTTP, dissect_http);
299   old_dissector_add("tcp.port", TCP_ALT_PORT_HTTP, dissect_http);
300   old_dissector_add("tcp.port", TCP_PORT_PROXY_HTTP, dissect_http);
301   old_dissector_add("tcp.port", TCP_PORT_PROXY_ADMIN_HTTP, dissect_http);
302 }