various string related changes, mainly replace sprintf/snprintf by g_snprintf
[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  * Copyright 2002, Tim Potter <tpot@samba.org>
7  * Copyright 1999, Andrew Tridgell <tridge@samba.org>
8  *
9  * $Id: packet-http.c,v 1.95 2004/02/21 01:31:56 guy Exp $
10  *
11  * Ethereal - Network traffic analyzer
12  * By Gerald Combs <gerald@ethereal.com>
13  * Copyright 1998 Gerald Combs
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <string.h>
35 #include <ctype.h>
36
37 #include <glib.h>
38 #include <epan/packet.h>
39 #include <epan/strutil.h>
40
41 #include "util.h"
42 #include "req_resp_hdrs.h"
43 #include "packet-http.h"
44 #include "prefs.h"
45
46 typedef enum _http_type {
47         HTTP_REQUEST,
48         HTTP_RESPONSE,
49         HTTP_NOTIFICATION,
50         HTTP_OTHERS
51 } http_type_t;
52
53 #include "tap.h"
54
55 static int http_tap = -1;
56
57 static int proto_http = -1;
58 static int hf_http_notification = -1;
59 static int hf_http_response = -1;
60 static int hf_http_request = -1;
61 static int hf_http_basic = -1;
62 static int hf_http_request_method = -1;
63 static int hf_http_response_code = -1;
64 static int hf_http_authorization = -1;
65 static int hf_http_proxy_authenticate = -1;
66 static int hf_http_proxy_authorization = -1;
67 static int hf_http_www_authenticate = -1;
68 static int hf_http_content_type = -1;
69 static int hf_http_content_length = -1;
70 static int hf_http_content_encoding = -1;
71 static int hf_http_transfer_encoding = -1;
72
73 static gint ett_http = -1;
74 static gint ett_http_ntlmssp = -1;
75 static gint ett_http_request = -1;
76
77 static dissector_handle_t data_handle;
78 static dissector_handle_t http_handle;
79
80 /*
81  * desegmentation of HTTP headers
82  * (when we are over TCP or another protocol providing the desegmentation API)
83  */
84 static gboolean http_desegment_headers = FALSE;
85
86 /*
87  * desegmentation of HTTP bodies
88  * (when we are over TCP or another protocol providing the desegmentation API)
89  * TODO let the user filter on content-type the bodies he wants desegmented
90  */
91 static gboolean http_desegment_body = FALSE;
92
93 #define TCP_PORT_HTTP                   80
94 #define TCP_PORT_PROXY_HTTP             3128
95 #define TCP_PORT_PROXY_ADMIN_HTTP       3132
96 #define TCP_ALT_PORT_HTTP               8080
97
98 /*
99  * SSDP is implemented atop HTTP (yes, it really *does* run over UDP).
100  */
101 #define TCP_PORT_SSDP                   1900
102 #define UDP_PORT_SSDP                   1900
103
104 /*
105  * Protocols implemented atop HTTP.
106  */
107 typedef enum {
108         PROTO_HTTP,             /* just HTTP */
109         PROTO_SSDP              /* Simple Service Discovery Protocol */
110 } http_proto_t;
111
112 typedef void (*RequestDissector)(tvbuff_t*, proto_tree*, int);
113
114 /*
115  * Structure holding information from headers needed by main
116  * HTTP dissector code.
117  */
118 typedef struct {
119         char    *content_type;
120         char    *content_type_parameters;
121         long    content_length; /* XXX - make it 64-bit? */
122         char    *content_encoding;
123         char    *transfer_encoding;
124 } headers_t;
125
126 static int is_http_request_or_reply(const gchar *data, int linelen, http_type_t *type,
127                 RequestDissector *req_dissector, int *req_strlen);
128 static void process_header(tvbuff_t *tvb, int offset, int next_offset,
129     const guchar *line, int linelen, int colon_offset, packet_info *pinfo,
130     proto_tree *tree, headers_t *eh_ptr);
131 static gint find_header_hf_value(tvbuff_t *tvb, int offset, guint header_len);
132 static gboolean check_auth_ntlmssp(proto_item *hdr_item, tvbuff_t *tvb,
133     packet_info *pinfo, gchar *value);
134 static gboolean check_auth_basic(proto_item *hdr_item, tvbuff_t *tvb,
135     gchar *value);
136
137 static dissector_table_t port_subdissector_table;
138 static dissector_table_t media_type_subdissector_table;
139 static heur_dissector_list_t heur_subdissector_list;
140
141 static dissector_handle_t ntlmssp_handle=NULL;
142
143
144 /* Return a tvb that contains the binary representation of a base64
145    string */
146
147 static tvbuff_t *
148 base64_to_tvb(const char *base64)
149 {
150         tvbuff_t *tvb;
151         char *data = g_strdup(base64);
152         size_t len;
153
154         len = base64_decode(data);
155         tvb = tvb_new_real_data((const guint8 *)data, len, len);
156
157         tvb_set_free_cb(tvb, g_free);
158
159         return tvb;
160 }
161
162 static void
163 dissect_http_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
164     const char *line)
165 {
166         tvbuff_t *ntlmssp_tvb;
167
168         ntlmssp_tvb = base64_to_tvb(line);
169         tvb_set_child_real_data_tvbuff(tvb, ntlmssp_tvb);
170         add_new_data_source(pinfo, ntlmssp_tvb, "NTLMSSP Data");
171
172         call_dissector(ntlmssp_handle, ntlmssp_tvb, pinfo, tree);
173 }
174
175 static void
176 cleanup_headers(void *arg)
177 {
178         headers_t *headers = arg;
179
180         if (headers->content_type != NULL)
181                 g_free(headers->content_type);
182         /*
183          * The content_type_parameters field actually points into the
184          * content_type headers, so don't free it, as that'll double-free
185          * some memory.
186          */
187         if (headers->content_encoding != NULL)
188                 g_free(headers->content_encoding);
189         if (headers->transfer_encoding != NULL)
190                 g_free(headers->transfer_encoding);
191 }
192
193 /*
194  * TODO: remove this ugly global variable.
195  *
196  * XXX - we leak "http_info_value_t" structures.
197  * XXX - this gets overwritten if there's more than one HTTP request or
198  * reply in the tvbuff.
199  */
200 static http_info_value_t        *stat_info;
201
202 static int
203 dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
204     proto_tree *tree)
205 {
206         http_proto_t    proto;
207         char            *proto_tag;
208         proto_tree      *http_tree = NULL;
209         proto_item      *ti = NULL;
210         const guchar    *line;
211         gint            next_offset;
212         const guchar    *linep, *lineend;
213         int             orig_offset;
214         int             first_linelen, linelen;
215         gboolean        is_request_or_reply;
216         gboolean        saw_req_resp_or_header;
217         guchar          c;
218         http_type_t     http_type;
219         proto_item      *hdr_item;
220         RequestDissector req_dissector;
221         int             req_strlen;
222         proto_tree      *req_tree;
223         int             colon_offset;
224         headers_t       headers;
225         int             datalen;
226         int             reported_datalen;
227         dissector_handle_t handle;
228         gboolean        dissected;
229
230         /*
231          * Is this a request or response?
232          *
233          * Note that "tvb_find_line_end()" will return a value that
234          * is not longer than what's in the buffer, so the
235          * "tvb_get_ptr()" call won't throw an exception.
236          */
237         first_linelen = tvb_find_line_end(tvb, offset,
238             tvb_ensure_length_remaining(tvb, offset), &next_offset,
239             FALSE);
240         /*
241          * Is the first line a request or response?
242          */
243         line = tvb_get_ptr(tvb, offset, first_linelen);
244         http_type = HTTP_OTHERS;        /* type not known yet */
245         is_request_or_reply = is_http_request_or_reply((const gchar *)line,
246                         first_linelen, &http_type, NULL, NULL);
247         if (is_request_or_reply) {
248                 /*
249                  * Yes, it's a request or response.
250                  * Do header desegmentation if we've been told to,
251                  * and do body desegmentation if we've been told to and
252                  * we find a Content-Length header.
253                  */
254                 if (!req_resp_hdrs_do_reassembly(tvb, pinfo,
255                     http_desegment_headers, http_desegment_body)) {
256                         /*
257                          * More data needed for desegmentation.
258                          */
259                         return -1;
260                 }
261         }
262
263         stat_info = g_malloc(sizeof(http_info_value_t));
264         stat_info->response_code = 0;
265         stat_info->request_method = NULL;
266
267         switch (pinfo->match_port) {
268
269         case TCP_PORT_SSDP:     /* TCP_PORT_SSDP = UDP_PORT_SSDP */
270                 proto = PROTO_SSDP;
271                 proto_tag = "SSDP";
272                 break;
273
274         default:
275                 proto = PROTO_HTTP;
276                 proto_tag = "HTTP";
277                 break;
278         }
279
280         if (check_col(pinfo->cinfo, COL_PROTOCOL))
281                 col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag);
282         if (check_col(pinfo->cinfo, COL_INFO)) {
283                 /*
284                  * Put the first line from the buffer into the summary
285                  * if it's an HTTP request or reply (but leave out the
286                  * line terminator).
287                  * Otherwise, just call it a continuation.
288                  *
289                  * Note that "tvb_find_line_end()" will return a value that
290                  * is not longer than what's in the buffer, so the
291                  * "tvb_get_ptr()" call won't throw an exception.
292                  */
293                 line = tvb_get_ptr(tvb, offset, first_linelen);
294                 if (is_request_or_reply)
295                         col_add_str(pinfo->cinfo, COL_INFO,
296                             format_text(line, first_linelen));
297                 else
298                         col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
299         }
300
301         orig_offset = offset;
302         if (tree) {
303                 ti = proto_tree_add_item(tree, proto_http, tvb, offset, -1,
304                     FALSE);
305                 http_tree = proto_item_add_subtree(ti, ett_http);
306         }
307
308         /*
309          * Process the packet data, a line at a time.
310          */
311         http_type = HTTP_OTHERS;        /* type not known yet */
312         headers.content_type = NULL;    /* content type not known yet */
313         headers.content_type_parameters = NULL; /* content type parameters too */
314         headers.content_length = -1;    /* content length not known yet */
315         headers.content_encoding = NULL; /* content encoding not known yet */
316         headers.transfer_encoding = NULL; /* transfer encoding not known yet */
317         saw_req_resp_or_header = FALSE; /* haven't seen anything yet */
318         CLEANUP_PUSH(cleanup_headers, &headers);
319         while (tvb_reported_length_remaining(tvb, offset) != 0) {
320                 /*
321                  * Find the end of the line.
322                  */
323                 linelen = tvb_find_line_end(tvb, offset,
324                     tvb_ensure_length_remaining(tvb, offset), &next_offset,
325                     FALSE);
326                 if (linelen < 0)
327                         return -1;
328
329                 /*
330                  * Get a buffer that refers to the line.
331                  */
332                 line = tvb_get_ptr(tvb, offset, linelen);
333                 lineend = line + linelen;
334                 colon_offset = -1;
335
336                 /*
337                  * OK, does it look like an HTTP request or response?
338                  */
339                 req_dissector = NULL;
340                 is_request_or_reply = is_http_request_or_reply((const gchar *)line,
341                                 linelen, &http_type, &req_dissector, &req_strlen);
342                 if (is_request_or_reply)
343                         goto is_http;
344
345                 /*
346                  * No.  Does it look like a blank line (as would appear
347                  * at the end of an HTTP request)?
348                  */
349                 if (linelen == 0)
350                         goto is_http;   /* Yes. */
351
352                 /*
353                  * No.  Does it look like a header?
354                  */
355                 linep = line;
356                 colon_offset = offset;
357                 while (linep < lineend) {
358                         c = *linep++;
359
360                         /*
361                          * This must be a CHAR to be part of a token; that
362                          * means it must be ASCII.
363                          */
364                         if (!isascii(c))
365                                 break;  /* not ASCII, thus not a CHAR */
366
367                         /*
368                          * This mustn't be a CTL to be part of a token;
369                          * that means it must be printable.
370                          *
371                          * XXX - what about leading LWS on continuation
372                          * lines of a header?
373                          */
374                         if (!isprint(c))
375                                 break;  /* not printable, not a header */
376
377                         /*
378                          * This mustn't be a SEP to be part of a token;
379                          * a ':' ends the token, everything else is an
380                          * indication that this isn't a header.
381                          */
382                         switch (c) {
383
384                         case '(':
385                         case ')':
386                         case '<':
387                         case '>':
388                         case '@':
389                         case ',':
390                         case ';':
391                         case '\\':
392                         case '"':
393                         case '/':
394                         case '[':
395                         case ']':
396                         case '?':
397                         case '=':
398                         case '{':
399                         case '}':
400                         case ' ':
401                                 /*
402                                  * It's a separator, so it's not part of a
403                                  * token, so it's not a field name for the
404                                  * beginning of a header.
405                                  *
406                                  * (We don't have to check for HT; that's
407                                  * already been ruled out by "isprint()".)
408                                  */
409                                 goto not_http;
410
411                         case ':':
412                                 /*
413                                  * This ends the token; we consider this
414                                  * to be a header.
415                                  */
416                                 goto is_http;
417
418                         default:
419                                 colon_offset++;
420                                 break;
421                         }
422                 }
423
424                 /*
425                  * We haven't seen the colon, but everything else looks
426                  * OK for a header line.
427                  *
428                  * If we've already seen an HTTP request or response
429                  * line, or a header line, and we're at the end of
430                  * the tvbuff, we assume this is an incomplete header
431                  * line.  (We quit this loop after seeing a blank line,
432                  * so if we've seen a request or response line, or a
433                  * header line, this is probably more of the request
434                  * or response we're presumably seeing.  There is some
435                  * risk of false positives, but the same applies for
436                  * full request or response lines or header lines,
437                  * although that's less likely.)
438                  *
439                  * We throw an exception in that case, by checking for
440                  * the existence of the next byte after the last one
441                  * in the line.  If it exists, "tvb_ensure_bytes_exist()"
442                  * throws no exception, and we fall through to the
443                  * "not HTTP" case.  If it doesn't exist,
444                  * "tvb_ensure_bytes_exist()" will throw the appropriate
445                  * exception.
446                  */
447                 if (saw_req_resp_or_header)
448                         tvb_ensure_bytes_exist(tvb, offset, linelen + 1);
449
450         not_http:
451                 /*
452                  * We don't consider this part of an HTTP request or
453                  * reply, so we don't display it.
454                  * (Yeah, that means we don't display, say, a text/http
455                  * page, but you can get that from the data pane.)
456                  */
457                 break;
458
459         is_http:
460                 /*
461                  * Process this line.
462                  */
463                 if (linelen == 0) {
464                         /*
465                          * This is a blank line, which means that
466                          * whatever follows it isn't part of this
467                          * request or reply.
468                          */
469                         proto_tree_add_text(http_tree, tvb, offset,
470                             next_offset - offset, "%s",
471                             tvb_format_text(tvb, offset, next_offset - offset));
472                         offset = next_offset;
473                         break;
474                 }
475
476                 /*
477                  * Not a blank line - either a request, a reply, or a header
478                  * line.
479                  */ 
480                 saw_req_resp_or_header = TRUE;
481                 if (is_request_or_reply) {
482                         if (tree) {
483                                 hdr_item = proto_tree_add_text(http_tree, tvb,
484                                     offset, next_offset - offset, "%s",
485                                     tvb_format_text(tvb, offset,
486                                       next_offset - offset));
487                                 if (req_dissector) {
488                                         req_tree = proto_item_add_subtree(
489                                             hdr_item, ett_http_request);
490                                         req_dissector(tvb, req_tree,
491                                             req_strlen);
492                                 }
493                         }
494                 } else {
495                         /*
496                          * Header.
497                          */
498                         process_header(tvb, offset, next_offset, line, linelen,
499                             colon_offset, pinfo, http_tree, &headers);
500                 }
501                 offset = next_offset;
502         }
503
504         if (tree) {
505                 switch (http_type) {
506
507                 case HTTP_NOTIFICATION:
508                         proto_tree_add_boolean_hidden(http_tree,
509                             hf_http_notification, tvb, 0, 0, 1);
510                         break;
511
512                 case HTTP_RESPONSE:
513                         proto_tree_add_boolean_hidden(http_tree,
514                             hf_http_response, tvb, 0, 0, 1);
515                         break;
516
517                 case HTTP_REQUEST:
518                         proto_tree_add_boolean_hidden(http_tree,
519                             hf_http_request, tvb, 0, 0, 1);
520                         break;
521
522                 case HTTP_OTHERS:
523                 default:
524                         break;
525                 }
526         }
527
528         /*
529          * If a content length was supplied, the amount of data to be
530          * processed as HTTP payload is the minimum of the content
531          * length and the amount of data remaining in the frame.
532          *
533          * If no content length was supplied (or if a bad content length
534          * was supplied), the amount of data to be processed is the amount
535          * of data remaining in the frame.
536          *
537          * If there was no Content-Length entity header, we should
538          * accumulate all data until the end of the connection.
539          * That'd require that the TCP dissector call subdissectors
540          * for all frames with FIN, even if they contain no data,
541          * which would require subdissectors to deal intelligently
542          * with empty segments.
543          *
544          * Acccording to RFC 2616, however, 1xx responses, 204 responses,
545          * and 304 responses MUST NOT include a message body; if no
546          * content length is specified for them, we don't attempt to
547          * dissect the body.
548          *
549          * XXX - it says the same about responses to HEAD requests;
550          * unless there's a way to determine from the response
551          * whether it's a response to a HEAD request, we have to
552          * keep information about the request and associate that with
553          * the response in order to handle that.
554          */
555         datalen = tvb_length_remaining(tvb, offset);
556         if (headers.content_length != -1) {
557                 if (datalen > headers.content_length)
558                         datalen = headers.content_length;
559
560                 /*
561                  * XXX - limit the reported length in the tvbuff we'll
562                  * hand to a subdissector to be no greater than the
563                  * content length.
564                  *
565                  * We really need both unreassembled and "how long it'd
566                  * be if it were reassembled" lengths for tvbuffs, so
567                  * that we throw the appropriate exceptions for
568                  * "not enough data captured" (running past the length),
569                  * "packet needed reassembly" (within the length but
570                  * running past the unreassembled length), and
571                  * "packet is malformed" (running past the reassembled
572                  * length).
573                  */
574                 reported_datalen = tvb_reported_length_remaining(tvb, offset);
575                 if (reported_datalen > headers.content_length)
576                         reported_datalen = headers.content_length;
577         } else {
578                 if ((stat_info->response_code/100) == 1 ||
579                     stat_info->response_code == 204 ||
580                     stat_info->response_code == 304)
581                         datalen = 0;    /* no content! */
582                 else
583                         reported_datalen = -1;
584         }
585
586         if (datalen > 0) {
587                 /*
588                  * There's stuff left over; process it.
589                  */
590                 tvbuff_t *next_tvb;
591                 void *save_private_data = NULL;
592
593                 /*
594                  * Create a tvbuff for the payload.
595                  *
596                  * The amount of data to be processed that's
597                  * available in the tvbuff is "datalen", which
598                  * is the minimum of the amount of data left in
599                  * the tvbuff and any specified content length.
600                  *
601                  * The amount of data to be processed that's in
602                  * this frame, regardless of whether it was
603                  * captured or not, is "reported_datalen",
604                  * which, if no content length was specified,
605                  * is -1, i.e. "to the end of the frame.
606                  */
607                 next_tvb = tvb_new_subset(tvb, offset, datalen,
608                     reported_datalen);
609
610                 /*
611                  * Handle content encodings other than "identity" (which
612                  * shouldn't appear in a Content-Encoding header, but
613                  * we handle it in any case).
614                  */
615                 if (headers.content_encoding != NULL &&
616                     strcasecmp(headers.content_encoding, "identity") != 0) {
617                         /*
618                          * We currently can't handle, for example, "gzip",
619                          * "compress", or "deflate"; just handle them as
620                          * data for now.
621                          */
622                         call_dissector(data_handle, next_tvb, pinfo,
623                             http_tree);
624                         goto body_dissected;
625                 }
626
627                 /*
628                  * Handle transfer encodings other than "identity".
629                  */
630                 if (headers.transfer_encoding != NULL &&
631                     strcasecmp(headers.transfer_encoding, "identity") != 0) {
632                         /*
633                          * We currently can't handle, for example, "chunked",
634                          * "gzip", "compress", or "deflate"; just handle them
635                          * as data for now.
636                          */
637                         call_dissector(data_handle, next_tvb, pinfo,
638                             http_tree);
639                         goto body_dissected;
640                 }
641
642                 /*
643                  * Do subdissector checks.
644                  *
645                  * First, check whether some subdissector asked that they
646                  * be called if something was on some particular port.
647                  */
648                 handle = dissector_get_port_handle(port_subdissector_table,
649                     pinfo->match_port);
650                 if (handle == NULL && headers.content_type != NULL) {
651                         /*
652                          * We didn't find any subdissector that
653                          * registered for the port, and we have a
654                          * Content-Type value.  Is there any subdissector
655                          * for that content type?
656                          */
657                         save_private_data = pinfo->private_data;
658                         if (headers.content_type_parameters)
659                                 pinfo->private_data = g_strdup(headers.content_type_parameters);
660                         else
661                                 pinfo->private_data = NULL;
662                         /*
663                          * Calling the string handle for the media type
664                          * dissector table will set pinfo->match_string
665                          * to headers.content_type for us.
666                          */
667                         pinfo->match_string = headers.content_type;
668                         handle = dissector_get_string_handle(
669                             media_type_subdissector_table,
670                             headers.content_type);
671                 }
672                 if (handle != NULL) {
673                         /*
674                          * We have a subdissector - call it.
675                          */
676                         dissected = call_dissector(handle, next_tvb, pinfo,
677                             tree);
678                 } else {
679                         /*
680                          * We don't have a subdissector - try the heuristic
681                          * subdissectors.
682                          */
683                         dissected = dissector_try_heuristic(
684                             heur_subdissector_list, next_tvb, pinfo, tree);
685                 }
686                 if (dissected) {
687                         /*
688                          * The subdissector dissected the body.
689                          * Fix up the top-level item so that it doesn't
690                          * include the stuff for that protocol.
691                          */
692                         if (ti != NULL)
693                                 proto_item_set_len(ti, offset);
694                 } else {
695                         call_dissector(data_handle, next_tvb, pinfo,
696                             http_tree);
697                 }
698
699         body_dissected:
700                 /*
701                  * Do *not* attempt at freeing the private data;
702                  * it may be in use by subdissectors.
703                  */
704                 if (save_private_data)
705                         pinfo->private_data = save_private_data;
706                 /*
707                  * We've processed "datalen" bytes worth of data
708                  * (which may be no data at all); advance the
709                  * offset past whatever data we've processed.
710                  */
711                 offset += datalen;
712         }
713
714         /*
715          * Clean up any header stuff, by calling and popping the cleanup
716          * handler.
717          */
718         CLEANUP_CALL_AND_POP;
719
720         tap_queue_packet(http_tap, pinfo, stat_info);
721
722         return offset - orig_offset;
723 }
724
725 /* This can be used to dissect an HTTP request until such time
726  * that a more complete dissector is written for that HTTP request.
727  * This simple dissectory only puts http.request_method into a sub-tree.
728  */
729 static void
730 basic_request_dissector(tvbuff_t *tvb, proto_tree *tree, int req_strlen)
731 {
732         proto_tree_add_item(tree, hf_http_request_method, tvb, 0, req_strlen, FALSE);
733 }
734
735 static void
736 basic_response_dissector(tvbuff_t *tvb, proto_tree *tree, int req_strlen _U_)
737 {
738         const guchar *data;
739         int minor, major, status_code;
740
741         data = tvb_get_ptr(tvb, 5, 12);
742         if (sscanf((const gchar *)data, "%d.%d %d", &minor, &major, &status_code) == 3) {
743                 proto_tree_add_uint(tree, hf_http_response_code, tvb, 9, 3, status_code);
744                 stat_info->response_code = status_code;
745         }
746 }
747
748 /*
749  * XXX - this won't handle HTTP 0.9 replies, but they're all data
750  * anyway.
751  */
752 static int
753 is_http_request_or_reply(const gchar *data, int linelen, http_type_t *type,
754                 RequestDissector *req_dissector, int *req_strlen)
755 {
756         int isHttpRequestOrReply = FALSE;
757         int prefix_len = 0;
758
759         /*
760          * From RFC 2774 - An HTTP Extension Framework
761          *
762          * Support the command prefix that identifies the presence of
763          * a "mandatory" header.
764          */
765         if (linelen >= 2 && strncmp(data, "M-", 2) == 0) {
766                 data += 2;
767                 linelen -= 2;
768                 prefix_len = 2;
769         }
770
771         /*
772          * From draft-cohen-gena-client-01.txt, available from the uPnP forum:
773          *      NOTIFY, SUBSCRIBE, UNSUBSCRIBE
774          *
775          * From draft-ietf-dasl-protocol-00.txt, a now vanished Microsoft draft:
776          *      SEARCH
777          */
778         if (linelen >= 5 && strncmp(data, "HTTP/", 5) == 0) {
779                 *type = HTTP_RESPONSE;
780                 isHttpRequestOrReply = TRUE;    /* response */
781                 if (req_dissector) {
782                         *req_dissector = basic_response_dissector;
783                         *req_strlen = linelen - 5;
784                 }
785         } else {
786                 const guchar * ptr = (const guchar *)data;
787                 int              index = 0;
788
789                 /* Look for the space following the Method */
790                 while (index < linelen) {
791                         if (*ptr == ' ')
792                                 break;
793                         else {
794                                 ptr++;
795                                 index++;
796                         }
797                 }
798
799                 /* Check the methods that have same length */
800                 switch (index) {
801
802                 case 3:
803                         if (strncmp(data, "GET", index) == 0 ||
804                             strncmp(data, "PUT", index) == 0) {
805                                 *type = HTTP_REQUEST;
806                                 isHttpRequestOrReply = TRUE;
807                         }
808                         else if (strncmp(data, "ICY", index) == 0) {
809                                 *type = HTTP_RESPONSE;
810                                 isHttpRequestOrReply = TRUE;
811                         }
812                         break;
813
814                 case 4:
815                         if (strncmp(data, "COPY", index) == 0 ||
816                             strncmp(data, "HEAD", index) == 0 ||
817                             strncmp(data, "LOCK", index) == 0 ||
818                             strncmp(data, "MOVE", index) == 0 ||
819                             strncmp(data, "POLL", index) == 0 ||
820                             strncmp(data, "POST", index) == 0) {
821                                 *type = HTTP_REQUEST;
822                                 isHttpRequestOrReply = TRUE;
823                         }
824                         break;
825
826                 case 5:
827                         if (strncmp(data, "BCOPY", index) == 0 ||
828                                 strncmp(data, "BMOVE", index) == 0 ||
829                                 strncmp(data, "MKCOL", index) == 0 ||
830                                 strncmp(data, "TRACE", index) == 0) {
831                                 *type = HTTP_REQUEST;
832                                 isHttpRequestOrReply = TRUE;
833                         }
834                         break;
835
836                 case 6:
837                         if (strncmp(data, "DELETE", index) == 0 ||
838                                 strncmp(data, "SEARCH", index) == 0 ||
839                                 strncmp(data, "UNLOCK", index) == 0) {
840                                 *type = HTTP_REQUEST;
841                                 isHttpRequestOrReply = TRUE;
842                         }
843                         else if (strncmp(data, "NOTIFY", index) == 0) {
844                                 *type = HTTP_NOTIFICATION;
845                                 isHttpRequestOrReply = TRUE;
846                         }
847                         break;
848
849                 case 7:
850                         if (strncmp(data, "BDELETE", index) == 0 ||
851                             strncmp(data, "CONNECT", index) == 0 ||
852                             strncmp(data, "OPTIONS", index) == 0) {
853                                 *type = HTTP_REQUEST;
854                                 isHttpRequestOrReply = TRUE;
855                         }
856                         break;
857
858                 case 8:
859                         if (strncmp(data, "PROPFIND", index) == 0) {
860                                 *type = HTTP_REQUEST;
861                                 isHttpRequestOrReply = TRUE;
862                         }
863                         break;
864
865                 case 9:
866                         if (strncmp(data, "SUBSCRIBE", index) == 0) {
867                                 *type = HTTP_NOTIFICATION;
868                                 isHttpRequestOrReply = TRUE;
869                         } else if (strncmp(data, "PROPPATCH", index) == 0 ||
870                             strncmp(data, "BPROPFIND", index) == 0) {
871                                 *type = HTTP_REQUEST;
872                                 isHttpRequestOrReply = TRUE;
873                         }
874                         break;
875
876                 case 10:
877                         if (strncmp(data, "BPROPPATCH", index) == 0) {
878                                 *type = HTTP_REQUEST;
879                                 isHttpRequestOrReply = TRUE;
880                         }
881                         break;
882
883                 case 11:
884                         if (strncmp(data, "UNSUBSCRIBE", index) == 0) {
885                                 *type = HTTP_NOTIFICATION;
886                                 isHttpRequestOrReply = TRUE;
887                         }
888                         break;
889
890                 default:
891                         break;
892                 }
893
894                 if (isHttpRequestOrReply && req_dissector) {
895                         *req_dissector = basic_request_dissector;
896                         *req_strlen = index + prefix_len;
897                 }
898                 if (isHttpRequestOrReply && req_dissector) {
899                         if (!stat_info->request_method)
900                                 stat_info->request_method = g_malloc( index+1 );
901                                 strncpy( stat_info->request_method, data, index);
902                                 stat_info->request_method[index] = '\0';
903                 }
904         }
905
906         return isHttpRequestOrReply;
907 }
908
909 /*
910  * Process headers.
911  */
912 typedef struct {
913         char    *name;
914         gint    *hf;
915         int     special;
916 } header_info;
917
918 #define HDR_NO_SPECIAL          0
919 #define HDR_AUTHORIZATION       1
920 #define HDR_AUTHENTICATE        2
921 #define HDR_CONTENT_TYPE        3
922 #define HDR_CONTENT_LENGTH      4
923 #define HDR_CONTENT_ENCODING    5
924 #define HDR_TRANSFER_ENCODING   6
925
926 static const header_info headers[] = {
927         { "Authorization", &hf_http_authorization, HDR_AUTHORIZATION },
928         { "Proxy-Authorization", &hf_http_proxy_authorization, HDR_AUTHORIZATION },
929         { "Proxy-Authenticate", &hf_http_proxy_authenticate, HDR_AUTHENTICATE },
930         { "WWW-Authenticate", &hf_http_www_authenticate, HDR_AUTHENTICATE },
931         { "Content-Type", &hf_http_content_type, HDR_CONTENT_TYPE },
932         { "Content-Length", &hf_http_content_length, HDR_CONTENT_LENGTH },
933         { "Content-Encoding", &hf_http_content_encoding, HDR_CONTENT_ENCODING },
934         { "Transfer-Encoding", &hf_http_transfer_encoding, HDR_TRANSFER_ENCODING },
935 };
936
937 static void
938 process_header(tvbuff_t *tvb, int offset, int next_offset,
939     const guchar *line, int linelen, int colon_offset,
940     packet_info *pinfo, proto_tree *tree, headers_t *eh_ptr)
941 {
942         int len;
943         int line_end_offset;
944         int header_len;
945         gint hf_index;
946         guchar c;
947         int value_offset;
948         int value_len;
949         char *value;
950         char *p;
951         guchar *up;
952         proto_item *hdr_item;
953         int i;
954
955         len = next_offset - offset;
956         line_end_offset = offset + linelen;
957         header_len = colon_offset - offset;
958         hf_index = find_header_hf_value(tvb, offset, header_len);
959
960         if (hf_index == -1) {
961                 /*
962                  * Not a header we know anything about.  Just put it into
963                  * the tree as text.
964                  */
965                 if (tree) {
966                         proto_tree_add_text(tree, tvb, offset, len,
967                             "%s", format_text(line, len));
968                 }
969         } else {
970                 /*
971                  * Skip whitespace after the colon.
972                  */
973                 value_offset = colon_offset + 1;
974                 while (value_offset < line_end_offset
975                     && ((c = line[value_offset - offset]) == ' ' || c == '\t'))
976                         value_offset++;
977
978                 /*
979                  * Fetch the value.
980                  */
981                 value_len = line_end_offset - value_offset;
982                 value = g_malloc(value_len + 1);
983                 memcpy(value, &line[value_offset - offset], value_len);
984                 value[value_len] = '\0';
985                 CLEANUP_PUSH(g_free, value);
986
987                 /*
988                  * Add it to the protocol tree as a particular field,
989                  * but display the line as is.
990                  */
991                 if (tree) {
992                         hdr_item = proto_tree_add_string_format(tree,
993                             *headers[hf_index].hf, tvb, offset, len,
994                             value, "%s", format_text(line, len));
995                 } else
996                         hdr_item = NULL;
997
998                 /*
999                  * Do any special processing that particular headers
1000                  * require.
1001                  */
1002                 switch (headers[hf_index].special) {
1003
1004                 case HDR_AUTHORIZATION:
1005                         if (check_auth_ntlmssp(hdr_item, tvb, pinfo, value))
1006                                 break;  /* dissected NTLMSSP */
1007                         check_auth_basic(hdr_item, tvb, value);
1008                         break;
1009
1010                 case HDR_AUTHENTICATE:
1011                         check_auth_ntlmssp(hdr_item, tvb, pinfo, value);
1012                         break;
1013
1014                 case HDR_CONTENT_TYPE:
1015                         if (eh_ptr->content_type != NULL)
1016                                 g_free(eh_ptr->content_type);
1017                         eh_ptr->content_type = g_malloc(value_len + 1);
1018                         for (i = 0; i < value_len; i++) {
1019                                 c = value[i];
1020                                 if (c == ';' || isspace(c)) {
1021                                         /*
1022                                          * End of subtype - either
1023                                          * white space or a ";"
1024                                          * separating the subtype from
1025                                          * a parameter.
1026                                          */
1027                                         break;
1028                                 }
1029
1030                                 /*
1031                                  * Map the character to lower case;
1032                                  * content types are case-insensitive.
1033                                  */
1034                                 eh_ptr->content_type[i] = tolower(c);
1035                         }
1036                         eh_ptr->content_type[i] = '\0';
1037                         /*
1038                          * Now find the start of the optional parameters;
1039                          * skip the optional white space and the semicolon
1040                          * if this has not been done before.
1041                          */
1042                         i++;
1043                         while (i < value_len) {
1044                                 c = value[i];
1045                                 if (c == ';' || isspace(c))
1046                                         /* Skip till start of parameters */
1047                                         i++;
1048                                 else
1049                                         break;
1050                         }
1051                         if (i < value_len)
1052                                 eh_ptr->content_type_parameters = value + i;
1053                         else
1054                                 eh_ptr->content_type_parameters = NULL;
1055                         break;
1056
1057                 case HDR_CONTENT_LENGTH:
1058                         eh_ptr->content_length = strtol(value, &p, 10);
1059                         up = (guchar *)p;
1060                         if (eh_ptr->content_length < 0 || p == value ||
1061                             (*up != '\0' && !isspace(*up)))
1062                                 eh_ptr->content_length = -1;    /* not valid */
1063                         break;
1064
1065                 case HDR_CONTENT_ENCODING:
1066                         if (eh_ptr->content_encoding != NULL)
1067                                 g_free(eh_ptr->content_encoding);
1068                         eh_ptr->content_encoding = g_malloc(value_len + 1);
1069                         memcpy(eh_ptr->content_encoding, value, value_len);
1070                         eh_ptr->content_encoding[value_len] = '\0';
1071                         break;
1072
1073                 case HDR_TRANSFER_ENCODING:
1074                         if (eh_ptr->transfer_encoding != NULL)
1075                                 g_free(eh_ptr->transfer_encoding);
1076                         eh_ptr->transfer_encoding = g_malloc(value_len + 1);
1077                         memcpy(eh_ptr->transfer_encoding, value, value_len);
1078                         eh_ptr->transfer_encoding[value_len] = '\0';
1079                         break;
1080                 }
1081
1082                 /*
1083                  * Free the value, by calling and popping the cleanup
1084                  * handler for it.
1085                  */
1086                 CLEANUP_CALL_AND_POP;
1087         }
1088 }
1089
1090 /* Returns index of header tag in headers */
1091 static gint
1092 find_header_hf_value(tvbuff_t *tvb, int offset, guint header_len)
1093 {
1094         guint i;
1095
1096         for (i = 0; i < array_length(headers); i++) {
1097                 if (header_len == strlen(headers[i].name) &&
1098                     tvb_strncaseeql(tvb, offset,
1099                                                 headers[i].name, header_len) == 0)
1100                         return i;
1101         }
1102
1103         return -1;
1104 }
1105
1106 /*
1107  * Dissect Microsoft's abomination called NTLMSSP over HTTP.
1108  */
1109 static gboolean
1110 check_auth_ntlmssp(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo,
1111     gchar *value)
1112 {
1113         static const char *ntlm_headers[] = {
1114                 "NTLM ",
1115                 "Negotiate ",
1116                 NULL
1117         };
1118         const char **header;
1119         size_t hdrlen;
1120         proto_tree *hdr_tree;
1121
1122         /*
1123          * Check for NTLM credentials and challenge; those can
1124          * occur with WWW-Authenticate.
1125          */
1126         for (header = &ntlm_headers[0]; *header != NULL; header++) {
1127                 hdrlen = strlen(*header);
1128                 if (strncmp(value, *header, hdrlen) == 0) {
1129                         if (hdr_item != NULL) {
1130                                 hdr_tree = proto_item_add_subtree(hdr_item,
1131                                     ett_http_ntlmssp);
1132                         } else
1133                                 hdr_tree = NULL;
1134                         value += hdrlen;
1135                         dissect_http_ntlmssp(tvb, pinfo, hdr_tree, value);
1136                         return TRUE;
1137                 }
1138         }
1139         return FALSE;
1140 }
1141
1142 /*
1143  * Dissect HTTP Basic authorization.
1144  */
1145 static gboolean
1146 check_auth_basic(proto_item *hdr_item, tvbuff_t *tvb, gchar *value)
1147 {
1148         static const char *basic_headers[] = {
1149                 "Basic ",
1150                 NULL
1151         };
1152         const char **header;
1153         size_t hdrlen;
1154         proto_tree *hdr_tree;
1155         size_t len;
1156
1157         for (header = &basic_headers[0]; *header != NULL; header++) {
1158                 hdrlen = strlen(*header);
1159                 if (strncmp(value, *header, hdrlen) == 0) {
1160                         if (hdr_item != NULL) {
1161                                 hdr_tree = proto_item_add_subtree(hdr_item,
1162                                     ett_http_ntlmssp);
1163                         } else
1164                                 hdr_tree = NULL;
1165                         value += hdrlen;
1166
1167                         len = base64_decode(value);
1168                         value[len] = '\0';
1169                         proto_tree_add_string(hdr_tree, hf_http_basic, tvb,
1170                             0, 0, value);
1171
1172                         return TRUE;
1173                 }
1174         }
1175         return FALSE;
1176 }
1177
1178 static void
1179 dissect_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1180 {
1181         int             offset = 0;
1182         int             len;
1183
1184         while (tvb_reported_length_remaining(tvb, offset) != 0) {
1185                 len = dissect_http_message(tvb, offset, pinfo, tree);
1186                 if (len == -1)
1187                         break;
1188                 offset += len;
1189
1190                 /*
1191                  * OK, we've set the Protocol and Info columns for the
1192                  * first HTTP message; make the columns non-writable,
1193                  * so that we don't change it for subsequent HTTP messages.
1194                  */
1195                 col_set_writable(pinfo->cinfo, FALSE);
1196         }
1197 }
1198
1199 static void
1200 dissect_http_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1201 {
1202         dissect_http_message(tvb, 0, pinfo, tree);
1203 }
1204
1205 void
1206 proto_register_http(void)
1207 {
1208         static hf_register_info hf[] = {
1209             { &hf_http_notification,
1210               { "Notification",         "http.notification",
1211                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1212                 "TRUE if HTTP notification", HFILL }},
1213             { &hf_http_response,
1214               { "Response",             "http.response",
1215                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1216                 "TRUE if HTTP response", HFILL }},
1217             { &hf_http_request,
1218               { "Request",              "http.request",
1219                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1220                 "TRUE if HTTP request", HFILL }},
1221             { &hf_http_basic,
1222               { "Credentials",          "http.authbasic",
1223                 FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }},
1224             { &hf_http_request_method,
1225               { "Request Method",       "http.request.method",
1226                 FT_STRING, BASE_NONE, NULL, 0x0,
1227                 "HTTP Request Method", HFILL }},
1228             { &hf_http_response_code,
1229               { "Response Code",        "http.response.code",
1230                 FT_UINT16, BASE_DEC, NULL, 0x0,
1231                 "HTTP Response Code", HFILL }},
1232             { &hf_http_authorization,
1233               { "Authorization",        "http.authorization",
1234                 FT_STRING, BASE_NONE, NULL, 0x0,
1235                 "HTTP Authorization header", HFILL }},
1236             { &hf_http_proxy_authenticate,
1237               { "Proxy-Authenticate",   "http.proxy_authenticate",
1238                 FT_STRING, BASE_NONE, NULL, 0x0,
1239                 "HTTP Proxy-Authenticate header", HFILL }},
1240             { &hf_http_proxy_authorization,
1241               { "Proxy-Authorization",  "http.proxy_authorization",
1242                 FT_STRING, BASE_NONE, NULL, 0x0,
1243                 "HTTP Proxy-Authorization header", HFILL }},
1244             { &hf_http_www_authenticate,
1245               { "WWW-Authenticate",     "http.www_authenticate",
1246                 FT_STRING, BASE_NONE, NULL, 0x0,
1247                 "HTTP WWW-Authenticate header", HFILL }},
1248             { &hf_http_content_type,
1249               { "Content-Type", "http.content_type",
1250                 FT_STRING, BASE_NONE, NULL, 0x0,
1251                 "HTTP Content-Type header", HFILL }},
1252             { &hf_http_content_length,
1253               { "Content-Length",       "http.content_length",
1254                 FT_STRING, BASE_NONE, NULL, 0x0,
1255                 "HTTP Content-Length header", HFILL }},
1256             { &hf_http_content_encoding,
1257               { "Content-Encoding",     "http.content_encoding",
1258                 FT_STRING, BASE_NONE, NULL, 0x0,
1259                 "HTTP Content-Encoding header", HFILL }},
1260             { &hf_http_transfer_encoding,
1261               { "Transfer-Encoding",    "http.transfer_encoding",
1262                 FT_STRING, BASE_NONE, NULL, 0x0,
1263                 "HTTP Transfer-Encoding header", HFILL }},
1264         };
1265         static gint *ett[] = {
1266                 &ett_http,
1267                 &ett_http_ntlmssp,
1268                 &ett_http_request,
1269         };
1270         module_t *http_module;
1271
1272         proto_http = proto_register_protocol("Hypertext Transfer Protocol",
1273             "HTTP", "http");
1274         proto_register_field_array(proto_http, hf, array_length(hf));
1275         proto_register_subtree_array(ett, array_length(ett));
1276         http_module = prefs_register_protocol(proto_http, NULL);
1277         prefs_register_bool_preference(http_module, "desegment_headers",
1278             "Desegment all HTTP headers spanning multiple TCP segments",
1279             "Whether the HTTP dissector should desegment all headers "
1280             "of a request spanning multiple TCP segments",
1281             &http_desegment_headers);
1282         prefs_register_bool_preference(http_module, "desegment_body",
1283             "Trust the \"Content-length:\" header and desegment HTTP "
1284             "bodies\nspanning multiple TCP segments",
1285             "Whether the HTTP dissector should use the "
1286             "\"Content-length:\" value to desegment the body "
1287             "of a request spanning multiple TCP segments",
1288             &http_desegment_body);
1289
1290         http_handle = create_dissector_handle(dissect_http, proto_http);
1291
1292         /*
1293          * Dissectors shouldn't register themselves in this table;
1294          * instead, they should call "http_dissector_add()", and
1295          * we'll register the port number they specify as a port
1296          * for HTTP, and register them in our subdissector table.
1297          *
1298          * This only works for protocols such as IPP that run over
1299          * HTTP on a specific non-HTTP port.
1300          */
1301         port_subdissector_table = register_dissector_table("http.port",
1302             "TCP port for protocols using HTTP", FT_UINT16, BASE_DEC);
1303
1304         /*
1305          * Dissectors can register themselves in this table.
1306          * It's just "media_type", not "http.content_type", because
1307          * it's an Internet media type, usable by other protocols as well.
1308          */
1309         media_type_subdissector_table =
1310             register_dissector_table("media_type",
1311                 "Internet media type", FT_STRING, BASE_NONE);
1312
1313         /*
1314          * Heuristic dissectors SHOULD register themselves in
1315          * this table using the standard heur_dissector_add()
1316          * function.
1317          */
1318         register_heur_dissector_list("http", &heur_subdissector_list);
1319
1320         /*
1321          * Register for tapping
1322          */
1323         http_tap = register_tap("http");
1324 }
1325
1326 /*
1327  * Called by dissectors for protocols that run atop HTTP/TCP.
1328  */
1329 void
1330 http_dissector_add(guint32 port, dissector_handle_t handle)
1331 {
1332         /*
1333          * Register ourselves as the handler for that port number
1334          * over TCP.
1335          */
1336         dissector_add("tcp.port", port, http_handle);
1337
1338         /*
1339          * And register them in *our* table for that port.
1340          */
1341         dissector_add("http.port", port, handle);
1342 }
1343
1344 void
1345 proto_reg_handoff_http(void)
1346 {
1347         dissector_handle_t http_udp_handle;
1348
1349         data_handle = find_dissector("data");
1350
1351         dissector_add("tcp.port", TCP_PORT_HTTP, http_handle);
1352         dissector_add("tcp.port", TCP_ALT_PORT_HTTP, http_handle);
1353         dissector_add("tcp.port", TCP_PORT_PROXY_HTTP, http_handle);
1354         dissector_add("tcp.port", TCP_PORT_PROXY_ADMIN_HTTP, http_handle);
1355
1356         /*
1357          * XXX - is there anything to dissect in the body of an SSDP
1358          * request or reply?  I.e., should there be an SSDP dissector?
1359          */
1360         dissector_add("tcp.port", TCP_PORT_SSDP, http_handle);
1361         http_udp_handle = create_dissector_handle(dissect_http_udp, proto_http);
1362         dissector_add("udp.port", UDP_PORT_SSDP, http_udp_handle);
1363
1364         ntlmssp_handle = find_dissector("ntlmssp");
1365 }
1366
1367 /*
1368  * Content-Type: message/http
1369  */
1370
1371 static gint proto_message_http = -1;
1372 static gint ett_message_http = -1;
1373 static dissector_handle_t message_http_handle;
1374
1375 static void
1376 dissect_message_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1377 {
1378         proto_tree      *subtree;
1379         proto_item      *ti;
1380         gint            offset = 0, next_offset;
1381         gint            len;
1382
1383         if (check_col(pinfo->cinfo, COL_INFO))
1384                 col_append_str(pinfo->cinfo, COL_INFO, " (message/http)");
1385         if (tree) {
1386                 ti = proto_tree_add_item(tree, proto_message_http,
1387                                 tvb, 0, -1, FALSE);
1388                 subtree = proto_item_add_subtree(ti, ett_message_http);
1389                 while (tvb_reported_length_remaining(tvb, offset) != 0) {
1390                         len = tvb_find_line_end(tvb, offset,
1391                                         tvb_ensure_length_remaining(tvb, offset),
1392                                         &next_offset, FALSE);
1393                         if (len == -1)
1394                                 break;
1395                         proto_tree_add_text(subtree, tvb, offset, next_offset - offset,
1396                                         "%s", tvb_format_text(tvb, offset, len));
1397                         offset = next_offset;
1398                 }
1399         }
1400 }
1401
1402 void
1403 proto_register_message_http(void)
1404 {
1405         static gint *ett[] = {
1406                 &ett_message_http,
1407         };
1408
1409         proto_message_http = proto_register_protocol(
1410                         "Media Type: message/http",
1411                         "message/http",
1412                         "message-http"
1413         );
1414         proto_register_subtree_array(ett, array_length(ett));
1415         message_http_handle = create_dissector_handle(dissect_message_http,
1416                         proto_message_http);
1417 }
1418
1419 void
1420 proto_reg_handoff_message_http(void)
1421 {
1422         message_http_handle = create_dissector_handle(dissect_message_http,
1423                         proto_message_http);
1424
1425         dissector_add_string("media_type", "message/http", message_http_handle);
1426 }