2 * Routines for HTTP packet disassembly
6 * Guy Harris <guy@alum.mit.edu>
8 * Copyright 2004, Jerry Talkington <jtalkington@users.sourceforge.net>
9 * Copyright 2002, Tim Potter <tpot@samba.org>
10 * Copyright 1999, Andrew Tridgell <tridge@samba.org>
14 * Ethereal - Network traffic analyzer
15 * By Gerald Combs <gerald@ethereal.com>
16 * Copyright 1998 Gerald Combs
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
41 #include <epan/packet.h>
42 #include <epan/strutil.h>
43 #include <epan/base64.h>
45 #include <epan/req_resp_hdrs.h>
46 #include "packet-http.h"
47 #include <epan/prefs.h>
49 typedef enum _http_type {
58 static int http_tap = -1;
60 static int proto_http = -1;
61 static int hf_http_notification = -1;
62 static int hf_http_response = -1;
63 static int hf_http_request = -1;
64 static int hf_http_basic = -1;
65 static int hf_http_request_method = -1;
66 static int hf_http_response_code = -1;
67 static int hf_http_authorization = -1;
68 static int hf_http_proxy_authenticate = -1;
69 static int hf_http_proxy_authorization = -1;
70 static int hf_http_www_authenticate = -1;
71 static int hf_http_content_type = -1;
72 static int hf_http_content_length = -1;
73 static int hf_http_content_encoding = -1;
74 static int hf_http_transfer_encoding = -1;
76 static gint ett_http = -1;
77 static gint ett_http_ntlmssp = -1;
78 static gint ett_http_request = -1;
79 static gint ett_http_chunked_response = -1;
80 static gint ett_http_chunk_data = -1;
81 static gint ett_http_encoded_entity = -1;
83 static dissector_handle_t data_handle;
84 static dissector_handle_t media_handle;
85 static dissector_handle_t http_handle;
88 * desegmentation of HTTP headers
89 * (when we are over TCP or another protocol providing the desegmentation API)
91 static gboolean http_desegment_headers = FALSE;
94 * desegmentation of HTTP bodies
95 * (when we are over TCP or another protocol providing the desegmentation API)
96 * TODO let the user filter on content-type the bodies he wants desegmented
98 static gboolean http_desegment_body = FALSE;
101 * De-chunking of content-encoding: chunk entity bodies.
103 static gboolean http_dechunk_body = TRUE;
106 * Decompression of zlib encoded entities.
109 static gboolean http_decompress_body = TRUE;
111 static gboolean http_decompress_body = FALSE;
115 #define TCP_PORT_HTTP 80
116 #define TCP_PORT_PROXY_HTTP 3128
117 #define TCP_PORT_PROXY_ADMIN_HTTP 3132
118 #define TCP_ALT_PORT_HTTP 8080
119 #define TCP_PORT_HKP 11371
120 #define TCP_PORT_DAAP 3689
122 * SSDP is implemented atop HTTP (yes, it really *does* run over UDP).
124 #define TCP_PORT_SSDP 1900
125 #define UDP_PORT_SSDP 1900
128 * Protocols implemented atop HTTP.
131 PROTO_HTTP, /* just HTTP */
132 PROTO_SSDP, /* Simple Service Discovery Protocol */
133 PROTO_DAAP /* Digital Audio Access Protocol */
136 typedef void (*RequestDissector)(tvbuff_t*, proto_tree*, int);
139 * Structure holding information from headers needed by main
140 * HTTP dissector code.
144 char *content_type_parameters;
145 long content_length; /* XXX - make it 64-bit? */
146 char *content_encoding;
147 char *transfer_encoding;
150 static int is_http_request_or_reply(const gchar *data, int linelen, http_type_t *type,
151 RequestDissector *req_dissector, int *req_strlen);
152 static int chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo,
153 proto_tree *tree, int offset);
154 static void process_header(tvbuff_t *tvb, int offset, int next_offset,
155 const guchar *line, int linelen, int colon_offset, packet_info *pinfo,
156 proto_tree *tree, headers_t *eh_ptr);
157 static gint find_header_hf_value(tvbuff_t *tvb, int offset, guint header_len);
158 static gboolean check_auth_ntlmssp(proto_item *hdr_item, tvbuff_t *tvb,
159 packet_info *pinfo, gchar *value);
160 static gboolean check_auth_basic(proto_item *hdr_item, tvbuff_t *tvb,
163 static dissector_table_t port_subdissector_table;
164 static dissector_table_t media_type_subdissector_table;
165 static heur_dissector_list_t heur_subdissector_list;
167 static dissector_handle_t ntlmssp_handle=NULL;
168 static dissector_handle_t gssapi_handle=NULL;
171 /* Return a tvb that contains the binary representation of a base64
175 base64_to_tvb(const char *base64)
178 char *data = g_strdup(base64);
181 len = epan_base64_decode(data);
182 tvb = tvb_new_real_data((const guint8 *)data, len, len);
184 tvb_set_free_cb(tvb, g_free);
190 dissect_http_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
193 tvbuff_t *ntlmssp_tvb;
195 ntlmssp_tvb = base64_to_tvb(line);
196 tvb_set_child_real_data_tvbuff(tvb, ntlmssp_tvb);
197 add_new_data_source(pinfo, ntlmssp_tvb, "NTLMSSP / GSSAPI Data");
198 if (tvb_strneql(ntlmssp_tvb, 0, "NTLMSSP", 7) == 0)
199 call_dissector(ntlmssp_handle, ntlmssp_tvb, pinfo, tree);
201 call_dissector(gssapi_handle, ntlmssp_tvb, pinfo, tree);
205 cleanup_headers(void *arg)
207 headers_t *headers = arg;
209 if (headers->content_type != NULL)
210 g_free(headers->content_type);
211 headers->content_type = NULL;
213 * The content_type_parameters field actually points into the
214 * content_type headers, so don't free it, as that'll double-free
217 headers->content_type_parameters = NULL;
218 if (headers->content_encoding != NULL)
219 g_free(headers->content_encoding);
220 headers->content_encoding = NULL;
221 if (headers->transfer_encoding != NULL)
222 g_free(headers->transfer_encoding);
223 headers->transfer_encoding = NULL;
227 * TODO: remove this ugly global variable.
229 * XXX - we leak "http_info_value_t" structures.
230 * XXX - this gets overwritten if there's more than one HTTP request or
231 * reply in the tvbuff.
233 static http_info_value_t *stat_info;
236 dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
241 proto_tree *http_tree = NULL;
242 proto_item *ti = NULL;
245 const guchar *linep, *lineend;
247 int first_linelen, linelen;
248 gboolean is_request_or_reply;
249 gboolean saw_req_resp_or_header;
251 http_type_t http_type;
252 proto_item *hdr_item;
253 RequestDissector req_dissector;
255 proto_tree *req_tree;
259 int reported_datalen;
260 dissector_handle_t handle;
264 * Is this a request or response?
266 * Note that "tvb_find_line_end()" will return a value that
267 * is not longer than what's in the buffer, so the
268 * "tvb_get_ptr()" call won't throw an exception.
270 first_linelen = tvb_find_line_end(tvb, offset,
271 tvb_ensure_length_remaining(tvb, offset), &next_offset,
274 * Is the first line a request or response?
276 line = tvb_get_ptr(tvb, offset, first_linelen);
277 http_type = HTTP_OTHERS; /* type not known yet */
278 is_request_or_reply = is_http_request_or_reply((const gchar *)line,
279 first_linelen, &http_type, NULL, NULL);
280 if (is_request_or_reply) {
282 * Yes, it's a request or response.
283 * Do header desegmentation if we've been told to,
284 * and do body desegmentation if we've been told to and
285 * we find a Content-Length header.
287 if (!req_resp_hdrs_do_reassembly(tvb, pinfo,
288 http_desegment_headers, http_desegment_body)) {
290 * More data needed for desegmentation.
296 stat_info = g_malloc(sizeof(http_info_value_t));
297 stat_info->response_code = 0;
298 stat_info->request_method = NULL;
300 switch (pinfo->match_port) {
302 case TCP_PORT_SSDP: /* TCP_PORT_SSDP = UDP_PORT_SSDP */
318 if (check_col(pinfo->cinfo, COL_PROTOCOL))
319 col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag);
320 if (check_col(pinfo->cinfo, COL_INFO)) {
322 * Put the first line from the buffer into the summary
323 * if it's an HTTP request or reply (but leave out the
325 * Otherwise, just call it a continuation.
327 * Note that "tvb_find_line_end()" will return a value that
328 * is not longer than what's in the buffer, so the
329 * "tvb_get_ptr()" call won't throw an exception.
331 line = tvb_get_ptr(tvb, offset, first_linelen);
332 if (is_request_or_reply)
333 col_add_str(pinfo->cinfo, COL_INFO,
334 format_text(line, first_linelen));
336 col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
339 orig_offset = offset;
341 ti = proto_tree_add_item(tree, proto_http, tvb, offset, -1,
343 http_tree = proto_item_add_subtree(ti, ett_http);
347 * Process the packet data, a line at a time.
349 http_type = HTTP_OTHERS; /* type not known yet */
350 headers.content_type = NULL; /* content type not known yet */
351 headers.content_type_parameters = NULL; /* content type parameters too */
352 headers.content_length = -1; /* content length not known yet */
353 headers.content_encoding = NULL; /* content encoding not known yet */
354 headers.transfer_encoding = NULL; /* transfer encoding not known yet */
355 saw_req_resp_or_header = FALSE; /* haven't seen anything yet */
356 CLEANUP_PUSH(cleanup_headers, &headers);
357 while (tvb_reported_length_remaining(tvb, offset) != 0) {
359 * Find the end of the line.
361 linelen = tvb_find_line_end(tvb, offset,
362 tvb_ensure_length_remaining(tvb, offset), &next_offset,
368 * Get a buffer that refers to the line.
370 line = tvb_get_ptr(tvb, offset, linelen);
371 lineend = line + linelen;
375 * OK, does it look like an HTTP request or response?
377 req_dissector = NULL;
378 is_request_or_reply = is_http_request_or_reply((const gchar *)line,
379 linelen, &http_type, &req_dissector, &req_strlen);
380 if (is_request_or_reply)
384 * No. Does it look like a blank line (as would appear
385 * at the end of an HTTP request)?
388 goto is_http; /* Yes. */
391 * No. Does it look like a header?
394 colon_offset = offset;
395 while (linep < lineend) {
399 * This must be a CHAR to be part of a token; that
400 * means it must be ASCII.
403 break; /* not ASCII, thus not a CHAR */
406 * This mustn't be a CTL to be part of a token.
408 * XXX - what about leading LWS on continuation
412 break; /* CTL, not part of a header */
415 * This mustn't be a SEP to be part of a token;
416 * a ':' ends the token, everything else is an
417 * indication that this isn't a header.
439 * It's a separator, so it's not part of a
440 * token, so it's not a field name for the
441 * beginning of a header.
443 * (We don't have to check for HT; that's
444 * already been ruled out by "iscntrl()".)
450 * This ends the token; we consider this
462 * We haven't seen the colon, but everything else looks
463 * OK for a header line.
465 * If we've already seen an HTTP request or response
466 * line, or a header line, and we're at the end of
467 * the tvbuff, we assume this is an incomplete header
468 * line. (We quit this loop after seeing a blank line,
469 * so if we've seen a request or response line, or a
470 * header line, this is probably more of the request
471 * or response we're presumably seeing. There is some
472 * risk of false positives, but the same applies for
473 * full request or response lines or header lines,
474 * although that's less likely.)
476 * We throw an exception in that case, by checking for
477 * the existence of the next byte after the last one
478 * in the line. If it exists, "tvb_ensure_bytes_exist()"
479 * throws no exception, and we fall through to the
480 * "not HTTP" case. If it doesn't exist,
481 * "tvb_ensure_bytes_exist()" will throw the appropriate
484 if (saw_req_resp_or_header)
485 tvb_ensure_bytes_exist(tvb, offset, linelen + 1);
489 * We don't consider this part of an HTTP request or
490 * reply, so we don't display it.
491 * (Yeah, that means we don't display, say, a text/http
492 * page, but you can get that from the data pane.)
502 * This is a blank line, which means that
503 * whatever follows it isn't part of this
506 proto_tree_add_text(http_tree, tvb, offset,
507 next_offset - offset, "%s",
508 tvb_format_text(tvb, offset, next_offset - offset));
509 offset = next_offset;
514 * Not a blank line - either a request, a reply, or a header
517 saw_req_resp_or_header = TRUE;
518 if (is_request_or_reply) {
520 hdr_item = proto_tree_add_text(http_tree, tvb,
521 offset, next_offset - offset, "%s",
522 tvb_format_text(tvb, offset,
523 next_offset - offset));
525 req_tree = proto_item_add_subtree(
526 hdr_item, ett_http_request);
527 req_dissector(tvb, req_tree,
535 process_header(tvb, offset, next_offset, line, linelen,
536 colon_offset, pinfo, http_tree, &headers);
538 offset = next_offset;
544 case HTTP_NOTIFICATION:
545 proto_tree_add_boolean_hidden(http_tree,
546 hf_http_notification, tvb, 0, 0, 1);
550 proto_tree_add_boolean_hidden(http_tree,
551 hf_http_response, tvb, 0, 0, 1);
555 proto_tree_add_boolean_hidden(http_tree,
556 hf_http_request, tvb, 0, 0, 1);
566 * If a content length was supplied, the amount of data to be
567 * processed as HTTP payload is the minimum of the content
568 * length and the amount of data remaining in the frame.
570 * If no content length was supplied (or if a bad content length
571 * was supplied), the amount of data to be processed is the amount
572 * of data remaining in the frame.
574 * If there was no Content-Length entity header, we should
575 * accumulate all data until the end of the connection.
576 * That'd require that the TCP dissector call subdissectors
577 * for all frames with FIN, even if they contain no data,
578 * which would require subdissectors to deal intelligently
579 * with empty segments.
581 * Acccording to RFC 2616, however, 1xx responses, 204 responses,
582 * and 304 responses MUST NOT include a message body; if no
583 * content length is specified for them, we don't attempt to
586 * XXX - it says the same about responses to HEAD requests;
587 * unless there's a way to determine from the response
588 * whether it's a response to a HEAD request, we have to
589 * keep information about the request and associate that with
590 * the response in order to handle that.
592 datalen = tvb_length_remaining(tvb, offset);
593 if (headers.content_length != -1) {
594 if (datalen > headers.content_length)
595 datalen = headers.content_length;
598 * XXX - limit the reported length in the tvbuff we'll
599 * hand to a subdissector to be no greater than the
602 * We really need both unreassembled and "how long it'd
603 * be if it were reassembled" lengths for tvbuffs, so
604 * that we throw the appropriate exceptions for
605 * "not enough data captured" (running past the length),
606 * "packet needed reassembly" (within the length but
607 * running past the unreassembled length), and
608 * "packet is malformed" (running past the reassembled
611 reported_datalen = tvb_reported_length_remaining(tvb, offset);
612 if (reported_datalen > headers.content_length)
613 reported_datalen = headers.content_length;
615 if ((stat_info->response_code/100) == 1 ||
616 stat_info->response_code == 204 ||
617 stat_info->response_code == 304)
618 datalen = 0; /* no content! */
620 reported_datalen = -1;
625 * There's stuff left over; process it.
628 void *save_private_data = NULL;
629 gint chunks_decoded = 0;
632 * Create a tvbuff for the payload.
634 * The amount of data to be processed that's
635 * available in the tvbuff is "datalen", which
636 * is the minimum of the amount of data left in
637 * the tvbuff and any specified content length.
639 * The amount of data to be processed that's in
640 * this frame, regardless of whether it was
641 * captured or not, is "reported_datalen",
642 * which, if no content length was specified,
643 * is -1, i.e. "to the end of the frame.
645 next_tvb = tvb_new_subset(tvb, offset, datalen,
648 * BEWARE - next_tvb is a subset of another tvb,
649 * so we MUST NOT attempt tvb_free(next_tvb);
653 * Handle *transfer* encodings other than "identity".
655 if (headers.transfer_encoding != NULL &&
656 strcasecmp(headers.transfer_encoding, "identity") != 0) {
657 if (http_dechunk_body &&
658 (strcasecmp(headers.transfer_encoding, "chunked")
661 chunks_decoded = chunked_encoding_dissector(
662 &next_tvb, pinfo, http_tree, 0);
664 if (chunks_decoded <= 0) {
666 * The chunks weren't reassembled,
667 * or there was a single zero
673 * Add a new data source for the
676 tvb_set_child_real_data_tvbuff(tvb,
678 add_new_data_source(pinfo, next_tvb,
679 "De-chunked entity body");
683 * We currently can't handle, for example,
684 * "gzip", "compress", or "deflate" as
685 * *transfer* encodings; just handle them
688 call_dissector(data_handle, next_tvb, pinfo,
694 * At this point, any chunked *transfer* coding has been removed
695 * (the entity body has been dechunked) so it can be presented
696 * for the following operation (*content* encoding), or it has
697 * been been handed off to the data dissector.
699 * Handle *content* encodings other than "identity" (which
700 * shouldn't appear in a Content-Encoding header, but
701 * we handle it in any case).
703 if (headers.content_encoding != NULL &&
704 strcasecmp(headers.content_encoding, "identity") != 0) {
706 * We currently can't handle, for example, "compress";
707 * just handle them as data for now.
709 * After July 7, 2004 the LZW patent expires, so support
710 * might be added then. However, I don't think that
711 * anybody ever really implemented "compress", due to
712 * the aforementioned patent.
714 tvbuff_t *uncomp_tvb = NULL;
715 proto_item *e_ti = NULL;
716 proto_tree *e_tree = NULL;
718 if (http_decompress_body &&
719 (strcasecmp(headers.content_encoding, "gzip") == 0 ||
720 strcasecmp(headers.content_encoding, "deflate")
723 uncomp_tvb = tvb_uncompress(next_tvb, 0,
724 tvb_length(next_tvb));
728 * Add the encoded entity to the protocol tree
730 e_ti = proto_tree_add_text(http_tree, next_tvb,
731 0, tvb_length(next_tvb),
732 "Content-encoded entity body (%s)",
733 headers.content_encoding);
734 e_tree = proto_item_add_subtree(e_ti,
735 ett_http_encoded_entity);
737 if (uncomp_tvb != NULL) {
739 * Decompression worked
742 /* XXX - Don't free this, since it's possible
743 * that the data was only partially
744 * decompressed, such as when desegmentation
749 next_tvb = uncomp_tvb;
750 tvb_set_child_real_data_tvbuff(tvb, next_tvb);
751 add_new_data_source(pinfo, next_tvb,
752 "Uncompressed entity body");
754 if (chunks_decoded > 1) {
755 tvb_set_child_real_data_tvbuff(tvb,
757 add_new_data_source(pinfo, next_tvb,
758 "Compressed entity body");
760 call_dissector(data_handle, next_tvb, pinfo,
767 * Note that a new data source is added for the entity body
768 * only if it was content-encoded and/or transfer-encoded.
772 * Do subdissector checks.
774 * First, check whether some subdissector asked that they
775 * be called if something was on some particular port.
777 handle = dissector_get_port_handle(port_subdissector_table,
779 if (handle == NULL && headers.content_type != NULL) {
781 * We didn't find any subdissector that
782 * registered for the port, and we have a
783 * Content-Type value. Is there any subdissector
784 * for that content type?
786 save_private_data = pinfo->private_data;
788 * XXX - this won't get freed if the subdissector
789 * throws an exception. Do we really need to
792 if (headers.content_type_parameters)
793 pinfo->private_data = g_strdup(headers.content_type_parameters);
795 pinfo->private_data = NULL;
797 * Calling the string handle for the media type
798 * dissector table will set pinfo->match_string
799 * to headers.content_type for us.
801 pinfo->match_string = headers.content_type;
802 handle = dissector_get_string_handle(
803 media_type_subdissector_table,
804 headers.content_type);
806 * Calling the default media handle otherwise
808 if (handle == NULL) {
809 handle = media_handle;
812 if (handle != NULL) {
814 * We have a subdissector - call it.
816 dissected = call_dissector(handle, next_tvb, pinfo,
820 * We don't have a subdissector - try the heuristic
823 dissected = dissector_try_heuristic(
824 heur_subdissector_list, next_tvb, pinfo, tree);
828 * The subdissector dissected the body.
829 * Fix up the top-level item so that it doesn't
830 * include the stuff for that protocol.
833 proto_item_set_len(ti, offset);
835 call_dissector(data_handle, next_tvb, pinfo,
841 * Do *not* attempt at freeing the private data;
842 * it may be in use by subdissectors.
844 if (save_private_data)
845 pinfo->private_data = save_private_data;
847 * We've processed "datalen" bytes worth of data
848 * (which may be no data at all); advance the
849 * offset past whatever data we've processed.
855 * Clean up any header stuff, by calling and popping the cleanup
858 CLEANUP_CALL_AND_POP;
860 tap_queue_packet(http_tap, pinfo, stat_info);
862 return offset - orig_offset;
865 /* This can be used to dissect an HTTP request until such time
866 * that a more complete dissector is written for that HTTP request.
867 * This simple dissectory only puts http.request_method into a sub-tree.
870 basic_request_dissector(tvbuff_t *tvb, proto_tree *tree, int req_strlen)
872 proto_tree_add_item(tree, hf_http_request_method, tvb, 0, req_strlen, FALSE);
876 basic_response_dissector(tvbuff_t *tvb, proto_tree *tree, int resp_strlen)
879 int minor, major, status_code;
881 /* BEWARE - sscanf() only operates on C strings.
882 * The pointer returned by tvb_get_ptr points into the real data,
883 * which is not necessarily NULL terminated. For this reason,
884 * the sscanf() call is only applied to a buffer guaranteed to
885 * only contain a NULL terminated string. */
886 data = g_strndup((const gchar *)tvb_get_ptr(tvb, 5, resp_strlen), resp_strlen);
887 if (sscanf((const gchar *)data, "%d.%d %d", &minor, &major, &status_code) == 3) {
888 proto_tree_add_uint(tree, hf_http_response_code, tvb, 9, 3, status_code);
889 stat_info->response_code = status_code;
895 * Dissect the http data chunks and add them to the tree.
898 chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo,
899 proto_tree *tree, int offset)
901 guint8 *chunk_string = NULL;
902 guint32 chunk_size = 0;
903 gint chunk_offset = 0;
906 gint chunks_decoded = 0;
907 tvbuff_t *tvb = NULL;
908 tvbuff_t *new_tvb = NULL;
909 gint chunked_data_size = 0;
910 proto_tree *subtree = NULL;
911 proto_item *ti = NULL;
913 if (tvb_ptr == NULL || *tvb_ptr == NULL) {
919 datalen = tvb_reported_length_remaining(tvb, offset);
922 ti = proto_tree_add_text(tree, tvb, offset, datalen,
923 "HTTP chunked response");
924 subtree = proto_item_add_subtree(ti, ett_http_chunked_response);
928 while (datalen != 0) {
929 proto_item *chunk_ti = NULL;
930 proto_tree *chunk_subtree = NULL;
931 tvbuff_t *data_tvb = NULL;
934 linelen = tvb_find_line_end(tvb, offset, -1, &chunk_offset, TRUE);
937 /* Can't get the chunk size line */
941 chunk_string = tvb_get_string(tvb, offset, linelen);
943 if (chunk_string == NULL) {
944 /* Can't get the chunk size line */
951 * We don't care about the extensions.
953 if ((c = strchr(c, ';'))) {
957 if (sscanf(chunk_string, "%x", &chunk_size) != 1) {
958 g_free(chunk_string);
962 g_free(chunk_string);
965 if (chunk_size > datalen) {
967 * The chunk size is more than what's in the tvbuff,
968 * so either the user hasn't enabled decoding, or all
969 * of the segments weren't captured.
971 chunk_size = datalen;
972 }/* else if (new_tvb == NULL) {
973 new_tvb = tvb_new_composite();
978 if (new_tvb != NULL && chunk_size != 0) {
979 tvbuff_t *chunk_tvb = NULL;
981 chunk_tvb = tvb_new_subset(tvb, chunk_offset,
982 chunk_size, datalen);
984 tvb_composite_append(new_tvb, chunk_tvb);
989 chunked_data_size += chunk_size;
991 if (chunk_size != 0) {
992 guint8 *raw_data = g_malloc(chunked_data_size);
995 if (new_tvb != NULL) {
996 raw_len = tvb_length_remaining(new_tvb, 0);
997 tvb_memcpy(new_tvb, raw_data, 0, raw_len);
1002 tvb_memcpy(tvb, (guint8 *)(raw_data + raw_len),
1003 chunk_offset, chunk_size);
1005 new_tvb = tvb_new_real_data(raw_data,
1006 chunked_data_size, chunked_data_size);
1007 tvb_set_free_cb(new_tvb, g_free);
1012 if (chunk_size == 0) {
1013 chunk_ti = proto_tree_add_text(subtree, tvb,
1015 chunk_offset - offset + chunk_size + 2,
1016 "Data chunk (last chunk)");
1018 chunk_ti = proto_tree_add_text(subtree, tvb,
1020 chunk_offset - offset + chunk_size + 2,
1021 "Data chunk (%u octets)", chunk_size);
1024 chunk_subtree = proto_item_add_subtree(chunk_ti,
1025 ett_http_chunk_data);
1027 proto_tree_add_text(chunk_subtree, tvb, offset,
1028 chunk_offset - offset, "Chunk size: %u octets",
1031 data_tvb = tvb_new_subset(tvb, chunk_offset, chunk_size,
1035 if (chunk_size > 0) {
1036 call_dissector(data_handle, data_tvb, pinfo,
1040 proto_tree_add_text(chunk_subtree, tvb, chunk_offset +
1041 chunk_size, 2, "Chunk boundary");
1045 offset = chunk_offset + chunk_size + 2;
1046 datalen = tvb_reported_length_remaining(tvb, offset);
1049 if (new_tvb != NULL) {
1051 /* Placeholder for the day that composite tvbuffer's will work.
1052 tvb_composite_finalize(new_tvb);
1053 / * tvb_set_reported_length(new_tvb, chunked_data_size); * /
1057 * XXX - Don't free this, since the tvbuffer that was passed
1058 * may be used if the data spans multiple frames and reassembly
1067 * We didn't create a new tvb, so don't allow sub dissectors
1068 * try to decode the non-existant entity body.
1070 chunks_decoded = -1;
1073 return chunks_decoded;
1079 * XXX - this won't handle HTTP 0.9 replies, but they're all data
1083 is_http_request_or_reply(const gchar *data, int linelen, http_type_t *type,
1084 RequestDissector *req_dissector, int *req_strlen)
1086 int isHttpRequestOrReply = FALSE;
1090 * From RFC 2774 - An HTTP Extension Framework
1092 * Support the command prefix that identifies the presence of
1093 * a "mandatory" header.
1095 if (linelen >= 2 && strncmp(data, "M-", 2) == 0) {
1102 * From draft-cohen-gena-client-01.txt, available from the uPnP forum:
1103 * NOTIFY, SUBSCRIBE, UNSUBSCRIBE
1105 * From draft-ietf-dasl-protocol-00.txt, a now vanished Microsoft draft:
1108 if (linelen >= 5 && strncmp(data, "HTTP/", 5) == 0) {
1109 *type = HTTP_RESPONSE;
1110 isHttpRequestOrReply = TRUE; /* response */
1111 if (req_dissector) {
1112 *req_dissector = basic_response_dissector;
1113 *req_strlen = linelen - 5;
1116 const guchar * ptr = (const guchar *)data;
1119 /* Look for the space following the Method */
1120 while (index < linelen) {
1129 /* Check the methods that have same length */
1133 if (strncmp(data, "GET", index) == 0 ||
1134 strncmp(data, "PUT", index) == 0) {
1135 *type = HTTP_REQUEST;
1136 isHttpRequestOrReply = TRUE;
1138 else if (strncmp(data, "ICY", index) == 0) {
1139 *type = HTTP_RESPONSE;
1140 isHttpRequestOrReply = TRUE;
1145 if (strncmp(data, "COPY", index) == 0 ||
1146 strncmp(data, "HEAD", index) == 0 ||
1147 strncmp(data, "LOCK", index) == 0 ||
1148 strncmp(data, "MOVE", index) == 0 ||
1149 strncmp(data, "POLL", index) == 0 ||
1150 strncmp(data, "POST", index) == 0) {
1151 *type = HTTP_REQUEST;
1152 isHttpRequestOrReply = TRUE;
1157 if (strncmp(data, "BCOPY", index) == 0 ||
1158 strncmp(data, "BMOVE", index) == 0 ||
1159 strncmp(data, "MKCOL", index) == 0 ||
1160 strncmp(data, "TRACE", index) == 0 ||
1161 strncmp(data, "LABEL", index) == 0 || /* RFC 3253 8.2 */
1162 strncmp(data, "MERGE", index) == 0) { /* RFC 3253 11.2 */
1163 *type = HTTP_REQUEST;
1164 isHttpRequestOrReply = TRUE;
1169 if (strncmp(data, "DELETE", index) == 0 ||
1170 strncmp(data, "SEARCH", index) == 0 ||
1171 strncmp(data, "UNLOCK", index) == 0 ||
1172 strncmp(data, "REPORT", index) == 0 || /* RFC 3253 3.6 */
1173 strncmp(data, "UPDATE", index) == 0) { /* RFC 3253 7.1 */
1174 *type = HTTP_REQUEST;
1175 isHttpRequestOrReply = TRUE;
1177 else if (strncmp(data, "NOTIFY", index) == 0) {
1178 *type = HTTP_NOTIFICATION;
1179 isHttpRequestOrReply = TRUE;
1184 if (strncmp(data, "BDELETE", index) == 0 ||
1185 strncmp(data, "CONNECT", index) == 0 ||
1186 strncmp(data, "OPTIONS", index) == 0 ||
1187 strncmp(data, "CHECKIN", index) == 0) { /* RFC 3253 4.4, 9.4 */
1188 *type = HTTP_REQUEST;
1189 isHttpRequestOrReply = TRUE;
1194 if (strncmp(data, "PROPFIND", index) == 0 ||
1195 strncmp(data, "CHECKOUT", index) == 0) { /* RFC 3253 4.3, 9.3 */
1196 *type = HTTP_REQUEST;
1197 isHttpRequestOrReply = TRUE;
1202 if (strncmp(data, "SUBSCRIBE", index) == 0) {
1203 *type = HTTP_NOTIFICATION;
1204 isHttpRequestOrReply = TRUE;
1205 } else if (strncmp(data, "PROPPATCH", index) == 0 ||
1206 strncmp(data, "BPROPFIND", index) == 0) {
1207 *type = HTTP_REQUEST;
1208 isHttpRequestOrReply = TRUE;
1213 if (strncmp(data, "BPROPPATCH", index) == 0 ||
1214 strncmp(data, "UNCHECKOUT", index) == 0 || /* RFC 3253 4.5 */
1215 strncmp(data, "MKACTIVITY", index) == 0) { /* RFC 3253 13.5 */
1216 *type = HTTP_REQUEST;
1217 isHttpRequestOrReply = TRUE;
1222 if (strncmp(data, "MKWORKSPACE", index) == 0) { /* RFC 3253 6.3 */
1223 *type = HTTP_REQUEST;
1224 isHttpRequestOrReply = TRUE;
1225 } else if (strncmp(data, "UNSUBSCRIBE", index) == 0) {
1226 *type = HTTP_NOTIFICATION;
1227 isHttpRequestOrReply = TRUE;
1232 if (strncmp(data, "VERSION-CONTROL", index) == 0) { /* RFC 3253 3.5 */
1233 *type = HTTP_REQUEST;
1234 isHttpRequestOrReply = TRUE;
1239 if (strncmp(data, "BASELINE-CONTROL", index) == 0) { /* RFC 3253 12.6 */
1240 *type = HTTP_REQUEST;
1241 isHttpRequestOrReply = TRUE;
1249 if (isHttpRequestOrReply && req_dissector) {
1250 *req_dissector = basic_request_dissector;
1251 *req_strlen = index + prefix_len;
1253 if (isHttpRequestOrReply && req_dissector) {
1254 if (!stat_info->request_method)
1255 stat_info->request_method = g_malloc( index+1 );
1256 strncpy( stat_info->request_method, data, index);
1257 stat_info->request_method[index] = '\0';
1261 return isHttpRequestOrReply;
1273 #define HDR_NO_SPECIAL 0
1274 #define HDR_AUTHORIZATION 1
1275 #define HDR_AUTHENTICATE 2
1276 #define HDR_CONTENT_TYPE 3
1277 #define HDR_CONTENT_LENGTH 4
1278 #define HDR_CONTENT_ENCODING 5
1279 #define HDR_TRANSFER_ENCODING 6
1281 static const header_info headers[] = {
1282 { "Authorization", &hf_http_authorization, HDR_AUTHORIZATION },
1283 { "Proxy-Authorization", &hf_http_proxy_authorization, HDR_AUTHORIZATION },
1284 { "Proxy-Authenticate", &hf_http_proxy_authenticate, HDR_AUTHENTICATE },
1285 { "WWW-Authenticate", &hf_http_www_authenticate, HDR_AUTHENTICATE },
1286 { "Content-Type", &hf_http_content_type, HDR_CONTENT_TYPE },
1287 { "Content-Length", &hf_http_content_length, HDR_CONTENT_LENGTH },
1288 { "Content-Encoding", &hf_http_content_encoding, HDR_CONTENT_ENCODING },
1289 { "Transfer-Encoding", &hf_http_transfer_encoding, HDR_TRANSFER_ENCODING },
1293 process_header(tvbuff_t *tvb, int offset, int next_offset,
1294 const guchar *line, int linelen, int colon_offset,
1295 packet_info *pinfo, proto_tree *tree, headers_t *eh_ptr)
1298 int line_end_offset;
1307 proto_item *hdr_item;
1310 len = next_offset - offset;
1311 line_end_offset = offset + linelen;
1312 header_len = colon_offset - offset;
1313 hf_index = find_header_hf_value(tvb, offset, header_len);
1315 if (hf_index == -1) {
1317 * Not a header we know anything about. Just put it into
1321 proto_tree_add_text(tree, tvb, offset, len,
1322 "%s", format_text(line, len));
1326 * Skip whitespace after the colon.
1328 value_offset = colon_offset + 1;
1329 while (value_offset < line_end_offset
1330 && ((c = line[value_offset - offset]) == ' ' || c == '\t'))
1336 value_len = line_end_offset - value_offset;
1337 value = g_malloc(value_len + 1);
1338 memcpy(value, &line[value_offset - offset], value_len);
1339 value[value_len] = '\0';
1340 CLEANUP_PUSH(g_free, value);
1343 * Add it to the protocol tree as a particular field,
1344 * but display the line as is.
1347 hdr_item = proto_tree_add_string_format(tree,
1348 *headers[hf_index].hf, tvb, offset, len,
1349 value, "%s", format_text(line, len));
1354 * Do any special processing that particular headers
1357 switch (headers[hf_index].special) {
1359 case HDR_AUTHORIZATION:
1360 if (check_auth_ntlmssp(hdr_item, tvb, pinfo, value))
1361 break; /* dissected NTLMSSP */
1362 check_auth_basic(hdr_item, tvb, value);
1365 case HDR_AUTHENTICATE:
1366 check_auth_ntlmssp(hdr_item, tvb, pinfo, value);
1369 case HDR_CONTENT_TYPE:
1370 if (eh_ptr->content_type != NULL)
1371 g_free(eh_ptr->content_type);
1372 eh_ptr->content_type = g_malloc(value_len + 1);
1373 memcpy(eh_ptr->content_type, value, value_len);
1374 for (i = 0; i < value_len; i++) {
1376 if (c == ';' || isspace(c)) {
1378 * End of subtype - either
1379 * white space or a ";"
1380 * separating the subtype from
1387 * Map the character to lower case;
1388 * content types are case-insensitive.
1390 eh_ptr->content_type[i] = tolower(eh_ptr->content_type[i]);
1392 eh_ptr->content_type[i] = '\0';
1394 * Now find the start of the optional parameters;
1395 * skip the optional white space and the semicolon
1396 * if this has not been done before.
1399 while (i < value_len) {
1400 c = eh_ptr->content_type[i];
1401 if (c == ';' || isspace(c))
1402 /* Skip till start of parameters */
1408 eh_ptr->content_type_parameters = eh_ptr->content_type + i;
1410 eh_ptr->content_type_parameters = NULL;
1413 case HDR_CONTENT_LENGTH:
1414 eh_ptr->content_length = strtol(value, &p, 10);
1416 if (eh_ptr->content_length < 0 || p == value ||
1417 (*up != '\0' && !isspace(*up)))
1418 eh_ptr->content_length = -1; /* not valid */
1421 case HDR_CONTENT_ENCODING:
1422 if (eh_ptr->content_encoding != NULL)
1423 g_free(eh_ptr->content_encoding);
1424 eh_ptr->content_encoding = g_malloc(value_len + 1);
1425 memcpy(eh_ptr->content_encoding, value, value_len);
1426 eh_ptr->content_encoding[value_len] = '\0';
1429 case HDR_TRANSFER_ENCODING:
1430 if (eh_ptr->transfer_encoding != NULL)
1431 g_free(eh_ptr->transfer_encoding);
1432 eh_ptr->transfer_encoding = g_malloc(value_len + 1);
1433 memcpy(eh_ptr->transfer_encoding, value, value_len);
1434 eh_ptr->transfer_encoding[value_len] = '\0';
1439 * Free the value, by calling and popping the cleanup
1442 CLEANUP_CALL_AND_POP;
1446 /* Returns index of header tag in headers */
1448 find_header_hf_value(tvbuff_t *tvb, int offset, guint header_len)
1452 for (i = 0; i < array_length(headers); i++) {
1453 if (header_len == strlen(headers[i].name) &&
1454 tvb_strncaseeql(tvb, offset,
1455 headers[i].name, header_len) == 0)
1463 * Dissect Microsoft's abomination called NTLMSSP over HTTP.
1466 check_auth_ntlmssp(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo,
1469 static const char *ntlm_headers[] = {
1474 const char **header;
1476 proto_tree *hdr_tree;
1479 * Check for NTLM credentials and challenge; those can
1480 * occur with WWW-Authenticate.
1482 for (header = &ntlm_headers[0]; *header != NULL; header++) {
1483 hdrlen = strlen(*header);
1484 if (strncmp(value, *header, hdrlen) == 0) {
1485 if (hdr_item != NULL) {
1486 hdr_tree = proto_item_add_subtree(hdr_item,
1491 dissect_http_ntlmssp(tvb, pinfo, hdr_tree, value);
1499 * Dissect HTTP Basic authorization.
1502 check_auth_basic(proto_item *hdr_item, tvbuff_t *tvb, gchar *value)
1504 static const char *basic_headers[] = {
1508 const char **header;
1510 proto_tree *hdr_tree;
1513 for (header = &basic_headers[0]; *header != NULL; header++) {
1514 hdrlen = strlen(*header);
1515 if (strncmp(value, *header, hdrlen) == 0) {
1516 if (hdr_item != NULL) {
1517 hdr_tree = proto_item_add_subtree(hdr_item,
1523 len = epan_base64_decode(value);
1525 proto_tree_add_string(hdr_tree, hf_http_basic, tvb,
1535 dissect_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1540 while (tvb_reported_length_remaining(tvb, offset) != 0) {
1541 len = dissect_http_message(tvb, offset, pinfo, tree);
1547 * OK, we've set the Protocol and Info columns for the
1548 * first HTTP message; make the columns non-writable,
1549 * so that we don't change it for subsequent HTTP messages.
1551 col_set_writable(pinfo->cinfo, FALSE);
1556 dissect_http_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1558 dissect_http_message(tvb, 0, pinfo, tree);
1562 proto_register_http(void)
1564 static hf_register_info hf[] = {
1565 { &hf_http_notification,
1566 { "Notification", "http.notification",
1567 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1568 "TRUE if HTTP notification", HFILL }},
1569 { &hf_http_response,
1570 { "Response", "http.response",
1571 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1572 "TRUE if HTTP response", HFILL }},
1574 { "Request", "http.request",
1575 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1576 "TRUE if HTTP request", HFILL }},
1578 { "Credentials", "http.authbasic",
1579 FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }},
1580 { &hf_http_request_method,
1581 { "Request Method", "http.request.method",
1582 FT_STRING, BASE_NONE, NULL, 0x0,
1583 "HTTP Request Method", HFILL }},
1584 { &hf_http_response_code,
1585 { "Response Code", "http.response.code",
1586 FT_UINT16, BASE_DEC, NULL, 0x0,
1587 "HTTP Response Code", HFILL }},
1588 { &hf_http_authorization,
1589 { "Authorization", "http.authorization",
1590 FT_STRING, BASE_NONE, NULL, 0x0,
1591 "HTTP Authorization header", HFILL }},
1592 { &hf_http_proxy_authenticate,
1593 { "Proxy-Authenticate", "http.proxy_authenticate",
1594 FT_STRING, BASE_NONE, NULL, 0x0,
1595 "HTTP Proxy-Authenticate header", HFILL }},
1596 { &hf_http_proxy_authorization,
1597 { "Proxy-Authorization", "http.proxy_authorization",
1598 FT_STRING, BASE_NONE, NULL, 0x0,
1599 "HTTP Proxy-Authorization header", HFILL }},
1600 { &hf_http_www_authenticate,
1601 { "WWW-Authenticate", "http.www_authenticate",
1602 FT_STRING, BASE_NONE, NULL, 0x0,
1603 "HTTP WWW-Authenticate header", HFILL }},
1604 { &hf_http_content_type,
1605 { "Content-Type", "http.content_type",
1606 FT_STRING, BASE_NONE, NULL, 0x0,
1607 "HTTP Content-Type header", HFILL }},
1608 { &hf_http_content_length,
1609 { "Content-Length", "http.content_length",
1610 FT_STRING, BASE_NONE, NULL, 0x0,
1611 "HTTP Content-Length header", HFILL }},
1612 { &hf_http_content_encoding,
1613 { "Content-Encoding", "http.content_encoding",
1614 FT_STRING, BASE_NONE, NULL, 0x0,
1615 "HTTP Content-Encoding header", HFILL }},
1616 { &hf_http_transfer_encoding,
1617 { "Transfer-Encoding", "http.transfer_encoding",
1618 FT_STRING, BASE_NONE, NULL, 0x0,
1619 "HTTP Transfer-Encoding header", HFILL }},
1621 static gint *ett[] = {
1625 &ett_http_chunked_response,
1626 &ett_http_chunk_data,
1627 &ett_http_encoded_entity,
1629 module_t *http_module;
1631 proto_http = proto_register_protocol("Hypertext Transfer Protocol",
1633 proto_register_field_array(proto_http, hf, array_length(hf));
1634 proto_register_subtree_array(ett, array_length(ett));
1635 http_module = prefs_register_protocol(proto_http, NULL);
1636 prefs_register_bool_preference(http_module, "desegment_headers",
1637 "Reassemble HTTP headers spanning multiple TCP segments",
1638 "Whether the HTTP dissector should reassemble headers "
1639 "of a request spanning multiple TCP segments. "
1640 "To use this option, you must also enable "
1641 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1642 &http_desegment_headers);
1643 prefs_register_bool_preference(http_module, "desegment_body",
1644 "Reassemble HTTP bodies spanning multiple TCP segments",
1645 "Whether the HTTP dissector should use the "
1646 "\"Content-length:\" value, if present, to reassemble "
1647 "the body of a request spanning multiple TCP segments, "
1648 "and reassemble chunked data spanning multiple TCP segments. "
1649 "To use this option, you must also enable "
1650 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1651 &http_desegment_body);
1652 prefs_register_bool_preference(http_module, "dechunk_body",
1653 "Reassemble chunked transfer-coded bodies",
1654 "Whether to reassemble bodies of entities that are transfered "
1655 "using the \"Transfer-Encoding: chunked\" method",
1656 &http_dechunk_body);
1658 prefs_register_bool_preference(http_module, "decompress_body",
1659 "Uncompress entity bodies",
1660 "Whether to uncompress entity bodies that are compressed "
1661 "using \"Content-Encoding: \"",
1662 &http_decompress_body);
1665 http_handle = create_dissector_handle(dissect_http, proto_http);
1668 * Dissectors shouldn't register themselves in this table;
1669 * instead, they should call "http_dissector_add()", and
1670 * we'll register the port number they specify as a port
1671 * for HTTP, and register them in our subdissector table.
1673 * This only works for protocols such as IPP that run over
1674 * HTTP on a specific non-HTTP port.
1676 port_subdissector_table = register_dissector_table("http.port",
1677 "TCP port for protocols using HTTP", FT_UINT16, BASE_DEC);
1680 * Dissectors can register themselves in this table.
1681 * It's just "media_type", not "http.content_type", because
1682 * it's an Internet media type, usable by other protocols as well.
1684 media_type_subdissector_table =
1685 register_dissector_table("media_type",
1686 "Internet media type", FT_STRING, BASE_NONE);
1689 * Heuristic dissectors SHOULD register themselves in
1690 * this table using the standard heur_dissector_add()
1693 register_heur_dissector_list("http", &heur_subdissector_list);
1696 * Register for tapping
1698 http_tap = register_tap("http");
1702 * Called by dissectors for protocols that run atop HTTP/TCP.
1705 http_dissector_add(guint32 port, dissector_handle_t handle)
1708 * Register ourselves as the handler for that port number
1711 dissector_add("tcp.port", port, http_handle);
1714 * And register them in *our* table for that port.
1716 dissector_add("http.port", port, handle);
1720 proto_reg_handoff_http(void)
1722 dissector_handle_t http_udp_handle;
1724 data_handle = find_dissector("data");
1725 media_handle = find_dissector("media");
1727 dissector_add("tcp.port", TCP_PORT_HTTP, http_handle);
1728 dissector_add("tcp.port", TCP_ALT_PORT_HTTP, http_handle);
1729 dissector_add("tcp.port", TCP_PORT_PROXY_HTTP, http_handle);
1730 dissector_add("tcp.port", TCP_PORT_PROXY_ADMIN_HTTP, http_handle);
1731 dissector_add("tcp.port", TCP_PORT_HKP, http_handle);
1734 * XXX - is there anything to dissect in the body of an SSDP
1735 * request or reply? I.e., should there be an SSDP dissector?
1737 dissector_add("tcp.port", TCP_PORT_SSDP, http_handle);
1738 http_udp_handle = create_dissector_handle(dissect_http_udp, proto_http);
1739 dissector_add("udp.port", UDP_PORT_SSDP, http_udp_handle);
1741 ntlmssp_handle = find_dissector("ntlmssp");
1742 gssapi_handle = find_dissector("gssapi");
1746 * Content-Type: message/http
1749 static gint proto_message_http = -1;
1750 static gint ett_message_http = -1;
1751 static dissector_handle_t message_http_handle;
1754 dissect_message_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1756 proto_tree *subtree;
1758 gint offset = 0, next_offset;
1761 if (check_col(pinfo->cinfo, COL_INFO))
1762 col_append_str(pinfo->cinfo, COL_INFO, " (message/http)");
1764 ti = proto_tree_add_item(tree, proto_message_http,
1766 subtree = proto_item_add_subtree(ti, ett_message_http);
1767 while (tvb_reported_length_remaining(tvb, offset) != 0) {
1768 len = tvb_find_line_end(tvb, offset,
1769 tvb_ensure_length_remaining(tvb, offset),
1770 &next_offset, FALSE);
1773 proto_tree_add_text(subtree, tvb, offset, next_offset - offset,
1774 "%s", tvb_format_text(tvb, offset, len));
1775 offset = next_offset;
1781 proto_register_message_http(void)
1783 static gint *ett[] = {
1787 proto_message_http = proto_register_protocol(
1788 "Media Type: message/http",
1792 proto_register_subtree_array(ett, array_length(ett));
1793 message_http_handle = create_dissector_handle(dissect_message_http,
1794 proto_message_http);
1798 proto_reg_handoff_message_http(void)
1800 message_http_handle = create_dissector_handle(dissect_message_http,
1801 proto_message_http);
1803 dissector_add_string("media_type", "message/http", message_http_handle);