HTTPS (almost) everywhere.
[metze/wireshark/wip.git] / epan / dissectors / packet-icap.c
1 /* packet-icap.c
2  * Routines for ICAP packet disassembly
3  * RFC 3507
4  *
5  * Srishylam Simharajan simha@netapp.com
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * SPDX-License-Identifier: GPL-2.0-or-later
12  */
13
14 #include "config.h"
15
16
17 #include <epan/packet.h>
18 #include <epan/strutil.h>
19 #include "packet-tls.h"
20
21 void proto_register_icap(void);
22 void proto_reg_handoff_icap(void);
23
24 typedef enum _icap_type {
25     ICAP_OPTIONS,
26     ICAP_REQMOD,
27     ICAP_RESPMOD,
28     ICAP_RESPONSE,
29     ICAP_OTHER
30 } icap_type_t;
31
32 static int proto_icap = -1;
33 static int hf_icap_response = -1;
34 static int hf_icap_reqmod = -1;
35 static int hf_icap_respmod = -1;
36 static int hf_icap_options = -1;
37 /* static int hf_icap_other = -1; */
38
39 static gint ett_icap = -1;
40
41 static dissector_handle_t http_handle;
42
43 #define TCP_PORT_ICAP           1344
44 static int is_icap_message(const guchar *data, int linelen, icap_type_t *type);
45 static int
46 dissect_icap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
47 {
48     proto_tree   *icap_tree = NULL;
49     proto_item   *ti        = NULL;
50     proto_item   *hidden_item;
51     tvbuff_t     *new_tvb;
52     gint          offset    = 0;
53     const guchar *line;
54     gint          next_offset;
55     const guchar *linep, *lineend;
56     int           linelen;
57     guchar        c;
58     icap_type_t   icap_type;
59     int           datalen;
60
61     col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICAP");
62
63     /*
64      * Put the first line from the buffer into the summary
65      * if it's an ICAP header (but leave out the
66      * line terminator).
67      * Otherwise, just call it a continuation.
68      *
69      * Note that "tvb_find_line_end()" will return a value that
70      * is not longer than what's in the buffer, so the
71      * "tvb_get_ptr()" call won't throw an exception.
72      */
73     linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
74     line = tvb_get_ptr(tvb, offset, linelen);
75     icap_type = ICAP_OTHER; /* type not known yet */
76     if (is_icap_message(line, linelen, &icap_type))
77         col_add_str(pinfo->cinfo, COL_INFO,
78             format_text(wmem_packet_scope(), line, linelen));
79     else
80         col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
81
82     if (tree) {
83         ti = proto_tree_add_item(tree, proto_icap, tvb, offset, -1,
84             ENC_NA);
85         icap_tree = proto_item_add_subtree(ti, ett_icap);
86     }
87
88     /*
89      * Process the packet data, a line at a time.
90      */
91     icap_type = ICAP_OTHER; /* type not known yet */
92     while (tvb_offset_exists(tvb, offset)) {
93         gboolean is_icap = FALSE;
94         gboolean loop_done = FALSE;
95         /*
96          * Find the end of the line.
97          */
98         linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
99             FALSE);
100
101         /*
102          * Get a buffer that refers to the line.
103          */
104         line = tvb_get_ptr(tvb, offset, linelen);
105         lineend = line + linelen;
106
107         /*
108          * find header format
109          */
110         if (is_icap_message(line, linelen, &icap_type)) {
111             goto is_icap_header;
112         }
113
114         /*
115          * if it looks like a blank line, end of header perhaps?
116          */
117         if (linelen == 0) {
118             goto is_icap_header;
119         }
120
121         /*
122          * No.  Does it look like a header?
123          */
124         linep = line;
125         loop_done = FALSE;
126         while (linep < lineend && (!loop_done)) {
127             c = *linep++;
128
129             /*
130              * This must be a CHAR, and must not be a CTL, to be part
131              * of a token; that means it must be printable ASCII.
132              *
133              * XXX - what about leading LWS on continuation
134              * lines of a header?
135              */
136             if (!g_ascii_isprint(c)) {
137                 is_icap = FALSE;
138                 break;
139             }
140
141             switch (c) {
142
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             case '=':
157             case '{':
158             case '}':
159                 /*
160                  * It's a separator, so it's not part of a
161                  * token, so it's not a field name for the
162                  * beginning of a header.
163                  *
164                  * (We don't have to check for HT; that's
165                  * already been ruled out by "iscntrl()".)
166                  *
167                  * XXX - what about ' '?  HTTP's checks
168                  * check for that.
169                  */
170                 is_icap = FALSE;
171                 loop_done = TRUE;
172                 break;
173
174             case ':':
175                 /*
176                  * This ends the token; we consider this
177                  * to be a header.
178                  */
179                 goto is_icap_header;
180             }
181         }
182
183         /*
184          * We don't consider this part of an ICAP message,
185          * so we don't display it.
186          * (Yeah, that means we don't display, say, a text/icap
187          * page, but you can get that from the data pane.)
188          */
189         if (!is_icap)
190             break;
191 is_icap_header:
192         proto_tree_add_format_text(icap_tree, tvb, offset, next_offset - offset);
193         offset = next_offset;
194     }
195
196     if (tree) {
197         switch (icap_type) {
198
199         case ICAP_OPTIONS:
200             hidden_item = proto_tree_add_boolean(icap_tree,
201                         hf_icap_options, tvb, 0, 0, 1);
202                         proto_item_set_hidden(hidden_item);
203             break;
204
205         case ICAP_REQMOD:
206             hidden_item = proto_tree_add_boolean(icap_tree,
207                         hf_icap_reqmod, tvb, 0, 0, 1);
208                         proto_item_set_hidden(hidden_item);
209             break;
210
211         case ICAP_RESPMOD:
212             hidden_item = proto_tree_add_boolean(icap_tree,
213                         hf_icap_respmod, tvb, 0, 0, 1);
214                         proto_item_set_hidden(hidden_item);
215             break;
216
217         case ICAP_RESPONSE:
218             hidden_item = proto_tree_add_boolean(icap_tree,
219                         hf_icap_response, tvb, 0, 0, 1);
220                         proto_item_set_hidden(hidden_item);
221             break;
222
223         case ICAP_OTHER:
224         default:
225             break;
226         }
227     }
228
229     datalen = tvb_reported_length_remaining(tvb, offset);
230     if (datalen > 0) {
231         if(http_handle){
232             new_tvb = tvb_new_subset_remaining(tvb, offset);
233             call_dissector(http_handle, new_tvb, pinfo, icap_tree);
234         }
235     }
236
237         return tvb_captured_length(tvb);
238 }
239
240
241 static int
242 is_icap_message(const guchar *data, int linelen, icap_type_t *type)
243 {
244 #define ICAP_COMPARE(string, length, msgtype) {     \
245     if (strncmp(data, string, length) == 0) {   \
246         if (*type == ICAP_OTHER)        \
247             *type = msgtype;        \
248         return TRUE;                \
249     }                       \
250 }
251     /*
252      * From draft-elson-opes-icap-01(72).txt
253      */
254     if (linelen >= 5) {
255         ICAP_COMPARE("ICAP/", 5, ICAP_RESPONSE); /* response */
256     }
257     if (linelen >= 7) {
258         ICAP_COMPARE("REQMOD ", 7, ICAP_REQMOD); /* request mod */
259     }
260     if (linelen >= 8) {
261         ICAP_COMPARE("OPTIONS ", 8, ICAP_OPTIONS); /* options */
262         ICAP_COMPARE("RESPMOD ", 8, ICAP_RESPMOD); /* response mod */
263     }
264     return FALSE;
265 #undef ICAP_COMPARE
266 }
267
268 void
269 proto_register_icap(void)
270 {
271     static hf_register_info hf[] = {
272         { &hf_icap_response,
273           { "Response",     "icap.response",
274             FT_BOOLEAN, BASE_NONE, NULL, 0x0,
275             "TRUE if ICAP response", HFILL }},
276         { &hf_icap_reqmod,
277           { "Reqmod",       "icap.reqmod",
278             FT_BOOLEAN, BASE_NONE, NULL, 0x0,
279             "TRUE if ICAP reqmod", HFILL }},
280         { &hf_icap_respmod,
281           { "Respmod",      "icap.respmod",
282             FT_BOOLEAN, BASE_NONE, NULL, 0x0,
283             "TRUE if ICAP respmod", HFILL }},
284         { &hf_icap_options,
285           { "Options",      "icap.options",
286             FT_BOOLEAN, BASE_NONE, NULL, 0x0,
287             "TRUE if ICAP options", HFILL }},
288 #if 0
289         { &hf_icap_other,
290           { "Other",        "icap.other",
291             FT_BOOLEAN, BASE_NONE, NULL, 0x0,
292             "TRUE if ICAP other", HFILL }},
293 #endif
294     };
295     static gint *ett[] = {
296         &ett_icap,
297     };
298
299     proto_icap = proto_register_protocol(
300             "Internet Content Adaptation Protocol",
301             "ICAP", "icap");
302     proto_register_field_array(proto_icap, hf, array_length(hf));
303     proto_register_subtree_array(ett, array_length(ett));
304
305 }
306
307 void
308 proto_reg_handoff_icap(void)
309 {
310     dissector_handle_t icap_handle;
311
312     http_handle = find_dissector_add_dependency("http", proto_icap);
313
314     icap_handle = register_dissector("icap", dissect_icap, proto_icap);
315     dissector_add_uint_with_preference("tcp.port", TCP_PORT_ICAP, icap_handle);
316
317     /* As ICAPS port is not officially assigned by IANA
318      * (de facto standard is 11344), we default to 0
319      * to have "decode as" available */
320     ssl_dissector_add(0, icap_handle);
321 }
322
323 /*
324  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
325  *
326  * Local variables:
327  * c-basic-offset: 4
328  * tab-width: 8
329  * indent-tabs-mode: nil
330  * End:
331  *
332  * vi: set shiftwidth=4 tabstop=8 expandtab:
333  * :indentSize=4:tabSize=8:noTabs=true:
334  */