From Richard Urwin a great enhancement to the color filter dialogue to
[obnox/wireshark/wip.git] / packet-icap.c
1 /* packet-icap.c
2  * Routines for ICAP packet disassembly
3  *
4  * Srishylam Simharajan simha@netapp.com
5  *
6  * $Id: packet-icap.c,v 1.12 2002/08/28 21:00:17 jmayer Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <string.h>
32 #include <ctype.h>
33
34 #include <glib.h>
35 #include <epan/packet.h>
36 #include <epan/strutil.h>
37
38 typedef enum _icap_type {
39         ICAP_OPTIONS,
40         ICAP_REQMOD,
41         ICAP_RESPMOD,
42         ICAP_RESPONSE,
43         ICAP_OTHER
44 } icap_type_t;
45
46 static int proto_icap = -1;
47 static int hf_icap_response = -1;
48 static int hf_icap_reqmod = -1;
49 static int hf_icap_respmod = -1;
50 static int hf_icap_options = -1;
51 static int hf_icap_other = -1;
52
53 static gint ett_icap = -1;
54
55 static dissector_handle_t data_handle;
56
57 #define TCP_PORT_ICAP                   1344
58 static int is_icap_message(const guchar *data, int linelen, icap_type_t *type);
59 static void
60 dissect_icap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
61 {
62         proto_tree      *icap_tree = NULL;
63         proto_item      *ti = NULL;
64         gint            offset = 0;
65         const guchar    *line;
66         gint            next_offset;
67         const guchar    *linep, *lineend;
68         int             linelen;
69         guchar          c;
70         icap_type_t     icap_type;
71         int             datalen;
72
73         if (check_col(pinfo->cinfo, COL_PROTOCOL))
74                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICAP");
75
76         if (check_col(pinfo->cinfo, COL_INFO)) {
77                 /*
78                  * Put the first line from the buffer into the summary
79                  * if it's an ICAP header (but leave out the
80                  * line terminator).
81                  * Otherwise, just call it a continuation.
82                  *
83                  * Note that "tvb_find_line_end()" will return a value that
84                  * is not longer than what's in the buffer, so the
85                  * "tvb_get_ptr()" call won't throw an exception.
86                  */
87                 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
88                     FALSE);
89                 line = tvb_get_ptr(tvb, offset, linelen);
90                 icap_type = ICAP_OTHER; /* type not known yet */
91                 if (is_icap_message(line, linelen, &icap_type))
92                         col_add_str(pinfo->cinfo, COL_INFO,
93                             format_text(line, linelen));
94                 else
95                         col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
96         }
97
98         if (tree) {
99                 ti = proto_tree_add_item(tree, proto_icap, tvb, offset, -1,
100                     FALSE);
101                 icap_tree = proto_item_add_subtree(ti, ett_icap);
102         }
103
104         /*
105          * Process the packet data, a line at a time.
106          */
107         icap_type = ICAP_OTHER; /* type not known yet */
108         while (tvb_offset_exists(tvb, offset)) {
109                 gboolean is_icap = FALSE;
110                 gboolean loop_done = FALSE;
111                 /*
112                  * Find the end of the line.
113                  */
114                 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
115                     FALSE);
116
117                 /*
118                  * Get a buffer that refers to the line.
119                  */
120                 line = tvb_get_ptr(tvb, offset, linelen);
121                 lineend = line + linelen;
122
123                 /*
124                  * find header format
125                  */
126                 if (is_icap_message(line, linelen, &icap_type)) {
127                         is_icap = TRUE;
128                         goto is_icap_header;
129                 }
130
131                 /*
132                  * if it looks like a blank line, end of header perhaps?
133                  */
134                 if (linelen == 0) {
135                         is_icap = TRUE;
136                         goto is_icap_header;
137                 }
138
139                 /*
140                  * No.  Does it look like a MIME header?
141                  */
142                 linep = line;
143                 loop_done = FALSE;
144                 while (linep < lineend && (!loop_done)) {
145                         c = *linep++;
146                         if (!isprint(c)) {
147                                 is_icap = FALSE;
148                                 break;  /* not printable, not a MIME header */
149                         }
150                         switch (c) {
151                         case ':':
152                                 is_icap = TRUE;
153                                 goto is_icap_header;
154                                 break;
155                         case '(':
156                         case ')':
157                         case '<':
158                         case '>':
159                         case '@':
160                         case ',':
161                         case ';':
162                         case '\\':
163                         case '"':
164                         case '/':
165                         case '[':
166                         case ']':
167                         case '?':
168                         case '=':
169                         case '{':
170                         case '}':
171                                 is_icap = FALSE;
172                                 loop_done = TRUE;
173                                 break;
174                         }
175                 }
176
177                 /*
178                  * We don't consider this part of an ICAP message,
179                  * so we don't display it.
180                  * (Yeah, that means we don't display, say, a text/icap
181                  * page, but you can get that from the data pane.)
182                  */
183                 if (!is_icap)
184                         break;
185 is_icap_header:
186                 if (tree) {
187                         proto_tree_add_text(icap_tree, tvb, offset,
188                                 next_offset - offset, "%s",
189                                 tvb_format_text(tvb, offset,
190                                                 next_offset - offset)
191                                 );
192                 }
193                 offset = next_offset;
194         }
195
196         if (tree) {
197                 switch (icap_type) {
198
199                 case ICAP_OPTIONS:
200                         proto_tree_add_boolean_hidden(icap_tree,
201                             hf_icap_options, tvb, 0, 0, 1);
202                         break;
203
204                 case ICAP_REQMOD:
205                         proto_tree_add_boolean_hidden(icap_tree,
206                             hf_icap_reqmod, tvb, 0, 0, 1);
207                         break;
208
209                 case ICAP_RESPMOD:
210                         proto_tree_add_boolean_hidden(icap_tree,
211                             hf_icap_respmod, tvb, 0, 0, 1);
212                         break;
213
214                 case ICAP_RESPONSE:
215                         proto_tree_add_boolean_hidden(icap_tree,
216                             hf_icap_response, tvb, 0, 0, 1);
217                         break;
218
219                 case ICAP_OTHER:
220                 default:
221                         break;
222                 }
223         }
224
225         datalen = tvb_length_remaining(tvb, offset);
226         if (datalen > 0) {
227                 call_dissector(data_handle,
228                     tvb_new_subset(tvb, offset, -1, -1), pinfo, icap_tree);
229         }
230 }
231
232
233 static int
234 is_icap_message(const guchar *data, int linelen, icap_type_t *type)
235 {
236 #define ICAP_COMPARE(string, length, msgtype) {         \
237         if (strncmp(data, string, length) == 0) {       \
238                 if (*type == ICAP_OTHER)                \
239                         *type = msgtype;                \
240                 return TRUE;                            \
241         }                                               \
242 }
243         /*
244          * From draft-elson-opes-icap-01(72).txt
245          */
246         if (linelen >= 5) {
247                 ICAP_COMPARE("ICAP/", 5, ICAP_RESPONSE); /* response */
248         }
249         if (linelen >= 7) {
250                 ICAP_COMPARE("REQMOD ", 7, ICAP_REQMOD); /* request mod */
251         }
252         if (linelen >= 8) {
253                 ICAP_COMPARE("OPTIONS ", 8, ICAP_OPTIONS); /* options */
254                 ICAP_COMPARE("RESPMOD ", 8, ICAP_RESPMOD); /* response mod */
255         }
256         return FALSE;
257 #undef ICAP_COMPARE
258 }
259
260 void
261 proto_register_icap(void)
262 {
263         static hf_register_info hf[] = {
264             { &hf_icap_response,
265               { "Response",             "icap.response",
266                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
267                 "TRUE if ICAP response", HFILL }},
268             { &hf_icap_reqmod,
269               { "Reqmod",               "icap.reqmod",
270                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
271                 "TRUE if ICAP reqmod", HFILL }},
272             { &hf_icap_respmod,
273               { "Respmod",              "icap.respmod",
274                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
275                 "TRUE if ICAP respmod", HFILL }},
276             { &hf_icap_options,
277               { "Options",              "icap.options",
278                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
279                 "TRUE if ICAP options", HFILL }},
280             { &hf_icap_other,
281               { "Other",                "icap.other",
282                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
283                 "TRUE if ICAP other", HFILL }},
284         };
285         static gint *ett[] = {
286                 &ett_icap,
287         };
288
289         proto_icap = proto_register_protocol(
290                         "Internet Content Adaptation Protocol",
291                         "ICAP", "icap");
292         proto_register_field_array(proto_icap, hf, array_length(hf));
293         proto_register_subtree_array(ett, array_length(ett));
294
295 }
296
297 void
298 proto_reg_handoff_icap(void)
299 {
300         dissector_handle_t icap_handle;
301
302         data_handle = find_dissector("data");
303         icap_handle = create_dissector_handle(dissect_icap, proto_icap);
304         dissector_add("tcp.port", TCP_PORT_ICAP, icap_handle);
305 }