Remove "text2pcap-scanner.obj" and "tools\lemon\lemon.obj" when a "nmake
[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  * packet-icap.c Mon Aug 13 17:50:19 PDT 2001 simha 
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 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
34
35 #include <string.h>
36 #include <ctype.h>
37
38 #include <glib.h>
39 #include "packet.h"
40 #include "strutil.h"
41
42 typedef enum _icap_type {
43         ICAP_OPTIONS,
44         ICAP_REQMOD,
45         ICAP_RESPMOD,
46         ICAP_RESPONSE,
47         ICAP_OTHER
48 } icap_type_t;
49
50 static int proto_icap = -1;
51 static int hf_icap_response = -1;
52 static int hf_icap_reqmod = -1;
53 static int hf_icap_respmod = -1;
54 static int hf_icap_options = -1;
55 static int hf_icap_other = -1;
56
57 static gint ett_icap = -1;
58
59 #define TCP_PORT_ICAP                   1344
60 static int is_icap_message(const u_char *data, int linelen, icap_type_t *type);
61 static void
62 dissect_icap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
63 {
64         proto_tree      *icap_tree = NULL;
65         proto_item      *ti = NULL;
66         gint            offset = 0;
67         const u_char    *line;
68         gint            next_offset;
69         const u_char    *linep, *lineend;
70         int             linelen;
71         u_char          c;
72         icap_type_t     icap_type;
73         int             datalen;
74
75         if (check_col(pinfo->fd, COL_PROTOCOL))
76                 col_set_str(pinfo->fd, COL_PROTOCOL, "ICAP");
77
78         if (check_col(pinfo->fd, COL_INFO)) {
79                 /*
80                  * Put the first line from the buffer into the summary
81                  * if it's an ICAP header (but leave out the
82                  * line terminator).
83                  * Otherwise, just call it a continuation.
84                  *
85                  * Note that "tvb_find_line_end()" will return a value that
86                  * is not longer than what's in the buffer, so the
87                  * "tvb_get_ptr()" call won't throw an exception.
88                  */
89                 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset);
90                 line = tvb_get_ptr(tvb, offset, linelen);
91                 icap_type = ICAP_OTHER; /* type not known yet */
92                 if (is_icap_message(line, linelen, &icap_type))
93                         col_add_str(pinfo->fd, COL_INFO,
94                             format_text(line, linelen));
95                 else
96                         col_set_str(pinfo->fd, COL_INFO, "Continuation");
97         }
98
99         if (tree) {
100                 ti = proto_tree_add_item(tree, proto_icap, tvb, offset,
101                     tvb_length_remaining(tvb, offset), FALSE);
102                 icap_tree = proto_item_add_subtree(ti, ett_icap);
103         }
104
105         /*
106          * Process the packet data, a line at a time.
107          */
108         icap_type = ICAP_OTHER; /* type not known yet */
109         while (tvb_offset_exists(tvb, offset)) {
110                 gboolean is_icap = FALSE;
111                 gboolean loop_done = FALSE;
112                 /*
113                  * Find the end of the line.
114                  */
115                 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset);
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                 tvbuff_t *next_tvb = tvb_new_subset(tvb, offset, -1, -1);
228                 dissect_data(tvb, offset, pinfo, icap_tree);
229         }
230 }
231
232         
233 static int
234 is_icap_message(const u_char *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_add("tcp.port", TCP_PORT_ICAP, dissect_icap, proto_icap);
301 }