2 * Routines for HTTP packet disassembly
6 * Guy Harris <guy@alum.mit.edu>
8 * Copyright 2017, Eugene Adell <eugene.adell@gmail.com>
9 * Copyright 2004, Jerry Talkington <jtalkington@users.sourceforge.net>
10 * Copyright 2002, Tim Potter <tpot@samba.org>
11 * Copyright 1999, Andrew Tridgell <tridge@samba.org>
13 * Wireshark - Network traffic analyzer
14 * By Gerald Combs <gerald@wireshark.org>
15 * Copyright 1998 Gerald Combs
17 * SPDX-License-Identifier: GPL-2.0-or-later
24 #include <epan/packet.h>
25 #include <epan/prefs.h>
26 #include <epan/expert.h>
27 #include <epan/follow.h>
28 #include <epan/addr_resolv.h>
30 #include <epan/strutil.h>
31 #include <epan/stats_tree.h>
32 #include <epan/to_str.h>
33 #include <epan/req_resp_hdrs.h>
34 #include <epan/proto_data.h>
35 #include <epan/export_object.h>
37 #include "packet-http.h"
38 #include "packet-tcp.h"
39 #include "packet-ssl.h"
41 void proto_register_http(void);
42 void proto_reg_handoff_http(void);
43 void proto_register_message_http(void);
44 void proto_reg_handoff_message_http(void);
46 static int http_tap = -1;
47 static int http_eo_tap = -1;
48 static int http_follow_tap = -1;
50 static int proto_http = -1;
51 static int proto_http2 = -1;
52 static int proto_ssdp = -1;
53 static int hf_http_notification = -1;
54 static int hf_http_response = -1;
55 static int hf_http_request = -1;
56 static int hf_http_response_number = -1;
57 static int hf_http_request_number = -1;
58 static int hf_http_response_line = -1;
59 static int hf_http_request_line = -1;
60 static int hf_http_basic = -1;
61 static int hf_http_citrix = -1;
62 static int hf_http_citrix_user = -1;
63 static int hf_http_citrix_domain = -1;
64 static int hf_http_citrix_passwd = -1;
65 static int hf_http_citrix_session = -1;
66 static int hf_http_request_method = -1;
67 static int hf_http_request_uri = -1;
68 static int hf_http_request_full_uri = -1;
69 static int hf_http_request_path = -1;
70 static int hf_http_request_query = -1;
71 static int hf_http_request_query_parameter = -1;
72 static int hf_http_request_version = -1;
73 static int hf_http_response_version = -1;
74 static int hf_http_response_code = -1;
75 static int hf_http_response_code_desc = -1;
76 static int hf_http_response_phrase = -1;
77 static int hf_http_authorization = -1;
78 static int hf_http_proxy_authenticate = -1;
79 static int hf_http_proxy_authorization = -1;
80 static int hf_http_proxy_connect_host = -1;
81 static int hf_http_proxy_connect_port = -1;
82 static int hf_http_www_authenticate = -1;
83 static int hf_http_content_type = -1;
84 static int hf_http_content_length_header = -1;
85 static int hf_http_content_length = -1;
86 static int hf_http_content_encoding = -1;
87 static int hf_http_transfer_encoding = -1;
88 static int hf_http_upgrade = -1;
89 static int hf_http_user_agent = -1;
90 static int hf_http_host = -1;
91 static int hf_http_connection = -1;
92 static int hf_http_cookie = -1;
93 static int hf_http_cookie_pair = -1;
94 static int hf_http_accept = -1;
95 static int hf_http_referer = -1;
96 static int hf_http_accept_language = -1;
97 static int hf_http_accept_encoding = -1;
98 static int hf_http_date = -1;
99 static int hf_http_cache_control = -1;
100 static int hf_http_server = -1;
101 static int hf_http_location = -1;
102 static int hf_http_sec_websocket_accept = -1;
103 static int hf_http_sec_websocket_extensions = -1;
104 static int hf_http_sec_websocket_key = -1;
105 static int hf_http_sec_websocket_protocol = -1;
106 static int hf_http_sec_websocket_version = -1;
107 static int hf_http_set_cookie = -1;
108 static int hf_http_last_modified = -1;
109 static int hf_http_x_forwarded_for = -1;
110 static int hf_http_request_in = -1;
111 static int hf_http_response_in = -1;
112 static int hf_http_next_request_in = -1;
113 static int hf_http_next_response_in = -1;
114 static int hf_http_prev_request_in = -1;
115 static int hf_http_prev_response_in = -1;
116 static int hf_http_time = -1;
117 static int hf_http_chunk_size = -1;
118 static int hf_http_chunk_boundary = -1;
119 static int hf_http_chunked_trailer_part = -1;
120 static int hf_http_file_data = -1;
121 static int hf_http_unknown_header = -1;
123 static gint ett_http = -1;
124 static gint ett_http_ntlmssp = -1;
125 static gint ett_http_kerberos = -1;
126 static gint ett_http_request = -1;
127 static gint ett_http_request_path = -1;
128 static gint ett_http_request_query = -1;
129 static gint ett_http_chunked_response = -1;
130 static gint ett_http_chunk_data = -1;
131 static gint ett_http_encoded_entity = -1;
132 static gint ett_http_header_item = -1;
134 static expert_field ei_http_chat = EI_INIT;
135 static expert_field ei_http_te_and_length = EI_INIT;
136 static expert_field ei_http_te_unknown = EI_INIT;
137 static expert_field ei_http_subdissector_failed = EI_INIT;
138 static expert_field ei_http_ssl_port = EI_INIT;
139 static expert_field ei_http_leading_crlf = EI_INIT;
140 static expert_field ei_http_bad_header_name = EI_INIT;
142 static dissector_handle_t http_handle;
143 static dissector_handle_t http_tcp_handle;
144 static dissector_handle_t http_tls_handle;
145 static dissector_handle_t http_sctp_handle;
147 static dissector_handle_t media_handle;
148 static dissector_handle_t http2_handle;
149 static dissector_handle_t sstp_handle;
150 static dissector_handle_t ntlmssp_handle;
151 static dissector_handle_t gssapi_handle;
153 /* Stuff for generation/handling of fields for custom HTTP headers */
154 typedef struct _header_field_t {
159 static header_field_t* header_fields;
160 static guint num_header_fields;
162 static GHashTable* header_fields_hash;
163 static hf_register_info* dynamic_hf;
164 static guint dynamic_hf_size;
167 header_fields_update_cb(void *r, char **err)
169 header_field_t *rec = (header_field_t *)r;
172 if (rec->header_name == NULL) {
173 *err = g_strdup("Header name can't be empty");
177 g_strstrip(rec->header_name);
178 if (rec->header_name[0] == 0) {
179 *err = g_strdup("Header name can't be empty");
183 /* Check for invalid characters (to avoid asserting out when
184 * registering the field).
186 c = proto_check_field_name(rec->header_name);
188 *err = g_strdup_printf("Header name can't contain '%c'", c);
197 header_fields_copy_cb(void* n, const void* o, size_t siz _U_)
199 header_field_t* new_rec = (header_field_t*)n;
200 const header_field_t* old_rec = (const header_field_t*)o;
202 new_rec->header_name = g_strdup(old_rec->header_name);
203 new_rec->header_desc = g_strdup(old_rec->header_desc);
209 header_fields_free_cb(void*r)
211 header_field_t* rec = (header_field_t*)r;
213 g_free(rec->header_name);
214 g_free(rec->header_desc);
217 UAT_CSTRING_CB_DEF(header_fields, header_name, header_field_t)
218 UAT_CSTRING_CB_DEF(header_fields, header_desc, header_field_t)
221 * desegmentation of HTTP headers
222 * (when we are over TCP or another protocol providing the desegmentation API)
224 static gboolean http_desegment_headers = TRUE;
227 * desegmentation of HTTP bodies
228 * (when we are over TCP or another protocol providing the desegmentation API)
229 * TODO let the user filter on content-type the bodies he wants desegmented
231 static gboolean http_desegment_body = TRUE;
234 * De-chunking of content-encoding: chunk entity bodies.
236 static gboolean http_dechunk_body = TRUE;
239 * Decompression of zlib encoded entities.
242 static gboolean http_decompress_body = TRUE;
244 static gboolean http_decompress_body = FALSE;
247 /* Simple Service Discovery Protocol
248 * SSDP is implemented atop HTTP (yes, it really *does* run over UDP).
249 * SSDP is the discovery protocol of Universal Plug and Play
250 * UPnP http://www.upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf
252 #define TCP_PORT_SSDP 1900
253 #define UDP_PORT_SSDP 1900
258 * 2710 is the XBT BitTorrent tracker
261 #define TCP_DEFAULT_RANGE "80,3128,3132,5985,8080,8088,11371,1900,2869,2710"
262 #define SCTP_DEFAULT_RANGE "80"
263 #define SSL_DEFAULT_RANGE "443"
265 static range_t *global_http_sctp_range = NULL;
266 static range_t *global_http_ssl_range = NULL;
268 static range_t *http_tcp_range = NULL;
269 static range_t *http_sctp_range = NULL;
270 static range_t *http_ssl_range = NULL;
272 typedef void (*ReqRespDissector)(tvbuff_t*, proto_tree*, int, const guchar*,
273 const guchar*, http_conv_t *);
276 * Transfer codings from
277 * https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#transfer-coding
278 * Note: chunked encoding is handled separately.
280 typedef enum _http_transfer_coding {
281 HTTP_TE_NONE, /* Dummy value for header which is not set */
282 /* HTTP_TE_CHUNKED, */
287 HTTP_TE_UNKNOWN, /* Header was set, but no valid name was found */
288 } http_transfer_coding;
291 * Structure holding information from headers needed by main
292 * HTTP dissector code.
296 char *content_type_parameters;
297 gboolean have_content_length;
298 gint64 content_length;
299 char *content_encoding;
300 gboolean transfer_encoding_chunked;
301 http_transfer_coding transfer_encoding;
305 static int is_http_request_or_reply(const gchar *data, int linelen,
306 http_type_t *type, ReqRespDissector
307 *reqresp_dissector, http_conv_t *conv_data);
308 static guint chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo,
309 proto_tree *tree, int offset);
310 static void process_header(tvbuff_t *tvb, int offset, int next_offset,
311 const guchar *line, int linelen, int colon_offset,
312 packet_info *pinfo, proto_tree *tree,
313 headers_t *eh_ptr, http_conv_t *conv_data,
314 http_type_t http_type);
315 static gint find_header_hf_value(tvbuff_t *tvb, int offset, guint header_len);
316 static gboolean check_auth_ntlmssp(proto_item *hdr_item, tvbuff_t *tvb,
317 packet_info *pinfo, gchar *value);
318 static gboolean check_auth_basic(proto_item *hdr_item, tvbuff_t *tvb,
320 static gboolean check_auth_citrixbasic(proto_item *hdr_item, tvbuff_t *tvb,
321 gchar *value, int offset);
322 static gboolean check_auth_kerberos(proto_item *hdr_item, tvbuff_t *tvb,
323 packet_info *pinfo, const gchar *value);
325 static dissector_table_t port_subdissector_table;
326 static dissector_table_t media_type_subdissector_table;
327 static dissector_table_t upgrade_subdissector_table;
328 static heur_dissector_list_t heur_subdissector_list;
330 /* Used for HTTP Export Object feature */
331 typedef struct _http_eo_t {
337 const guint8 *payload_data;
341 http_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data)
343 export_object_list_t *object_list = (export_object_list_t *)tapdata;
344 const http_eo_t *eo_info = (const http_eo_t *)data;
345 export_object_entry_t *entry;
347 if(eo_info) { /* We have data waiting for us */
348 /* These values will be freed when the Export Object window
350 entry = g_new(export_object_entry_t, 1);
352 entry->pkt_num = pinfo->num;
353 entry->hostname = g_strdup(eo_info->hostname);
354 entry->content_type = g_strdup(eo_info->content_type);
355 entry->filename = eo_info->filename ? g_path_get_basename(eo_info->filename) : NULL;
356 entry->payload_len = eo_info->payload_len;
357 entry->payload_data = (guint8 *)g_memdup(eo_info->payload_data, eo_info->payload_len);
359 object_list->add_entry(object_list->gui_data, entry);
361 return TRUE; /* State changed - window should be redrawn */
363 return FALSE; /* State unchanged - no window updates needed */
367 /* --- HTTP Status Codes */
368 /* Note: The reference for uncommented entries is RFC 2616 */
369 const value_string vals_http_status_code[] = {
371 { 101, "Switching Protocols" },
372 { 102, "Processing" }, /* RFC 2518 */
373 { 103, "Early Hints" }, /* RFC-ietf-httpbis-early-hints-05 */
374 { 199, "Informational - Others" },
379 { 203, "Non-authoritative Information"},
380 { 204, "No Content"},
381 { 205, "Reset Content"},
382 { 206, "Partial Content"},
383 { 207, "Multi-Status"}, /* RFC 4918 */
384 { 208, "Already Reported"}, /* RFC 5842 */
385 { 226, "IM Used"}, /* RFC 3229 */
386 { 299, "Success - Others"},
388 { 300, "Multiple Choices"},
389 { 301, "Moved Permanently"},
392 { 304, "Not Modified"},
394 { 307, "Temporary Redirect"},
395 { 308, "Permanent Redirect"}, /* RFC 7538 */
396 { 399, "Redirection - Others"},
398 { 400, "Bad Request"},
399 { 401, "Unauthorized"},
400 { 402, "Payment Required"},
403 { 405, "Method Not Allowed"},
404 { 406, "Not Acceptable"},
405 { 407, "Proxy Authentication Required"},
406 { 408, "Request Time-out"},
409 { 411, "Length Required"},
410 { 412, "Precondition Failed"},
411 { 413, "Request Entity Too Large"},
412 { 414, "Request-URI Too Long"},
413 { 415, "Unsupported Media Type"},
414 { 416, "Requested Range Not Satisfiable"},
415 { 417, "Expectation Failed"},
416 { 418, "I'm a teapot"}, /* RFC 2324 */
417 { 421, "Misdirected Request"}, /* RFC 7540 */
418 { 422, "Unprocessable Entity"}, /* RFC 4918 */
419 { 423, "Locked"}, /* RFC 4918 */
420 { 424, "Failed Dependency"}, /* RFC 4918 */
421 { 426, "Upgrade Required"}, /* RFC 2817 */
422 { 428, "Precondition Required"}, /* RFC 6585 */
423 { 429, "Too Many Requests"}, /* RFC 6585 */
424 { 431, "Request Header Fields Too Large"}, /* RFC 6585 */
425 { 451, "Unavailable For Legal Reasons"}, /* RFC 7725 */
426 { 499, "Client Error - Others"},
428 { 500, "Internal Server Error"},
429 { 501, "Not Implemented"},
430 { 502, "Bad Gateway"},
431 { 503, "Service Unavailable"},
432 { 504, "Gateway Time-out"},
433 { 505, "HTTP Version not supported"},
434 { 506, "Variant Also Negotiates"}, /* RFC 2295 */
435 { 507, "Insufficient Storage"}, /* RFC 4918 */
436 { 508, "Loop Detected"}, /* RFC 5842 */
437 { 510, "Not Extended"}, /* RFC 2774 */
438 { 511, "Network Authentication Required"}, /* RFC 6585 */
439 { 599, "Server Error - Others"},
444 static const gchar* st_str_reqs = "HTTP Requests by Server";
445 static const gchar* st_str_reqs_by_srv_addr = "HTTP Requests by Server Address";
446 static const gchar* st_str_reqs_by_http_host = "HTTP Requests by HTTP Host";
447 static const gchar* st_str_resps_by_srv_addr = "HTTP Responses by Server Address";
449 static int st_node_reqs = -1;
450 static int st_node_reqs_by_srv_addr = -1;
451 static int st_node_reqs_by_http_host = -1;
452 static int st_node_resps_by_srv_addr = -1;
454 /* HTTP/Load Distribution stats init function */
456 http_reqs_stats_tree_init(stats_tree* st)
458 st_node_reqs = stats_tree_create_node(st, st_str_reqs, 0, TRUE);
459 st_node_reqs_by_srv_addr = stats_tree_create_node(st, st_str_reqs_by_srv_addr, st_node_reqs, TRUE);
460 st_node_reqs_by_http_host = stats_tree_create_node(st, st_str_reqs_by_http_host, st_node_reqs, TRUE);
461 st_node_resps_by_srv_addr = stats_tree_create_node(st, st_str_resps_by_srv_addr, 0, TRUE);
464 /* HTTP/Load Distribution stats packet function */
466 http_reqs_stats_tree_packet(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt _U_, const void* p)
468 const http_info_value_t* v = (const http_info_value_t*)p;
469 int reqs_by_this_host;
470 int reqs_by_this_addr;
471 int resps_by_this_addr;
472 int i = v->response_code;
476 if (v->request_method) {
477 ip_str = address_to_str(NULL, &pinfo->dst);
479 tick_stat_node(st, st_str_reqs, 0, FALSE);
480 tick_stat_node(st, st_str_reqs_by_srv_addr, st_node_reqs, TRUE);
481 tick_stat_node(st, st_str_reqs_by_http_host, st_node_reqs, TRUE);
482 reqs_by_this_addr = tick_stat_node(st, ip_str, st_node_reqs_by_srv_addr, TRUE);
485 reqs_by_this_host = tick_stat_node(st, v->http_host, st_node_reqs_by_http_host, TRUE);
486 tick_stat_node(st, ip_str, reqs_by_this_host, FALSE);
488 tick_stat_node(st, v->http_host, reqs_by_this_addr, FALSE);
491 wmem_free(NULL, ip_str);
496 ip_str = address_to_str(NULL, &pinfo->src);
498 tick_stat_node(st, st_str_resps_by_srv_addr, 0, FALSE);
499 resps_by_this_addr = tick_stat_node(st, ip_str, st_node_resps_by_srv_addr, TRUE);
501 if ( (i>100)&&(i<400) ) {
502 tick_stat_node(st, "OK", resps_by_this_addr, FALSE);
504 tick_stat_node(st, "KO", resps_by_this_addr, FALSE);
507 wmem_free(NULL, ip_str);
516 static int st_node_requests_by_host = -1;
517 static const gchar *st_str_requests_by_host = "HTTP Requests by HTTP Host";
519 /* HTTP/Requests stats init function */
521 http_req_stats_tree_init(stats_tree* st)
523 st_node_requests_by_host = stats_tree_create_node(st, st_str_requests_by_host, 0, TRUE);
526 /* HTTP/Requests stats packet function */
528 http_req_stats_tree_packet(stats_tree* st, packet_info* pinfo _U_, epan_dissect_t* edt _U_, const void* p)
530 const http_info_value_t* v = (const http_info_value_t*)p;
531 int reqs_by_this_host;
533 if (v->request_method) {
534 tick_stat_node(st, st_str_requests_by_host, 0, FALSE);
537 reqs_by_this_host = tick_stat_node(st, v->http_host, st_node_requests_by_host, TRUE);
539 if (v->request_uri) {
540 tick_stat_node(st, v->request_uri, reqs_by_this_host, TRUE);
550 static const gchar *st_str_packets = "Total HTTP Packets";
551 static const gchar *st_str_requests = "HTTP Request Packets";
552 static const gchar *st_str_responses = "HTTP Response Packets";
553 static const gchar *st_str_resp_broken = "???: broken";
554 static const gchar *st_str_resp_100 = "1xx: Informational";
555 static const gchar *st_str_resp_200 = "2xx: Success";
556 static const gchar *st_str_resp_300 = "3xx: Redirection";
557 static const gchar *st_str_resp_400 = "4xx: Client Error";
558 static const gchar *st_str_resp_500 = "5xx: Server Error";
559 static const gchar *st_str_other = "Other HTTP Packets";
561 static int st_node_packets = -1;
562 static int st_node_requests = -1;
563 static int st_node_responses = -1;
564 static int st_node_resp_broken = -1;
565 static int st_node_resp_100 = -1;
566 static int st_node_resp_200 = -1;
567 static int st_node_resp_300 = -1;
568 static int st_node_resp_400 = -1;
569 static int st_node_resp_500 = -1;
570 static int st_node_other = -1;
573 /* HTTP/Packet Counter stats init function */
575 http_stats_tree_init(stats_tree* st)
577 st_node_packets = stats_tree_create_node(st, st_str_packets, 0, TRUE);
578 st_node_requests = stats_tree_create_pivot(st, st_str_requests, st_node_packets);
579 st_node_responses = stats_tree_create_node(st, st_str_responses, st_node_packets, TRUE);
580 st_node_resp_broken = stats_tree_create_node(st, st_str_resp_broken, st_node_responses, TRUE);
581 st_node_resp_100 = stats_tree_create_node(st, st_str_resp_100, st_node_responses, TRUE);
582 st_node_resp_200 = stats_tree_create_node(st, st_str_resp_200, st_node_responses, TRUE);
583 st_node_resp_300 = stats_tree_create_node(st, st_str_resp_300, st_node_responses, TRUE);
584 st_node_resp_400 = stats_tree_create_node(st, st_str_resp_400, st_node_responses, TRUE);
585 st_node_resp_500 = stats_tree_create_node(st, st_str_resp_500, st_node_responses, TRUE);
586 st_node_other = stats_tree_create_node(st, st_str_other, st_node_packets,FALSE);
589 /* HTTP/Packet Counter stats packet function */
591 http_stats_tree_packet(stats_tree* st, packet_info* pinfo _U_, epan_dissect_t* edt _U_, const void* p)
593 const http_info_value_t* v = (const http_info_value_t*)p;
594 guint i = v->response_code;
596 const gchar *resp_str;
599 tick_stat_node(st, st_str_packets, 0, FALSE);
602 tick_stat_node(st, st_str_responses, st_node_packets, FALSE);
604 if ( (i<100)||(i>=600) ) {
605 resp_grp = st_node_resp_broken;
606 resp_str = st_str_resp_broken;
608 resp_grp = st_node_resp_100;
609 resp_str = st_str_resp_100;
611 resp_grp = st_node_resp_200;
612 resp_str = st_str_resp_200;
614 resp_grp = st_node_resp_300;
615 resp_str = st_str_resp_300;
617 resp_grp = st_node_resp_400;
618 resp_str = st_str_resp_400;
620 resp_grp = st_node_resp_500;
621 resp_str = st_str_resp_500;
624 tick_stat_node(st, resp_str, st_node_responses, FALSE);
626 g_snprintf(str, sizeof(str), "%u %s", i,
627 val_to_str(i, vals_http_status_code, "Unknown (%d)"));
628 tick_stat_node(st, str, resp_grp, FALSE);
629 } else if (v->request_method) {
630 stats_tree_tick_pivot(st,st_node_requests,v->request_method);
632 tick_stat_node(st, st_str_other, st_node_packets, FALSE);
639 Generates a referer tree - a best-effort representation of which web request led to which.
642 A user can be forwarded to a single sites from multiple sources. For example,
643 google.com -> foo.com and bing.com -> foo.com. A URI alone is not unique.
645 Additionally, if a user has a subsequent request to foo.com -> bar.com, the
646 full chain could either be:
647 google.com -> foo.com -> bar.com, or
648 bing.com -> foo.com -> bar.com,
650 This indicates that a URI and its referer are not unique. Only a URI and its
651 full referer chain are unique. However, HTTP requests only contain the URI
652 and the immediate referer. This means that any attempt at generating a
653 referer tree is inherently going to be a best-effort approach.
655 This code assumes that the referer in a request is from the most-recent request
658 * To maintain readability of the statistics, whenever a site is visited, all
659 prior referers are 'ticked' as well, so that one can easily see the breakdown.
662 /* Root node for all referer statistics */
663 static int st_node_requests_by_referer = -1;
664 /* Referer statistics root node's text */
665 static const gchar *st_str_request_sequences = "HTTP Request Sequences";
667 /* Mapping of URIs to the most-recently seen node id */
668 static wmem_map_t* refstats_uri_to_node_id_hash = NULL;
669 /* Mapping of node ids to the node's URI ('name' value) */
670 static wmem_map_t* refstats_node_id_to_uri_hash = NULL;
671 /* Mapping of node ids to the parent node id */
672 static wmem_map_t* refstats_node_id_to_parent_node_id_hash = NULL;
675 /* HTTP/Request Sequences stats init function */
677 http_seq_stats_tree_init(stats_tree* st)
679 gint root_node_id = 0;
680 gpointer root_node_id_p = GINT_TO_POINTER(root_node_id);
681 gpointer node_id_p = NULL;
684 refstats_node_id_to_parent_node_id_hash = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
685 refstats_node_id_to_uri_hash = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
686 refstats_uri_to_node_id_hash = wmem_map_new(wmem_file_scope(), wmem_str_hash, g_str_equal);
688 /* Add the root node and its mappings */
689 st_node_requests_by_referer = stats_tree_create_node(st, st_str_request_sequences, root_node_id, TRUE);
690 node_id_p = GINT_TO_POINTER(st_node_requests_by_referer);
691 uri = wmem_strdup(wmem_file_scope(), st_str_request_sequences);
693 wmem_map_insert(refstats_uri_to_node_id_hash, uri, node_id_p);
694 wmem_map_insert(refstats_node_id_to_uri_hash, node_id_p, uri);
695 wmem_map_insert(refstats_node_id_to_parent_node_id_hash, node_id_p, root_node_id_p);
699 http_seq_stats_tick_referer(stats_tree* st, const gchar* arg_referer_uri)
701 gint root_node_id = st_node_requests_by_referer;
702 gpointer root_node_id_p = GINT_TO_POINTER(st_node_requests_by_referer);
703 gint referer_node_id;
704 gpointer referer_node_id_p;
705 gint referer_parent_node_id;
706 gpointer referer_parent_node_id_p;
709 /* Tick the referer's URI */
710 /* Does the node exist? */
711 if (!wmem_map_lookup_extended(refstats_uri_to_node_id_hash, arg_referer_uri, NULL, &referer_node_id_p)) {
712 /* The node for the referer didn't already exist, create the mappings */
713 referer_node_id = tick_stat_node(st, arg_referer_uri, root_node_id, TRUE);
714 referer_node_id_p = GINT_TO_POINTER(referer_node_id);
715 referer_parent_node_id_p = root_node_id_p;
717 referer_uri = wmem_strdup(wmem_file_scope(), arg_referer_uri);
718 wmem_map_insert(refstats_uri_to_node_id_hash, referer_uri, referer_node_id_p);
719 wmem_map_insert(refstats_node_id_to_uri_hash, referer_node_id_p, referer_uri);
720 wmem_map_insert(refstats_node_id_to_parent_node_id_hash, referer_node_id_p, referer_parent_node_id_p);
722 /* The node for the referer already exists, tick it */
723 referer_parent_node_id_p = wmem_map_lookup(refstats_node_id_to_parent_node_id_hash, referer_node_id_p);
724 referer_parent_node_id = GPOINTER_TO_INT(referer_parent_node_id_p);
725 referer_node_id = tick_stat_node(st, arg_referer_uri, referer_parent_node_id, TRUE);
727 return referer_node_id;
731 http_seq_stats_tick_request(stats_tree* st, const gchar* arg_full_uri, gint referer_node_id)
733 gpointer referer_node_id_p = GINT_TO_POINTER(referer_node_id);
738 node_id = tick_stat_node(st, arg_full_uri, referer_node_id, TRUE);
739 node_id_p = GINT_TO_POINTER(node_id);
741 /* Update the mappings. Even if the URI was already seen, the URI->node mapping may need to be updated */
743 /* Is this a new node? */
744 uri = (gchar *) wmem_map_lookup(refstats_node_id_to_uri_hash, node_id_p);
746 /* node not found, add mappings for the node and uri */
747 uri = wmem_strdup(wmem_file_scope(), arg_full_uri);
749 wmem_map_insert(refstats_uri_to_node_id_hash, uri, node_id_p);
750 wmem_map_insert(refstats_node_id_to_uri_hash, node_id_p, uri);
751 wmem_map_insert(refstats_node_id_to_parent_node_id_hash, node_id_p, referer_node_id_p);
753 /* We've seen the node id before. Update the URI mapping refer to this node id*/
754 wmem_map_insert(refstats_uri_to_node_id_hash, uri, node_id_p);
759 determine_http_location_target(const gchar *base_url, const gchar * location_url)
761 /* Resolving a base URI + relative URI to an absolute URI ("Relative Resolution")
762 is complicated. Because of that, we take shortcuts that may result in
763 inaccurate results, but is also significantly simpler.
764 It would be best to use an external library to do this for us.
765 For reference, the RFC is located at https://tools.ietf.org/html/rfc3986#section-5.4
767 Returns NULL if the resolution fails
771 /* base_url must be an absolute URL.*/
772 if (strstr(base_url, "://") == NULL){
777 if (location_url[0] == '\0') {
778 final_target = wmem_strdup(wmem_packet_scope(), base_url);
781 /* Protocol Relative */
782 else if (g_str_has_prefix(location_url, "//") ) {
783 char *base_scheme = g_uri_parse_scheme(base_url);
784 if (base_scheme == NULL) {
787 final_target = wmem_strdup_printf(wmem_packet_scope(), "%s:%s", base_scheme, location_url);
792 else if (strstr(location_url, "://") != NULL) {
793 final_target = wmem_strdup(wmem_packet_scope(), location_url);
798 gchar *start_fragment = strstr(base_url, "#");
799 gchar *start_query = NULL;
800 gchar *base_url_no_fragment = NULL;
801 gchar *base_url_no_query = NULL;
803 /* Strip off the fragment (which should never be present)*/
804 if (start_fragment == NULL) {
805 base_url_no_fragment = wmem_strdup(wmem_packet_scope(), base_url);
808 base_url_no_fragment = wmem_strndup(wmem_packet_scope(), base_url, start_fragment - base_url);
811 /* Strip off the query (Queries are stripped from all relative URIs) */
812 start_query = strstr(base_url_no_fragment, "?");
813 if (start_query == NULL) {
814 base_url_no_query = wmem_strdup(wmem_packet_scope(), base_url_no_fragment);
817 base_url_no_query = wmem_strndup(wmem_packet_scope(), base_url_no_fragment, start_query - base_url_no_fragment);
820 /* A leading question mark (?) means to replace the old query with the new*/
821 if (g_str_has_prefix(location_url, "?")) {
822 final_target = wmem_strdup_printf(wmem_packet_scope(), "%s%s", base_url_no_query, location_url);
825 /* A leading slash means to put the location after the netloc */
826 else if (g_str_has_prefix(location_url, "/")) {
827 gchar *scheme_end = strstr(base_url_no_query, "://") + 3;
830 if (scheme_end[0] == '\0') {
833 netloc_end = strstr(scheme_end, "/");
834 if (netloc_end == NULL) {
837 netloc_length = (gint) (netloc_end - base_url_no_query);
838 final_target = wmem_strdup_printf(wmem_packet_scope(), "%.*s%s", netloc_length, base_url_no_query, location_url);
841 /* Otherwise, it replaces the last element in the URI */
843 gchar *scheme_end = strstr(base_url_no_query, "://") + 3;
844 gchar *end_of_path = g_strrstr(scheme_end, "/");
846 if (end_of_path != NULL) {
847 gint base_through_path = (gint) (end_of_path - base_url_no_query);
848 final_target = wmem_strdup_printf(wmem_packet_scope(), "%.*s/%s", base_through_path, base_url_no_query, location_url);
851 final_target = wmem_strdup_printf(wmem_packet_scope(), "%s/%s", base_url_no_query, location_url);
860 /* HTTP/Request Sequences stats packet function */
862 http_seq_stats_tree_packet(stats_tree* st, packet_info* pinfo _U_, epan_dissect_t* edt _U_, const void* p)
864 const http_info_value_t* v = (const http_info_value_t*)p;
866 /* Track HTTP Redirects */
867 if (v->location_target && v->location_base_uri) {
868 gint referer_node_id;
870 gpointer parent_node_id_p;
871 gpointer current_node_id_p;
874 gchar *absolute_target = determine_http_location_target(v->location_base_uri, v->location_target);
875 /* absolute_target is NULL if the resolution fails */
876 if (absolute_target != NULL) {
877 /* We assume the user makes the request to the absolute_target */
878 /* Tick the base URI */
879 referer_node_id = http_seq_stats_tick_referer(st, v->location_base_uri);
881 /* Tick the location header's resolved URI */
882 http_seq_stats_tick_request(st, absolute_target, referer_node_id);
884 /* Tick all stats nodes above the location */
885 current_node_id_p = GINT_TO_POINTER(referer_node_id);
886 while (wmem_map_lookup_extended(refstats_node_id_to_parent_node_id_hash, current_node_id_p, NULL, &parent_node_id_p)) {
887 parent_node_id = GPOINTER_TO_INT(parent_node_id_p);
888 uri = (gchar *) wmem_map_lookup(refstats_node_id_to_uri_hash, current_node_id_p);
889 tick_stat_node(st, uri, parent_node_id, TRUE);
890 current_node_id_p = parent_node_id_p;
895 /* Track HTTP Requests/Referers */
896 if (v->request_method && v->referer_uri && v->full_uri) {
897 gint referer_node_id;
899 gpointer parent_node_id_p;
900 gpointer current_node_id_p;
902 /* Tick the referer's URI */
903 referer_node_id = http_seq_stats_tick_referer(st, v->referer_uri);
905 /* Tick the request's URI */
906 http_seq_stats_tick_request(st, v->full_uri, referer_node_id);
908 /* Tick all stats nodes above the referer */
909 current_node_id_p = GINT_TO_POINTER(referer_node_id);
910 while (wmem_map_lookup_extended(refstats_node_id_to_parent_node_id_hash, current_node_id_p, NULL, &parent_node_id_p)) {
911 parent_node_id = GPOINTER_TO_INT(parent_node_id_p);
912 uri = (gchar *) wmem_map_lookup(refstats_node_id_to_uri_hash, current_node_id_p);
913 tick_stat_node(st, uri, parent_node_id, TRUE);
914 current_node_id_p = parent_node_id_p;
922 dissect_http_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
925 tvbuff_t *ntlmssp_tvb;
927 ntlmssp_tvb = base64_to_tvb(tvb, line);
928 add_new_data_source(pinfo, ntlmssp_tvb, "NTLMSSP / GSSAPI Data");
929 if (tvb_strneql(ntlmssp_tvb, 0, "NTLMSSP", 7) == 0)
930 call_dissector(ntlmssp_handle, ntlmssp_tvb, pinfo, tree);
932 call_dissector(gssapi_handle, ntlmssp_tvb, pinfo, tree);
936 dissect_http_kerberos(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
939 tvbuff_t *kerberos_tvb;
941 kerberos_tvb = base64_to_tvb(tvb, line + 9); /* skip 'Kerberos ' which is 9 chars */
942 add_new_data_source(pinfo, kerberos_tvb, "Kerberos Data");
943 call_dissector(gssapi_handle, kerberos_tvb, pinfo, tree);
949 get_http_conversation_data(packet_info *pinfo, conversation_t **conversation)
951 http_conv_t *conv_data;
953 *conversation = find_or_create_conversation(pinfo);
955 /* Retrieve information from conversation
956 * or add it if it isn't there yet
958 conv_data = (http_conv_t *)conversation_get_proto_data(*conversation, proto_http);
960 /* Setup the conversation structure itself */
961 conv_data = (http_conv_t *)wmem_alloc0(wmem_file_scope(), sizeof(http_conv_t));
963 conversation_add_proto_data(*conversation, proto_http,
971 * create a new http_req_res_t and add it to the conversation.
972 * @return the new allocated object which is already added to the linked list
974 static http_req_res_t* push_req_res(http_conv_t *conv_data)
976 http_req_res_t *req_res = (http_req_res_t *)wmem_alloc0(wmem_file_scope(), sizeof(http_req_res_t));
977 nstime_set_unset(&(req_res->req_ts));
978 req_res->number = ++conv_data->req_res_num;
980 if (! conv_data->req_res_tail) {
981 conv_data->req_res_tail = req_res;
983 req_res->prev = conv_data->req_res_tail;
984 conv_data->req_res_tail->next = req_res;
985 conv_data->req_res_tail = req_res;
992 * push a request frame number and its time stamp to the conversation data.
994 static void push_req(http_conv_t *conv_data, packet_info *pinfo)
996 /* a request will always create a new http_req_res_t object */
997 http_req_res_t *req_res = push_req_res(conv_data);
999 req_res->req_framenum = pinfo->num;
1000 req_res->req_ts = pinfo->abs_ts;
1002 p_add_proto_data(wmem_file_scope(), pinfo, proto_http, 0, req_res);
1006 * push a response frame number to the conversation data.
1008 static void push_res(http_conv_t *conv_data, packet_info *pinfo)
1010 /* a response will create a new http_req_res_t object: if no
1011 object exists, or if one exists for another response. In
1012 both cases the corresponding request was not
1013 detected/included in the conversation. In all other cases
1014 the http_req_res_t object created by the request is
1016 http_req_res_t *req_res = conv_data->req_res_tail;
1017 if (!req_res || req_res->res_framenum > 0) {
1018 req_res = push_req_res(conv_data);
1020 req_res->res_framenum = pinfo->num;
1021 p_add_proto_data(wmem_file_scope(), pinfo, proto_http, 0, req_res);
1025 * TODO: remove this ugly global variable.
1026 * XXX: do we really want to have to pass this from one function to another?
1028 static http_info_value_t *stat_info;
1031 dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
1032 proto_tree *tree, http_conv_t *conv_data,
1033 const char* proto_tag, int proto, gboolean end_of_stream)
1035 proto_tree *http_tree = NULL;
1036 proto_item *ti = NULL;
1037 proto_item *hidden_item;
1038 const guchar *line, *firstline;
1040 const guchar *linep, *lineend;
1042 int first_linelen, linelen;
1043 gboolean is_request_or_reply, is_ssl = FALSE;
1044 gboolean saw_req_resp_or_header;
1045 http_type_t http_type;
1046 proto_item *hdr_item = NULL;
1047 ReqRespDissector reqresp_dissector;
1048 proto_tree *req_tree;
1052 int reported_datalen = -1;
1053 dissector_handle_t handle;
1054 gboolean dissected = FALSE;
1055 gboolean first_loop = TRUE;
1056 gboolean have_seen_http = FALSE;
1058 /*http_info_value_t *si;*/
1060 heur_dtbl_entry_t *hdtbl_entry;
1061 int reported_length;
1063 gboolean leading_crlf = FALSE;
1064 http_message_info_t message_info;
1066 reported_length = tvb_reported_length_remaining(tvb, offset);
1067 if (reported_length < 1) {
1072 * In the interest of robustness, servers SHOULD ignore any empty
1073 * line(s) received where a Request-Line is expected. In other words, if
1074 * the server is reading the protocol stream at the beginning of a
1075 * message and receives a CRLF first, it should ignore the CRLF.
1077 if (reported_length > 3) {
1078 word = tvb_get_ntohs(tvb,offset);
1079 if (word == 0x0d0a) {
1080 leading_crlf = TRUE;
1086 * If we previously dissected an HTTP request in this conversation then
1087 * we should be pretty sure that whatever we got in this TVB is
1088 * actually HTTP (even if what we have here is part of a file being
1089 * transferred over HTTP).
1091 if (conv_data->request_uri)
1092 have_seen_http = TRUE;
1095 * If this is binary data then there's no point in doing all the string
1096 * operations below: they'll just be slow on this data.
1098 if (!g_ascii_isprint(tvb_get_guint8(tvb, offset))) {
1100 * But, if we've seen some real HTTP then we're sure this is
1101 * an HTTP conversation. Mark it as such.
1103 if (have_seen_http) {
1104 col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag);
1105 col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
1106 ti = proto_tree_add_item(tree, proto, tvb, offset, -1, ENC_NA);
1107 http_tree = proto_item_add_subtree(ti, ett_http);
1109 call_data_dissector(tvb, pinfo, http_tree);
1115 * Is this a request or response?
1117 * Note that "tvb_find_line_end()" will return a value that
1118 * is not longer than what's in the buffer, so the
1119 * "tvb_get_ptr()" call won't throw an exception.
1121 first_linelen = tvb_find_line_end(tvb, offset,
1122 tvb_ensure_captured_length_remaining(tvb, offset), &next_offset,
1125 if (first_linelen == -1) {
1126 /* No complete line was found in this segment, do
1127 * desegmentation if we're told to.
1129 if (!req_resp_hdrs_do_reassembly(tvb, offset, pinfo,
1130 http_desegment_headers, http_desegment_body)) {
1132 * More data needed for desegmentation.
1139 * Is the first line a request or response?
1141 * Note that "tvb_find_line_end()" will return a value that
1142 * is not longer than what's in the buffer, so the
1143 * "tvb_get_ptr()" call won't throw an exception.
1145 firstline = tvb_get_ptr(tvb, offset, first_linelen);
1146 http_type = HTTP_OTHERS; /* type not known yet */
1147 is_request_or_reply = is_http_request_or_reply((const gchar *)firstline,
1148 first_linelen, &http_type, NULL, conv_data);
1149 if (is_request_or_reply) {
1150 gboolean try_desegment_body;
1153 * Yes, it's a request or response.
1154 * Put the first line from the buffer into the summary
1155 * (but leave out the line terminator).
1157 col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", format_text(wmem_packet_scope(), firstline, first_linelen));
1160 * Do header desegmentation if we've been told to,
1161 * and do body desegmentation if we've been told to and
1162 * we find a Content-Length header. Responses to HEAD MUST NOT
1163 * contain a message body, so ignore the Content-Length header
1164 * which is done by disabling body desegmentation.
1166 try_desegment_body = (http_desegment_body &&
1167 !(http_type == HTTP_RESPONSE && conv_data->request_method && g_str_equal(conv_data->request_method, "HEAD")) &&
1169 if (!req_resp_hdrs_do_reassembly(tvb, offset, pinfo,
1170 http_desegment_headers, try_desegment_body)) {
1172 * More data needed for desegmentation.
1176 } else if (have_seen_http) {
1178 * If we know this is HTTP then call it continuation.
1180 col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
1183 if (is_request_or_reply || have_seen_http) {
1185 * Now set COL_PROTOCOL and create the http tree for the
1186 * cases where we set COL_INFO above.
1188 col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag);
1189 ti = proto_tree_add_item(tree, proto, tvb, offset, -1, ENC_NA);
1190 http_tree = proto_item_add_subtree(ti, ett_http);
1193 proto_tree_add_expert(http_tree, pinfo, &ei_http_leading_crlf, tvb, offset-2, 2);
1197 is_ssl = proto_is_frame_protocol(pinfo->layers, "ssl");
1199 stat_info = wmem_new(wmem_packet_scope(), http_info_value_t);
1200 stat_info->framenum = pinfo->num;
1201 stat_info->response_code = 0;
1202 stat_info->request_method = NULL;
1203 stat_info->request_uri = NULL;
1204 stat_info->referer_uri = NULL;
1205 stat_info->http_host = NULL;
1206 stat_info->full_uri = NULL;
1207 stat_info->location_target = NULL;
1208 stat_info->location_base_uri = NULL;
1210 orig_offset = offset;
1213 * Process the packet data, a line at a time.
1215 http_type = HTTP_OTHERS; /* type not known yet */
1216 headers.content_type = NULL; /* content type not known yet */
1217 headers.content_type_parameters = NULL; /* content type parameters too */
1218 headers.have_content_length = FALSE; /* content length not known yet */
1219 headers.content_length = 0; /* content length set to 0 (avoid a gcc warning) */
1220 headers.content_encoding = NULL; /* content encoding not known yet */
1221 headers.transfer_encoding_chunked = FALSE;
1222 headers.transfer_encoding = HTTP_TE_NONE;
1223 headers.upgrade = NULL; /* assume no upgrade header */
1224 saw_req_resp_or_header = FALSE; /* haven't seen anything yet */
1225 while (tvb_offset_exists(tvb, offset)) {
1227 * Find the end of the line.
1228 * XXX - what if we don't find it because the packet
1229 * is cut short by a snapshot length or the header is
1230 * split across TCP segments? How much dissection should
1233 linelen = tvb_find_line_end(tvb, offset,
1234 tvb_ensure_captured_length_remaining(tvb, offset), &next_offset,
1240 * Get a buffer that refers to the line.
1242 * Note that "tvb_find_line_end()" will return a value that
1243 * is not longer than what's in the buffer, so the
1244 * "tvb_get_ptr()" call won't throw an exception.
1246 line = tvb_get_ptr(tvb, offset, linelen);
1247 lineend = line + linelen;
1251 * OK, does it look like an HTTP request or response?
1253 reqresp_dissector = NULL;
1254 is_request_or_reply =
1255 is_http_request_or_reply((const gchar *)line,
1256 linelen, &http_type, &reqresp_dissector, conv_data);
1257 if (is_request_or_reply)
1261 * No. Does it look like a blank line (as would appear
1262 * at the end of an HTTP request)?
1265 goto is_http; /* Yes. */
1268 * No. Does it look like a header?
1270 colon_offset = offset;
1272 linep = (const guchar *)memchr(line, ':', linelen);
1275 * Colon found, assume it is a header.
1277 colon_offset += (int)(linep - line);
1282 * We haven't seen the colon yet.
1284 * If we've already seen an HTTP request or response
1285 * line, or a header line, and we're at the end of
1286 * the tvbuff, we assume this is an incomplete header
1287 * line. (We quit this loop after seeing a blank line,
1288 * so if we've seen a request or response line, or a
1289 * header line, this is probably more of the request
1290 * or response we're presumably seeing. There is some
1291 * risk of false positives, but the same applies for
1292 * full request or response lines or header lines,
1293 * although that's less likely.)
1295 * We throw an exception in that case, by checking for
1296 * the existence of the next byte after the last one
1297 * in the line. If it exists, "tvb_ensure_bytes_exist()"
1298 * throws no exception, and we fall through to the
1299 * "not HTTP" case. If it doesn't exist,
1300 * "tvb_ensure_bytes_exist()" will throw the appropriate
1303 if (saw_req_resp_or_header)
1304 tvb_ensure_bytes_exist(tvb, offset, linelen + 1);
1307 * We don't consider this part of an HTTP request or
1308 * reply, so we don't display it.
1309 * (Yeah, that means we don't display, say, a text/http
1310 * page, but you can get that from the data pane.)
1315 if ((tree) && (http_tree == NULL)) {
1316 ti = proto_tree_add_item(tree, proto, tvb, orig_offset, -1, ENC_NA);
1317 http_tree = proto_item_add_subtree(ti, ett_http);
1319 proto_tree_add_expert(http_tree, pinfo, &ei_http_leading_crlf, tvb, orig_offset-2, 2);
1323 if (first_loop && !is_ssl && pinfo->ptype == PT_TCP &&
1324 (pinfo->srcport == 443 || pinfo->destport == 443)) {
1325 expert_add_info(pinfo, ti, &ei_http_ssl_port);
1331 * Process this line.
1336 * This is a blank line, which means that
1337 * whatever follows it isn't part of this
1340 proto_tree_add_format_text(http_tree, tvb, offset, next_offset - offset);
1341 offset = next_offset;
1346 * Not a blank line - either a request, a reply, or a header
1349 saw_req_resp_or_header = TRUE;
1350 if (is_request_or_reply) {
1351 char *text = tvb_format_text(tvb, offset, next_offset - offset);
1353 req_tree = proto_tree_add_subtree(http_tree, tvb,
1354 offset, next_offset - offset, ett_http_request, &hdr_item, text);
1356 expert_add_info_format(pinfo, hdr_item, &ei_http_chat, "%s", text);
1357 if (reqresp_dissector) {
1358 reqresp_dissector(tvb, req_tree, offset, line,
1359 lineend, conv_data);
1365 process_header(tvb, offset, next_offset, line, linelen,
1366 colon_offset, pinfo, http_tree, &headers, conv_data,
1369 offset = next_offset;
1371 if (stat_info->http_host && stat_info->request_uri) {
1375 if ((g_ascii_strncasecmp(stat_info->request_uri, "http://", 7) == 0) ||
1376 (g_ascii_strncasecmp(stat_info->request_uri, "https://", 8) == 0) ||
1377 (g_ascii_strncasecmp(conv_data->request_method, "CONNECT", 7) == 0)) {
1378 uri = wmem_strdup(wmem_packet_scope(), stat_info->request_uri);
1381 uri = wmem_strdup_printf(wmem_packet_scope(), "%s://%s%s",
1382 is_ssl ? "https" : "http",
1383 g_strstrip(wmem_strdup(wmem_packet_scope(), stat_info->http_host)), stat_info->request_uri);
1385 stat_info->full_uri = wmem_strdup(wmem_packet_scope(), uri);
1386 conv_data->full_uri = wmem_strdup(wmem_file_scope(), uri);
1388 e_ti = proto_tree_add_string(http_tree,
1389 hf_http_request_full_uri, tvb, 0,
1392 PROTO_ITEM_SET_URL(e_ti);
1393 PROTO_ITEM_SET_GENERATED(e_ti);
1397 if (!PINFO_FD_VISITED(pinfo)) {
1398 if (http_type == HTTP_REQUEST) {
1399 push_req(conv_data, pinfo);
1400 } else if (http_type == HTTP_RESPONSE) {
1401 push_res(conv_data, pinfo);
1407 http_req_res_t *curr = (http_req_res_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_http, 0);
1408 http_req_res_t *prev = curr ? curr->prev : NULL;
1409 http_req_res_t *next = curr ? curr->next : NULL;
1411 switch (http_type) {
1413 case HTTP_NOTIFICATION:
1414 hidden_item = proto_tree_add_boolean(http_tree,
1415 hf_http_notification, tvb, 0, 0, 1);
1416 PROTO_ITEM_SET_HIDDEN(hidden_item);
1420 hidden_item = proto_tree_add_boolean(http_tree,
1421 hf_http_response, tvb, 0, 0, 1);
1422 PROTO_ITEM_SET_HIDDEN(hidden_item);
1427 pi = proto_tree_add_uint_format(http_tree, hf_http_response_number, tvb, 0, 0, curr->number, "HTTP response %u/%u", curr->number, conv_data->req_res_num);
1428 PROTO_ITEM_SET_GENERATED(pi);
1430 if (! nstime_is_unset(&(curr->req_ts))) {
1431 nstime_delta(&delta, &pinfo->abs_ts, &(curr->req_ts));
1432 pi = proto_tree_add_time(http_tree, hf_http_time, tvb, 0, 0, &delta);
1433 PROTO_ITEM_SET_GENERATED(pi);
1436 if (prev && prev->req_framenum) {
1437 pi = proto_tree_add_uint(http_tree, hf_http_prev_request_in, tvb, 0, 0, prev->req_framenum);
1438 PROTO_ITEM_SET_GENERATED(pi);
1440 if (prev && prev->res_framenum) {
1441 pi = proto_tree_add_uint(http_tree, hf_http_prev_response_in, tvb, 0, 0, prev->res_framenum);
1442 PROTO_ITEM_SET_GENERATED(pi);
1444 if (curr && curr->req_framenum) {
1445 pi = proto_tree_add_uint(http_tree, hf_http_request_in, tvb, 0, 0, curr->req_framenum);
1446 PROTO_ITEM_SET_GENERATED(pi);
1448 if (next && next->req_framenum) {
1449 pi = proto_tree_add_uint(http_tree, hf_http_next_request_in, tvb, 0, 0, next->req_framenum);
1450 PROTO_ITEM_SET_GENERATED(pi);
1452 if (next && next->res_framenum) {
1453 pi = proto_tree_add_uint(http_tree, hf_http_next_response_in, tvb, 0, 0, next->res_framenum);
1454 PROTO_ITEM_SET_GENERATED(pi);
1460 hidden_item = proto_tree_add_boolean(http_tree,
1461 hf_http_request, tvb, 0, 0, 1);
1462 PROTO_ITEM_SET_HIDDEN(hidden_item);
1465 pi = proto_tree_add_uint_format(http_tree, hf_http_request_number, tvb, 0, 0, curr->number, "HTTP request %u/%u", curr->number, conv_data->req_res_num);
1466 PROTO_ITEM_SET_GENERATED(pi);
1468 if (prev && prev->req_framenum) {
1469 pi = proto_tree_add_uint(http_tree, hf_http_prev_request_in, tvb, 0, 0, prev->req_framenum);
1470 PROTO_ITEM_SET_GENERATED(pi);
1472 if (curr && curr->res_framenum) {
1473 pi = proto_tree_add_uint(http_tree, hf_http_response_in, tvb, 0, 0, curr->res_framenum);
1474 PROTO_ITEM_SET_GENERATED(pi);
1476 if (next && next->req_framenum) {
1477 pi = proto_tree_add_uint(http_tree, hf_http_next_request_in, tvb, 0, 0, next->req_framenum);
1478 PROTO_ITEM_SET_GENERATED(pi);
1489 /* Give the follw tap what we've currently dissected */
1490 if(have_tap_listener(http_follow_tap)) {
1491 tap_queue_packet(http_follow_tap, pinfo, tvb_new_subset_length(tvb, 0, offset));
1494 reported_datalen = tvb_reported_length_remaining(tvb, offset);
1495 datalen = tvb_captured_length_remaining(tvb, offset);
1498 * If a content length was supplied, the amount of data to be
1499 * processed as HTTP payload is the minimum of the content
1500 * length and the amount of data remaining in the frame.
1502 * If a message is received with both a Transfer-Encoding
1503 * header field and a Content-Length header field, the latter
1506 * If no content length was supplied (or if a bad content length
1507 * was supplied), the amount of data to be processed is the amount
1508 * of data remaining in the frame.
1510 * If there was no Content-Length entity header, we should
1511 * accumulate all data until the end of the connection.
1512 * That'd require that the TCP dissector call subdissectors
1513 * for all frames with FIN, even if they contain no data,
1514 * which would require subdissectors to deal intelligently
1515 * with empty segments.
1517 * According to RFC 2616, however, 1xx responses, 204 responses,
1518 * and 304 responses MUST NOT include a message body; if no
1519 * content length is specified for them, we don't attempt to
1522 * XXX - it says the same about responses to HEAD requests;
1523 * unless there's a way to determine from the response
1524 * whether it's a response to a HEAD request, we have to
1525 * keep information about the request and associate that with
1526 * the response in order to handle that.
1528 if (headers.have_content_length &&
1529 headers.transfer_encoding == HTTP_TE_NONE) {
1530 if (datalen > headers.content_length)
1531 datalen = (int)headers.content_length;
1534 * XXX - limit the reported length in the tvbuff we'll
1535 * hand to a subdissector to be no greater than the
1538 * We really need both unreassembled and "how long it'd
1539 * be if it were reassembled" lengths for tvbuffs, so
1540 * that we throw the appropriate exceptions for
1541 * "not enough data captured" (running past the length),
1542 * "packet needed reassembly" (within the length but
1543 * running past the unreassembled length), and
1544 * "packet is malformed" (running past the reassembled
1547 if (reported_datalen > headers.content_length)
1548 reported_datalen = (int)headers.content_length;
1550 switch (http_type) {
1554 * Requests have no content if there's no
1555 * Content-Length header and no Transfer-Encoding
1558 if (headers.transfer_encoding == HTTP_TE_NONE)
1561 reported_datalen = -1;
1565 if ((stat_info->response_code/100) == 1 ||
1566 stat_info->response_code == 204 ||
1567 stat_info->response_code == 304)
1568 datalen = 0; /* no content! */
1571 * XXX - responses to HEAD requests,
1572 * and possibly other responses,
1573 * "MUST NOT" include a
1576 reported_datalen = -1;
1582 * XXX - what about HTTP_NOTIFICATION?
1584 reported_datalen = -1;
1591 * There's stuff left over; process it.
1594 guint chunked_datalen = 0;
1595 char *media_str = NULL;
1596 const gchar *file_data;
1599 * Create a tvbuff for the payload.
1601 * The amount of data to be processed that's
1602 * available in the tvbuff is "datalen", which
1603 * is the minimum of the amount of data left in
1604 * the tvbuff and any specified content length.
1606 * The amount of data to be processed that's in
1607 * this frame, regardless of whether it was
1608 * captured or not, is "reported_datalen",
1609 * which, if no content length was specified,
1610 * is -1, i.e. "to the end of the frame.
1612 next_tvb = tvb_new_subset_length_caplen(tvb, offset, datalen,
1616 * Handle *transfer* encodings.
1618 if (headers.transfer_encoding_chunked) {
1619 if (!http_dechunk_body) {
1620 /* Chunking disabled, cannot dissect further. */
1621 call_data_dissector(next_tvb, pinfo, http_tree);
1622 goto body_dissected;
1625 chunked_datalen = chunked_encoding_dissector(
1626 &next_tvb, pinfo, http_tree, 0);
1628 if (chunked_datalen == 0) {
1630 * The chunks weren't reassembled,
1631 * or there was a single zero
1634 goto body_dissected;
1637 * Add a new data source for the
1640 #if 0 /* Handled in chunked_encoding_dissector() */
1641 tvb_set_child_real_data_tvbuff(tvb,
1644 add_new_data_source(pinfo, next_tvb,
1645 "De-chunked entity body");
1646 /* chunked-body might be smaller than
1648 datalen = chunked_datalen;
1651 /* Handle other transfer codings after de-chunking. */
1652 switch (headers.transfer_encoding) {
1653 case HTTP_TE_COMPRESS:
1654 case HTTP_TE_DEFLATE:
1657 * We currently can't handle, for example, "gzip",
1658 * "compress", or "deflate" as *transfer* encodings;
1659 * just handle them as data for now.
1661 call_data_dissector(next_tvb, pinfo, http_tree);
1662 goto body_dissected;
1664 /* Nothing to do for "identity" or when header is
1665 * missing or invalid. */
1669 * At this point, any chunked *transfer* coding has been removed
1670 * (the entity body has been dechunked) so it can be presented
1671 * for the following operation (*content* encoding), or it has
1672 * been been handed off to the data dissector.
1674 * Handle *content* encodings other than "identity" (which
1675 * shouldn't appear in a Content-Encoding header, but
1676 * we handle it in any case).
1678 if (headers.content_encoding != NULL &&
1679 g_ascii_strcasecmp(headers.content_encoding, "identity") != 0) {
1681 * We currently don't handle, for example, "compress";
1682 * just handle them as data for now.
1684 * After July 7, 2004 the LZW patent expired, so
1685 * support could be added. However, I don't think
1686 * that anybody ever really implemented "compress",
1687 * due to the aforementioned patent.
1689 tvbuff_t *uncomp_tvb = NULL;
1690 proto_item *e_ti = NULL;
1691 proto_tree *e_tree = NULL;
1693 if (http_decompress_body &&
1694 (g_ascii_strcasecmp(headers.content_encoding, "gzip") == 0 ||
1695 g_ascii_strcasecmp(headers.content_encoding, "deflate") == 0 ||
1696 g_ascii_strcasecmp(headers.content_encoding, "x-gzip") == 0 ||
1697 g_ascii_strcasecmp(headers.content_encoding, "x-deflate") == 0))
1699 uncomp_tvb = tvb_child_uncompress(tvb, next_tvb, 0,
1700 tvb_captured_length(next_tvb));
1704 * Add the encoded entity to the protocol tree
1706 e_tree = proto_tree_add_subtree_format(http_tree, next_tvb,
1707 0, tvb_captured_length(next_tvb), ett_http_encoded_entity, &e_ti,
1708 "Content-encoded entity body (%s): %u bytes",
1709 headers.content_encoding,
1710 tvb_captured_length(next_tvb));
1712 if (uncomp_tvb != NULL) {
1714 * Decompression worked
1717 /* XXX - Don't free this, since it's possible
1718 * that the data was only partially
1719 * decompressed, such as when desegmentation
1724 proto_item_append_text(e_ti, " -> %u bytes", tvb_captured_length(uncomp_tvb));
1725 next_tvb = uncomp_tvb;
1726 add_new_data_source(pinfo, next_tvb,
1727 "Uncompressed entity body");
1729 proto_item_append_text(e_ti, " [Error: Decompression failed]");
1730 call_data_dissector(next_tvb, pinfo, e_tree);
1732 goto body_dissected;
1736 * Note that a new data source is added for the entity body
1737 * only if it was content-encoded and/or transfer-encoded.
1740 /* Save values for the Export Object GUI feature if we have
1741 * an active listener to process it (which happens when
1742 * the export object window is open). */
1743 if(have_tap_listener(http_eo_tap)) {
1744 eo_info = wmem_new(wmem_packet_scope(), http_eo_t);
1746 eo_info->hostname = conv_data->http_host;
1747 eo_info->filename = conv_data->request_uri;
1748 eo_info->content_type = headers.content_type;
1749 eo_info->payload_len = tvb_captured_length(next_tvb);
1750 eo_info->payload_data = tvb_get_ptr(next_tvb, 0, eo_info->payload_len);
1752 tap_queue_packet(http_eo_tap, pinfo, eo_info);
1755 /* Save values for the Export Object GUI feature if we have
1756 * an active listener to process it (which happens when
1757 * the export object window is open). */
1758 if(have_tap_listener(http_follow_tap)) {
1759 tap_queue_packet(http_follow_tap, pinfo, next_tvb);
1761 file_data = tvb_get_string_enc(wmem_packet_scope(), next_tvb, 0, tvb_captured_length(next_tvb), ENC_ASCII);
1762 proto_tree_add_string_format_value(http_tree, hf_http_file_data,
1763 next_tvb, 0, tvb_captured_length(next_tvb), file_data, "%u bytes", tvb_captured_length(next_tvb));
1766 * Do subdissector checks.
1768 * First, if we have a Content-Type value, check whether
1769 * there's a subdissector for that media type.
1772 if (headers.content_type != NULL) {
1774 * We didn't find any subdissector that
1775 * registered for the port, and we have a
1776 * Content-Type value. Is there any subdissector
1777 * for that content type?
1779 if (headers.content_type_parameters)
1780 media_str = wmem_strdup(wmem_packet_scope(), headers.content_type_parameters);
1783 * Calling the string handle for the media type
1784 * dissector table will set pinfo->match_string
1785 * to headers.content_type for us.
1787 pinfo->match_string = headers.content_type;
1788 handle = dissector_get_string_handle(
1789 media_type_subdissector_table,
1790 headers.content_type);
1791 if (handle == NULL &&
1792 strncmp(headers.content_type, "multipart/", sizeof("multipart/")-1) == 0) {
1793 /* Try to decode the unknown multipart subtype anyway */
1794 handle = dissector_get_string_handle(
1795 media_type_subdissector_table,
1801 * Now, if we didn't find such a subdissector, check
1802 * whether some subdissector asked that they be called
1803 * if HTTP traffic was on some particular port. This
1804 * handles protocols that use HTTP syntax but don't have
1805 * a media type and instead use a specified port.
1807 if (handle == NULL) {
1808 handle = dissector_get_uint_handle(port_subdissector_table,
1812 message_info.type = http_type;
1813 message_info.media_str = media_str;
1814 if (handle != NULL) {
1816 * We have a subdissector - call it.
1818 dissected = call_dissector_only(handle, next_tvb, pinfo, tree, &message_info);
1820 expert_add_info(pinfo, http_tree, &ei_http_subdissector_failed);
1825 * We don't have a subdissector or we have one and it did not
1826 * dissect the payload - try the heuristic subdissectors.
1828 dissected = dissector_try_heuristic(heur_subdissector_list,
1829 next_tvb, pinfo, tree, &hdtbl_entry, NULL);
1834 * The subdissector dissected the body.
1835 * Fix up the top-level item so that it doesn't
1836 * include the stuff for that protocol.
1839 proto_item_set_len(ti, offset);
1841 if (headers.content_type != NULL) {
1843 * Calling the default media handle if there is a content-type that
1844 * wasn't handled above.
1846 call_dissector_with_data(media_handle, next_tvb, pinfo, tree, &message_info);
1848 /* Call the default data dissector */
1849 call_data_dissector(next_tvb, pinfo, http_tree);
1855 * We've processed "datalen" bytes worth of data
1856 * (which may be no data at all); advance the
1857 * offset past whatever data we've processed.
1862 /* Detect protocol changes after receiving full response headers. */
1863 if (conv_data->request_method && http_type == HTTP_RESPONSE && pinfo->desegment_offset <= 0 && pinfo->desegment_len <= 0) {
1864 dissector_handle_t next_handle = NULL;
1865 gboolean server_acked = FALSE;
1868 * SSTP uses a special request method (instead of the Upgrade
1869 * header) and expects a 200 response to set up the session.
1871 if (strcmp(conv_data->request_method, "SSTP_DUPLEX_POST") == 0 && conv_data->response_code == 200) {
1872 next_handle = sstp_handle;
1873 server_acked = TRUE;
1877 * An HTTP/1.1 upgrade only proceeds if the server responds
1878 * with 101 Switching Protocols. See RFC 7230 Section 6.7.
1880 if (headers.upgrade && conv_data->response_code == 101) {
1881 next_handle = dissector_get_string_handle(upgrade_subdissector_table, headers.upgrade);
1883 char *slash_pos = strchr(headers.upgrade, '/');
1885 /* Try again without version suffix. */
1886 next_handle = dissector_get_string_handle(upgrade_subdissector_table,
1887 wmem_strndup(wmem_packet_scope(), headers.upgrade, slash_pos - headers.upgrade));
1890 server_acked = TRUE;
1894 conv_data->startframe = pinfo->num;
1895 conv_data->startoffset = offset;
1896 conv_data->next_handle = next_handle;
1897 copy_address_wmem(wmem_file_scope(), &conv_data->server_addr, &pinfo->src);
1898 conv_data->server_port = pinfo->srcport;
1902 tap_queue_packet(http_tap, pinfo, stat_info);
1904 return offset - orig_offset;
1907 /* This can be used to dissect an HTTP request until such time
1908 * that a more complete dissector is written for that HTTP request.
1909 * This simple dissector only puts the request method, URI, and
1910 * protocol version into a sub-tree.
1913 basic_request_dissector(tvbuff_t *tvb, proto_tree *tree, int offset,
1914 const guchar *line, const guchar *lineend,
1915 http_conv_t *conv_data)
1917 const guchar *next_token;
1918 const gchar *request_uri;
1919 gchar *query_str, *parameter_str, *path_str;
1920 int request_uri_len, query_str_len, parameter_str_len;
1921 int tokenlen, query_offset, path_len;
1922 proto_item *ti, *tj;
1923 proto_tree *query_tree, *path_tree;
1925 /* The first token is the method. */
1926 tokenlen = get_token_len(line, lineend, &next_token);
1929 proto_tree_add_item(tree, hf_http_request_method, tvb, offset, tokenlen,
1931 if ((next_token - line) > 2 && next_token[-1] == ' ' && next_token[-2] == ' ') {
1932 /* Two spaces in a now indicates empty URI, so roll back one here */
1935 offset += (int) (next_token - line);
1938 /* The next token is the URI. */
1939 tokenlen = get_token_len(line, lineend, &next_token);
1941 /* Save the request URI for various later uses */
1942 request_uri = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, tokenlen, ENC_ASCII);
1943 stat_info->request_uri = wmem_strdup(wmem_packet_scope(), request_uri);
1944 conv_data->request_uri = wmem_strdup(wmem_file_scope(), request_uri);
1946 tj = proto_tree_add_string(tree, hf_http_request_uri, tvb, offset, tokenlen, request_uri);
1947 if (( query_str = strchr(request_uri, '?')) != NULL) {
1948 if (strlen(query_str) > 1) {
1950 query_str_len = (int)strlen(query_str);
1951 request_uri_len = (int)strlen(request_uri);
1952 path_len = request_uri_len - query_str_len;
1953 query_offset = offset + path_len;
1954 path_tree = proto_item_add_subtree(tj, ett_http_request_path);
1955 path_str = wmem_strndup(wmem_packet_scope(), request_uri, path_len-1);
1956 proto_tree_add_string(path_tree, hf_http_request_path, tvb, offset, path_len-1, path_str);
1957 ti = proto_tree_add_string(path_tree, hf_http_request_query, tvb, query_offset, query_str_len, query_str);
1958 query_tree = proto_item_add_subtree(ti, ett_http_request_query);
1959 for ( parameter_str = strtok(query_str, "&"); parameter_str; parameter_str = strtok(NULL, "&") ) {
1960 parameter_str_len = (int) strlen(parameter_str);
1961 proto_tree_add_string(query_tree, hf_http_request_query_parameter, tvb, query_offset, parameter_str_len, parameter_str);
1962 query_offset += parameter_str_len + 1;
1966 offset += (int) (next_token - line);
1969 /* Everything to the end of the line is the version. */
1970 tokenlen = (int) (lineend - line);
1971 proto_tree_add_item(tree, hf_http_request_version, tvb, offset, tokenlen,
1976 basic_response_dissector(tvbuff_t *tvb, proto_tree *tree, int offset,
1977 const guchar *line, const guchar *lineend,
1978 http_conv_t *conv_data _U_)
1980 const guchar *next_token;
1982 gchar response_code_chars[4];
1986 * The first token is the HTTP Version.
1988 tokenlen = get_token_len(line, lineend, &next_token);
1991 proto_tree_add_item(tree, hf_http_response_version, tvb, offset, tokenlen,
1993 /* Advance to the start of the next token. */
1994 offset += (int) (next_token - line);
1998 * The second token is the Status Code.
2000 tokenlen = get_token_len(line, lineend, &next_token);
2004 /* The Status Code characters must be copied into a null-terminated
2005 * buffer for strtoul() to parse them into an unsigned integer value.
2007 memcpy(response_code_chars, line, 3);
2008 response_code_chars[3] = '\0';
2010 stat_info->response_code = conv_data->response_code =
2011 (guint)strtoul(response_code_chars, NULL, 10);
2013 proto_tree_add_uint(tree, hf_http_response_code, tvb, offset, 3,
2014 stat_info->response_code);
2016 r_ti = proto_tree_add_string(tree, hf_http_response_code_desc,
2017 tvb, offset, 3, val_to_str(stat_info->response_code,
2018 vals_http_status_code, "Unknown (%d)"));
2020 PROTO_ITEM_SET_GENERATED(r_ti);
2022 /* Advance to the start of the next token. */
2023 offset += (int) (next_token - line);
2027 * The remaining tokens in the line comprise the Reason Phrase.
2029 tokenlen = (int) (lineend - line);
2030 if (tokenlen >= 1) {
2031 proto_tree_add_item(tree, hf_http_response_phrase, tvb, offset,
2032 tokenlen, ENC_ASCII|ENC_NA);
2036 #if 0 /* XXX: Replaced by code creating the "Dechunked" tvb O(N) rather than O(N^2) */
2038 * Dissect the http data chunks and add them to the tree.
2041 chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo,
2042 proto_tree *tree, int offset)
2044 guint8 *chunk_string = NULL;
2045 guint32 chunk_size = 0;
2046 gint chunk_offset = 0;
2047 guint32 datalen = 0;
2049 gint chunks_decoded = 0;
2050 tvbuff_t *tvb = NULL;
2051 tvbuff_t *new_tvb = NULL;
2052 gint chunked_data_size = 0;
2053 proto_tree *subtree;
2056 if (tvb_ptr == NULL || *tvb_ptr == NULL) {
2062 datalen = tvb_reported_length_remaining(tvb, offset);
2064 subtree = proto_tree_add_subtree(tree, tvb, offset, datalen,
2065 ett_http_chunked_response, NULL, "HTTP chunked response");
2067 while (datalen > 0) {
2068 proto_item *chunk_ti = NULL, *chuck_size_item;
2069 proto_tree *chunk_subtree = NULL;
2070 tvbuff_t *data_tvb = NULL; /* */
2075 linelen = tvb_find_line_end(tvb, offset, -1, &chunk_offset, TRUE);
2078 /* Can't get the chunk size line */
2082 chunk_string = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, linelen, ENC_ASCII);
2084 if (chunk_string == NULL) {
2085 /* Can't get the chunk size line */
2089 c = (gchar*) chunk_string;
2092 * We don't care about the extensions.
2094 if ((c = strchr(c, ';'))) {
2098 chunk_size = (guint32)strtol((gchar*)chunk_string, NULL, 16);
2100 if (chunk_size > datalen) {
2102 * The chunk size is more than what's in the tvbuff,
2103 * so either the user hasn't enabled decoding, or all
2104 * of the segments weren't captured.
2106 chunk_size = datalen;
2109 else if (new_tvb == NULL) {
2110 new_tvb = tvb_new_composite();
2115 if (new_tvb != NULL && chunk_size != 0) {
2116 tvbuff_t *chunk_tvb = NULL;
2118 chunk_tvb = tvb_new_subset_length_caplen(tvb, chunk_offset,
2119 chunk_size, datalen);
2121 tvb_composite_append(new_tvb, chunk_tvb);
2126 chunked_data_size += chunk_size;
2128 raw_data = wmem_alloc(pinfo->pool, chunked_data_size);
2131 if (new_tvb != NULL) {
2132 raw_len = tvb_captured_length_remaining(new_tvb, 0);
2133 tvb_memcpy(new_tvb, raw_data, 0, raw_len);
2138 tvb_memcpy(tvb, (guint8 *)(raw_data + raw_len),
2139 chunk_offset, chunk_size);
2141 /* Don't create a new tvb if we have a single chunk with
2142 * a size of zero (meaning it is the end of the chunks). */
2143 if(chunked_data_size > 0) {
2144 new_tvb = tvb_new_real_data(raw_data,
2145 chunked_data_size, chunked_data_size);
2150 if(chunk_size == 0) {
2151 chunk_subtree = proto_tree_add_subtree(subtree, tvb,
2152 offset, chunk_offset - offset + chunk_size + 2,
2153 ett_http_chunk_data, NULL, "End of chunked encoding");
2155 chunk_subtree = proto_tree_add_subtree_format(subtree, tvb,
2157 chunk_offset - offset + chunk_size + 2,
2158 ett_http_chunk_data, NULL, "Data chunk (%u octets)", chunk_size);
2161 chuck_size_item = proto_tree_add_uint(chunk_subtree, hf_http_chunk_size, tvb, offset,
2163 proto_item_set_len(chuck_size_item, chunk_offset - offset);
2166 * XXX - just add the chunk's data as an item?
2168 * Using the data dissector means that, in
2169 * TShark, you get the entire chunk dumped
2170 * out in hex, in addition to whatever
2171 * dissection is done on the reassembled data.
2173 data_tvb = tvb_new_subset_length(tvb, chunk_offset, chunk_size);
2174 call_data_dissector(data_tvb, pinfo, chunk_subtree);
2176 proto_tree_add_item(chunk_subtree, hf_http_chunked_boundary, tvb,
2177 chunk_offset + chunk_size, 2, ENC_NA);
2181 offset = chunk_offset + chunk_size + 2;
2182 datalen = tvb_reported_length_remaining(tvb, offset);
2185 if (new_tvb != NULL) {
2187 /* Placeholder for the day that composite tvbuffer's will work.
2188 tvb_composite_finalize(new_tvb);
2189 / * tvb_set_reported_length(new_tvb, chunked_data_size); * /
2193 * XXX - Don't free this, since the tvbuffer that was passed
2194 * may be used if the data spans multiple frames and reassembly
2203 * We didn't create a new tvb, so don't allow sub dissectors
2204 * try to decode the non-existent entity body.
2206 chunks_decoded = -1;
2209 return chunks_decoded;
2214 * Dissect the http data chunks and add them to the tree.
2217 chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo,
2218 proto_tree *tree, int offset)
2222 guint32 orig_datalen;
2223 gint chunked_data_size;
2224 proto_tree *subtree;
2225 proto_item *pi_chunked = NULL;
2229 if ((tvb_ptr == NULL) || (*tvb_ptr == NULL)) {
2235 datalen = tvb_reported_length_remaining(tvb, offset);
2237 subtree = proto_tree_add_subtree(tree, tvb, offset, datalen,
2238 ett_http_chunked_response, &pi_chunked,
2239 "HTTP chunked response");
2241 /* Dechunk the "chunked response" to a new memory buffer */
2242 orig_datalen = datalen;
2243 raw_data = (guint8 *)wmem_alloc(pinfo->pool, datalen);
2245 chunked_data_size = 0;
2247 while (datalen > 0) {
2251 guint8 *chunk_string;
2255 linelen = tvb_find_line_end(tvb, offset, -1, &chunk_offset, TRUE);
2258 /* Can't get the chunk size line */
2262 chunk_string = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, linelen, ENC_ASCII);
2264 if (chunk_string == NULL) {
2265 /* Can't get the chunk size line */
2269 c = (gchar*)chunk_string;
2272 * We don't care about the extensions.
2274 if ((c = strchr(c, ';'))) {
2278 chunk_size = (guint32)strtol((gchar*)chunk_string, NULL, 16);
2280 if (chunk_size > datalen) {
2282 * The chunk size is more than what's in the tvbuff,
2283 * so either the user hasn't enabled decoding, or all
2284 * of the segments weren't captured.
2286 chunk_size = datalen;
2289 chunked_data_size += chunk_size;
2291 DISSECTOR_ASSERT((raw_len+chunk_size) <= orig_datalen);
2292 tvb_memcpy(tvb, (guint8 *)(raw_data + raw_len), chunk_offset, chunk_size);
2293 raw_len += chunk_size;
2296 proto_tree *chunk_subtree;
2297 proto_item *chunk_size_item;
2299 if(chunk_size == 0) {
2300 chunk_subtree = proto_tree_add_subtree(subtree, tvb,
2302 chunk_offset - offset + chunk_size + 2,
2303 ett_http_chunk_data, NULL,
2304 "End of chunked encoding");
2306 chunk_subtree = proto_tree_add_subtree_format(subtree, tvb,
2308 chunk_offset - offset + chunk_size + 2,
2309 ett_http_chunk_data, NULL,
2310 "Data chunk (%u octets)", chunk_size);
2313 chunk_size_item = proto_tree_add_uint(chunk_subtree, hf_http_chunk_size, tvb, offset,
2315 proto_item_set_len(chunk_size_item, chunk_offset - offset);
2317 /* last-chunk does not have chunk-data CRLF. */
2318 if (chunk_size > 0) {
2320 * XXX - just add the chunk's data as an item?
2322 * Using the data dissector means that, in
2323 * TShark, you get the entire chunk dumped
2324 * out in hex, in addition to whatever
2325 * dissection is done on the reassembled data.
2327 data_tvb = tvb_new_subset_length(tvb, chunk_offset, chunk_size);
2328 call_data_dissector(data_tvb, pinfo, chunk_subtree);
2330 proto_tree_add_item(chunk_subtree, hf_http_chunk_boundary, tvb,
2331 chunk_offset + chunk_size, 2, ENC_NA);
2335 offset = chunk_offset + chunk_size; /* beginning of next chunk */
2336 if (chunk_size > 0) offset += 2; /* CRLF of chunk */
2337 datalen = tvb_reported_length_remaining(tvb, offset);
2339 /* This is the last chunk */
2340 if (chunk_size == 0) {
2341 /* Check for: trailer-part CRLF.
2342 * trailer-part = *( header-field CRLF ) */
2343 gint trailer_offset = offset, trailer_len;
2344 gint header_field_len;
2345 /* Skip all header-fields. */
2347 trailer_len = trailer_offset - offset;
2348 header_field_len = tvb_find_line_end(tvb,
2350 datalen - trailer_len,
2351 &trailer_offset, TRUE);
2352 } while (header_field_len > 0);
2353 if (trailer_len > 0) {
2354 proto_tree_add_item(subtree,
2355 hf_http_chunked_trailer_part,
2356 tvb, offset, trailer_len, ENC_ASCII|ENC_NA);
2357 offset += trailer_len;
2358 datalen -= trailer_len;
2361 /* last CRLF of chunked-body is found. */
2362 if (header_field_len == 0) {
2363 proto_tree_add_format_text(subtree, tvb, offset,
2364 trailer_offset - offset);
2365 datalen -= trailer_offset - offset;
2371 /* datalen is the remaining bytes that are available for consumption. If
2372 * smaller than orig_datalen, then bytes were consumed. */
2373 if (datalen < orig_datalen) {
2375 proto_item_set_len(pi_chunked, orig_datalen - datalen);
2376 new_tvb = tvb_new_child_real_data(tvb, raw_data, chunked_data_size, chunked_data_size);
2380 /* Size of chunked-body or 0 if none was found. */
2381 return orig_datalen - datalen;
2386 conversation_dissector_is_http(conversation_t *conv, guint32 frame_num)
2388 dissector_handle_t conv_handle;
2392 conv_handle = conversation_get_dissector(conv, frame_num);
2393 return conv_handle == http_handle ||
2394 conv_handle == http_tcp_handle ||
2395 conv_handle == http_sctp_handle;
2398 /* Call a subdissector to handle HTTP CONNECT's traffic */
2400 http_payload_subdissector(tvbuff_t *tvb, proto_tree *tree,
2401 packet_info *pinfo, http_conv_t *conv_data, void* data)
2403 guint32 *ptr = NULL;
2404 guint32 uri_port, saved_port, srcport, destport;
2405 gchar **strings; /* An array for splitting the request URI into hostname and port */
2407 proto_tree *proxy_tree;
2408 conversation_t *conv;
2409 gboolean from_server = pinfo->srcport == conv_data->server_port &&
2410 addresses_equal(&conv_data->server_addr, &pinfo->src);
2412 /* Grab the destination port number from the request URI to find the right subdissector */
2413 strings = wmem_strsplit(wmem_packet_scope(), conv_data->request_uri, ":", 2);
2415 if(strings[0] != NULL && strings[1] != NULL) {
2417 * The string was successfully split in two
2418 * Create a proxy-connect subtree
2421 item = proto_tree_add_item(tree, proto_http, tvb, 0, -1, ENC_NA);
2422 proxy_tree = proto_item_add_subtree(item, ett_http);
2424 item = proto_tree_add_string(proxy_tree, hf_http_proxy_connect_host,
2425 tvb, 0, 0, strings[0]);
2426 PROTO_ITEM_SET_GENERATED(item);
2428 item = proto_tree_add_uint(proxy_tree, hf_http_proxy_connect_port,
2429 tvb, 0, 0, (guint32)strtol(strings[1], NULL, 10) );
2430 PROTO_ITEM_SET_GENERATED(item);
2433 uri_port = (int)strtol(strings[1], NULL, 10); /* Convert string to a base-10 integer */
2436 srcport = pinfo->srcport;
2437 destport = uri_port;
2440 destport = pinfo->destport;
2443 conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_TCP, srcport, destport, 0);
2445 /* We may get stuck in a recursion loop if we let process_tcp_payload() call us.
2446 * So, if the port in the URI is one we're registered for or we have set up a
2447 * conversation (e.g., one we detected heuristically or via Decode-As) call the data
2448 * dissector directly.
2450 if (value_is_in_range(http_tcp_range, uri_port) ||
2451 conversation_dissector_is_http(conv, pinfo->num)) {
2452 call_data_dissector(tvb, pinfo, tree);
2454 /* set pinfo->{src/dst port} and call the TCP sub-dissector lookup */
2456 ptr = &pinfo->destport;
2458 ptr = &pinfo->srcport;
2460 /* Increase pinfo->can_desegment because we are traversing
2461 * http and want to preserve desegmentation functionality for
2462 * the proxied protocol
2464 if( pinfo->can_desegment>0 )
2465 pinfo->can_desegment++;
2469 decode_tcp_ports(tvb, 0, pinfo, tree,
2470 pinfo->srcport, pinfo->destport, NULL,
2471 (struct tcpinfo *)data);
2480 * XXX - this won't handle HTTP 0.9 replies, but they're all data
2484 is_http_request_or_reply(const gchar *data, int linelen, http_type_t *type,
2485 ReqRespDissector *reqresp_dissector,
2486 http_conv_t *conv_data)
2488 int isHttpRequestOrReply = FALSE;
2491 * From RFC 2774 - An HTTP Extension Framework
2493 * Support the command prefix that identifies the presence of
2494 * a "mandatory" header.
2496 if (linelen >= 2 && strncmp(data, "M-", 2) == 0) {
2502 * From draft-cohen-gena-client-01.txt, available from the uPnP forum:
2503 * NOTIFY, SUBSCRIBE, UNSUBSCRIBE
2505 * From draft-ietf-dasl-protocol-00.txt, a now vanished Microsoft draft:
2508 if ((linelen >= 5 && strncmp(data, "HTTP/", 5) == 0) ||
2509 (linelen >= 3 && strncmp(data, "ICY", 3) == 0)) {
2510 *type = HTTP_RESPONSE;
2511 isHttpRequestOrReply = TRUE; /* response */
2512 if (reqresp_dissector)
2513 *reqresp_dissector = basic_response_dissector;
2515 const guchar * ptr = (const guchar *)data;
2518 /* Look for the space following the Method */
2519 while (indx < linelen) {
2528 /* Check the methods that have same length */
2532 if (strncmp(data, "GET", indx) == 0 ||
2533 strncmp(data, "PUT", indx) == 0) {
2534 *type = HTTP_REQUEST;
2535 isHttpRequestOrReply = TRUE;
2540 if (strncmp(data, "COPY", indx) == 0 ||
2541 strncmp(data, "HEAD", indx) == 0 ||
2542 strncmp(data, "LOCK", indx) == 0 ||
2543 strncmp(data, "MOVE", indx) == 0 ||
2544 strncmp(data, "POLL", indx) == 0 ||
2545 strncmp(data, "POST", indx) == 0) {
2546 *type = HTTP_REQUEST;
2547 isHttpRequestOrReply = TRUE;
2552 if (strncmp(data, "BCOPY", indx) == 0 ||
2553 strncmp(data, "BMOVE", indx) == 0 ||
2554 strncmp(data, "MKCOL", indx) == 0 ||
2555 strncmp(data, "TRACE", indx) == 0 ||
2556 strncmp(data, "PATCH", indx) == 0 || /* RFC 5789 */
2557 strncmp(data, "LABEL", indx) == 0 || /* RFC 3253 8.2 */
2558 strncmp(data, "MERGE", indx) == 0) { /* RFC 3253 11.2 */
2559 *type = HTTP_REQUEST;
2560 isHttpRequestOrReply = TRUE;
2565 if (strncmp(data, "DELETE", indx) == 0 ||
2566 strncmp(data, "SEARCH", indx) == 0 ||
2567 strncmp(data, "UNLOCK", indx) == 0 ||
2568 strncmp(data, "REPORT", indx) == 0 || /* RFC 3253 3.6 */
2569 strncmp(data, "UPDATE", indx) == 0) { /* RFC 3253 7.1 */
2570 *type = HTTP_REQUEST;
2571 isHttpRequestOrReply = TRUE;
2573 else if (strncmp(data, "NOTIFY", indx) == 0) {
2574 *type = HTTP_NOTIFICATION;
2575 isHttpRequestOrReply = TRUE;
2580 if (strncmp(data, "BDELETE", indx) == 0 ||
2581 strncmp(data, "CONNECT", indx) == 0 ||
2582 strncmp(data, "OPTIONS", indx) == 0 ||
2583 strncmp(data, "CHECKIN", indx) == 0) { /* RFC 3253 4.4, 9.4 */
2584 *type = HTTP_REQUEST;
2585 isHttpRequestOrReply = TRUE;
2590 if (strncmp(data, "PROPFIND", indx) == 0 ||
2591 strncmp(data, "CHECKOUT", indx) == 0 || /* RFC 3253 4.3, 9.3 */
2592 strncmp(data, "CCM_POST", indx) == 0) {
2593 *type = HTTP_REQUEST;
2594 isHttpRequestOrReply = TRUE;
2599 if (strncmp(data, "SUBSCRIBE", indx) == 0) {
2600 *type = HTTP_NOTIFICATION;
2601 isHttpRequestOrReply = TRUE;
2602 } else if (strncmp(data, "PROPPATCH", indx) == 0 ||
2603 strncmp(data, "BPROPFIND", indx) == 0) {
2604 *type = HTTP_REQUEST;
2605 isHttpRequestOrReply = TRUE;
2610 if (strncmp(data, "BPROPPATCH", indx) == 0 ||
2611 strncmp(data, "UNCHECKOUT", indx) == 0 || /* RFC 3253 4.5 */
2612 strncmp(data, "MKACTIVITY", indx) == 0) { /* RFC 3253 13.5 */
2613 *type = HTTP_REQUEST;
2614 isHttpRequestOrReply = TRUE;
2619 if (strncmp(data, "MKWORKSPACE", indx) == 0 || /* RFC 3253 6.3 */
2620 strncmp(data, "RPC_CONNECT", indx) == 0 || /* [MS-RPCH] 2.1.1.1.1 */
2621 strncmp(data, "RPC_IN_DATA", indx) == 0) { /* [MS-RPCH] 2.1.2.1.1 */
2622 *type = HTTP_REQUEST;
2623 isHttpRequestOrReply = TRUE;
2624 } else if (strncmp(data, "UNSUBSCRIBE", indx) == 0) {
2625 *type = HTTP_NOTIFICATION;
2626 isHttpRequestOrReply = TRUE;
2631 if (strncmp(data, "RPC_OUT_DATA", indx) == 0) { /* [MS-RPCH] 2.1.2.1.2 */
2632 *type = HTTP_REQUEST;
2633 isHttpRequestOrReply = TRUE;
2638 if (strncmp(data, "VERSION-CONTROL", indx) == 0) { /* RFC 3253 3.5 */
2639 *type = HTTP_REQUEST;
2640 isHttpRequestOrReply = TRUE;
2645 if (strncmp(data, "BASELINE-CONTROL", indx) == 0) { /* RFC 3253 12.6 */
2646 *type = HTTP_REQUEST;
2647 isHttpRequestOrReply = TRUE;
2648 } else if (strncmp(data, "SSTP_DUPLEX_POST", indx) == 0) { /* MS SSTP */
2649 *type = HTTP_REQUEST;
2650 isHttpRequestOrReply = TRUE;
2658 if (isHttpRequestOrReply && reqresp_dissector) {
2659 *reqresp_dissector = basic_request_dissector;
2661 stat_info->request_method = wmem_strndup(wmem_packet_scope(), data, indx);
2662 conv_data->request_method = wmem_strndup(wmem_file_scope(), data, indx);
2669 return isHttpRequestOrReply;
2681 #define HDR_NO_SPECIAL 0
2682 #define HDR_AUTHORIZATION 1
2683 #define HDR_AUTHENTICATE 2
2684 #define HDR_CONTENT_TYPE 3
2685 #define HDR_CONTENT_LENGTH 4
2686 #define HDR_CONTENT_ENCODING 5
2687 #define HDR_TRANSFER_ENCODING 6
2689 #define HDR_UPGRADE 8
2690 #define HDR_COOKIE 9
2691 #define HDR_WEBSOCKET_PROTOCOL 10
2692 #define HDR_WEBSOCKET_EXTENSIONS 11
2693 #define HDR_REFERER 12
2694 #define HDR_LOCATION 13
2696 static const header_info headers[] = {
2697 { "Authorization", &hf_http_authorization, HDR_AUTHORIZATION },
2698 { "Proxy-Authorization", &hf_http_proxy_authorization, HDR_AUTHORIZATION },
2699 { "Proxy-Authenticate", &hf_http_proxy_authenticate, HDR_AUTHENTICATE },
2700 { "WWW-Authenticate", &hf_http_www_authenticate, HDR_AUTHENTICATE },
2701 { "Content-Type", &hf_http_content_type, HDR_CONTENT_TYPE },
2702 { "Content-Length", &hf_http_content_length_header, HDR_CONTENT_LENGTH },
2703 { "Content-Encoding", &hf_http_content_encoding, HDR_CONTENT_ENCODING },
2704 { "Transfer-Encoding", &hf_http_transfer_encoding, HDR_TRANSFER_ENCODING },
2705 { "Upgrade", &hf_http_upgrade, HDR_UPGRADE },
2706 { "User-Agent", &hf_http_user_agent, HDR_NO_SPECIAL },
2707 { "Host", &hf_http_host, HDR_HOST },
2708 { "Connection", &hf_http_connection, HDR_NO_SPECIAL },
2709 { "Cookie", &hf_http_cookie, HDR_COOKIE },
2710 { "Accept", &hf_http_accept, HDR_NO_SPECIAL },
2711 { "Referer", &hf_http_referer, HDR_REFERER },
2712 { "Accept-Language", &hf_http_accept_language, HDR_NO_SPECIAL },
2713 { "Accept-Encoding", &hf_http_accept_encoding, HDR_NO_SPECIAL },
2714 { "Date", &hf_http_date, HDR_NO_SPECIAL },
2715 { "Cache-Control", &hf_http_cache_control, HDR_NO_SPECIAL },
2716 { "Server", &hf_http_server, HDR_NO_SPECIAL },
2717 { "Location", &hf_http_location, HDR_LOCATION },
2718 { "Sec-WebSocket-Accept", &hf_http_sec_websocket_accept, HDR_NO_SPECIAL },
2719 { "Sec-WebSocket-Extensions", &hf_http_sec_websocket_extensions, HDR_WEBSOCKET_EXTENSIONS },
2720 { "Sec-WebSocket-Key", &hf_http_sec_websocket_key, HDR_NO_SPECIAL },
2721 { "Sec-WebSocket-Protocol", &hf_http_sec_websocket_protocol, HDR_WEBSOCKET_PROTOCOL },
2722 { "Sec-WebSocket-Version", &hf_http_sec_websocket_version, HDR_NO_SPECIAL },
2723 { "Set-Cookie", &hf_http_set_cookie, HDR_NO_SPECIAL },
2724 { "Last-Modified", &hf_http_last_modified, HDR_NO_SPECIAL },
2725 { "X-Forwarded-For", &hf_http_x_forwarded_for, HDR_NO_SPECIAL },
2729 * Look up a header name (assume lower-case header_name).
2732 get_hf_for_header(char* header_name)
2736 if (header_fields_hash) {
2737 hf_id = (gint*) g_hash_table_lookup(header_fields_hash, header_name);
2749 deregister_header_fields(void)
2752 /* Deregister all fields */
2753 for (guint i = 0; i < dynamic_hf_size; i++) {
2754 proto_deregister_field (proto_http, *(dynamic_hf[i].p_id));
2755 g_free (dynamic_hf[i].p_id);
2758 proto_add_deregistered_data (dynamic_hf);
2760 dynamic_hf_size = 0;
2763 if (header_fields_hash) {
2764 g_hash_table_destroy (header_fields_hash);
2765 header_fields_hash = NULL;
2770 header_fields_post_update_cb(void)
2774 gchar* header_name_key;
2776 deregister_header_fields();
2778 if (num_header_fields) {
2779 header_fields_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
2780 dynamic_hf = g_new0(hf_register_info, num_header_fields);
2781 dynamic_hf_size = num_header_fields;
2783 for (guint i = 0; i < dynamic_hf_size; i++) {
2784 hf_id = g_new(gint,1);
2786 header_name = g_strdup(header_fields[i].header_name);
2787 header_name_key = g_ascii_strdown(header_name, -1);
2789 dynamic_hf[i].p_id = hf_id;
2790 dynamic_hf[i].hfinfo.name = header_name;
2791 dynamic_hf[i].hfinfo.abbrev = g_strdup_printf("http.header.%s", header_name);
2792 dynamic_hf[i].hfinfo.type = FT_STRING;
2793 dynamic_hf[i].hfinfo.display = BASE_NONE;
2794 dynamic_hf[i].hfinfo.strings = NULL;
2795 dynamic_hf[i].hfinfo.bitmask = 0;
2796 dynamic_hf[i].hfinfo.blurb = g_strdup(header_fields[i].header_desc);
2797 HFILL_INIT(dynamic_hf[i]);
2799 g_hash_table_insert(header_fields_hash, header_name_key, hf_id);
2802 proto_register_field_array(proto_http, dynamic_hf, dynamic_hf_size);
2807 header_fields_reset_cb(void)
2809 deregister_header_fields();
2813 * Parses the transfer-coding, returning TRUE if everything was fully understood
2814 * or FALSE when unknown names were encountered.
2817 http_parse_transfer_coding(const char *value, headers_t *eh_ptr)
2819 gboolean is_fully_parsed = TRUE;
2821 /* Mark header as set, but with unknown encoding. */
2822 eh_ptr->transfer_encoding = HTTP_TE_UNKNOWN;
2825 /* skip OWS (SP / HTAB) and commas; stop at the end. */
2826 while (*value == ' ' || *value == '\t' || *value == ',')
2831 if (g_str_has_prefix(value, "chunked")) {
2832 eh_ptr->transfer_encoding_chunked = TRUE;
2833 value += sizeof("chunked") - 1;
2837 /* For now assume that chunked can only combined with exactly
2838 * one other (compression) encoding. Anything else is
2840 if (eh_ptr->transfer_encoding != HTTP_TE_UNKNOWN) {
2841 /* No more transfer codings are expected. */
2842 is_fully_parsed = FALSE;
2846 if (g_str_has_prefix(value, "compress")) {
2847 eh_ptr->transfer_encoding = HTTP_TE_COMPRESS;
2848 value += sizeof("compress") - 1;
2849 } else if (g_str_has_prefix(value, "deflate")) {
2850 eh_ptr->transfer_encoding = HTTP_TE_DEFLATE;
2851 value += sizeof("deflate") - 1;
2852 } else if (g_str_has_prefix(value, "gzip")) {
2853 eh_ptr->transfer_encoding = HTTP_TE_GZIP;
2854 value += sizeof("gzip") - 1;
2855 } else if (g_str_has_prefix(value, "identity")) {
2856 eh_ptr->transfer_encoding = HTTP_TE_IDENTITY;
2857 value += sizeof("identity") - 1;
2858 } else if (g_str_has_prefix(value, "x-compress")) {
2859 eh_ptr->transfer_encoding = HTTP_TE_COMPRESS;
2860 value += sizeof("x-compress") - 1;
2861 } else if (g_str_has_prefix(value, "x-gzip")) {
2862 eh_ptr->transfer_encoding = HTTP_TE_GZIP;
2863 value += sizeof("x-gzip") - 1;
2865 /* Unknown transfer encoding, skip until next comma.
2866 * Stop when no more names are found. */
2867 is_fully_parsed = FALSE;
2868 value = strchr(value, ',');
2874 return is_fully_parsed;
2878 is_token_char(char c)
2880 /* tchar according to https://tools.ietf.org/html/rfc7230#section-3.2.6 */
2881 return strchr("!#$%&\\:*+-.^_`|~", c) || g_ascii_isalnum(c);
2885 process_header(tvbuff_t *tvb, int offset, int next_offset,
2886 const guchar *line, int linelen, int colon_offset,
2887 packet_info *pinfo, proto_tree *tree, headers_t *eh_ptr,
2888 http_conv_t *conv_data, http_type_t http_type)
2891 int line_end_offset;
2901 proto_item *hdr_item, *it;
2905 len = next_offset - offset;
2906 line_end_offset = offset + linelen;
2907 header_len = colon_offset - offset;
2910 * Validate the header name. This allows no space between the field name
2911 * and colon (RFC 7230, Section. 3.2.4).
2913 gboolean valid_header_name = header_len != 0;
2914 if (valid_header_name) {
2915 for (i = 0; i < header_len; i++) {
2917 * NUL is not a valid character; treat it specially
2918 * due to C's notion that strings are NUL-terminated.
2920 if (line[i] == '\0') {
2921 valid_header_name = FALSE;
2924 if (!is_token_char(line[i])) {
2925 valid_header_name = FALSE;
2931 * Not a valid header name? Just add a line plus expert info.
2933 if (!valid_header_name) {
2934 if (http_type == HTTP_REQUEST) {
2935 hf_index = hf_http_request_line;
2936 } else if (http_type == HTTP_RESPONSE) {
2937 hf_index = hf_http_response_line;
2939 hf_index = hf_http_unknown_header;
2941 it = proto_tree_add_item(tree, hf_index, tvb, offset, len, ENC_NA|ENC_ASCII);
2942 proto_item_set_text(it, "%s", format_text(wmem_packet_scope(), line, len));
2943 expert_add_info(pinfo, it, &ei_http_bad_header_name);
2948 * Make a null-terminated, all-lower-case version of the header
2951 header_name = wmem_ascii_strdown(wmem_packet_scope(), &line[0], header_len);
2953 hf_index = find_header_hf_value(tvb, offset, header_len);
2956 * Skip whitespace after the colon.
2958 value_offset = colon_offset + 1;
2959 while (value_offset < line_end_offset
2960 && ((c = line[value_offset - offset]) == ' ' || c == '\t'))
2966 * XXX - the line may well have a NUL in it. Wireshark should
2967 * really treat strings extracted from packets as counted
2968 * strings, so that NUL isn't any different from any other
2969 * character. For now, we just allocate a buffer that's
2970 * value_len+1 bytes long, copy value_len bytes, and stick
2971 * in a NUL terminator, so that the buffer for value actually
2972 * has value_len bytes in it.
2974 value_len = line_end_offset - value_offset;
2975 value = (char *)wmem_alloc(wmem_packet_scope(), value_len+1);
2976 memcpy(value, &line[value_offset - offset], value_len);
2977 value[value_len] = '\0';
2979 if (hf_index == -1) {
2981 * Not a header we know anything about.
2982 * Check if a HF generated from UAT information exists.
2984 hf_id = get_hf_for_header(header_name);
2988 if (http_type == HTTP_REQUEST ||
2989 http_type == HTTP_RESPONSE) {
2990 it = proto_tree_add_item(tree,
2991 http_type == HTTP_RESPONSE ?
2992 hf_http_response_line :
2993 hf_http_request_line,
2996 proto_item_set_text(it, "%s",
2997 format_text(wmem_packet_scope(), line, len));
2999 gchar* str = format_text(wmem_packet_scope(), line, len);
3000 proto_tree_add_string_format(tree, hf_http_unknown_header, tvb, offset,
3001 len, str, "%s", str);
3005 proto_tree_add_string_format(tree,
3006 *hf_id, tvb, offset, len,
3007 value, "%s", format_text(wmem_packet_scope(), line, len));
3008 if (http_type == HTTP_REQUEST ||
3009 http_type == HTTP_RESPONSE) {
3010 it = proto_tree_add_item(tree,
3011 http_type == HTTP_RESPONSE ?
3012 hf_http_response_line :
3013 hf_http_request_line,
3016 proto_item_set_text(it, "%s",
3017 format_text(wmem_packet_scope(), line, len));
3018 PROTO_ITEM_SET_HIDDEN(it);
3024 * Add it to the protocol tree as a particular field,
3025 * but display the line as is.
3028 header_field_info *hfinfo;
3031 hfinfo = proto_registrar_get_nth(*headers[hf_index].hf);
3032 switch(hfinfo->type){
3041 tmp=(guint32)strtol(value, NULL, 10);
3042 hdr_item = proto_tree_add_uint(tree, *headers[hf_index].hf, tvb, offset, len, tmp);
3043 if (http_type == HTTP_REQUEST ||
3044 http_type == HTTP_RESPONSE) {
3045 it = proto_tree_add_item(tree,
3046 http_type == HTTP_RESPONSE ?
3047 hf_http_response_line :
3048 hf_http_request_line,
3051 proto_item_set_text(it, "%d", tmp);
3052 PROTO_ITEM_SET_HIDDEN(it);
3056 hdr_item = proto_tree_add_string_format(tree,
3057 *headers[hf_index].hf, tvb, offset, len,
3058 value, "%s", format_text(wmem_packet_scope(), line, len));
3059 if (http_type == HTTP_REQUEST ||
3060 http_type == HTTP_RESPONSE) {
3061 it = proto_tree_add_item(tree,
3062 http_type == HTTP_RESPONSE ?
3063 hf_http_response_line :
3064 hf_http_request_line,
3067 proto_item_set_text(it, "%s",
3068 format_text(wmem_packet_scope(), line, len));
3069 PROTO_ITEM_SET_HIDDEN(it);
3076 * Do any special processing that particular headers
3079 switch (headers[hf_index].special) {
3081 case HDR_AUTHORIZATION:
3082 if (check_auth_ntlmssp(hdr_item, tvb, pinfo, value))
3083 break; /* dissected NTLMSSP */
3084 if (check_auth_basic(hdr_item, tvb, value))
3085 break; /* dissected basic auth */
3086 if (check_auth_citrixbasic(hdr_item, tvb, value, offset))
3087 break; /* dissected citrix basic auth */
3088 check_auth_kerberos(hdr_item, tvb, pinfo, value);
3091 case HDR_AUTHENTICATE:
3092 if (check_auth_ntlmssp(hdr_item, tvb, pinfo, value))
3093 break; /* dissected NTLMSSP */
3094 check_auth_kerberos(hdr_item, tvb, pinfo, value);
3097 case HDR_CONTENT_TYPE:
3098 eh_ptr->content_type = (gchar*) wmem_memdup(wmem_packet_scope(), (guint8*)value,value_len + 1);
3100 for (i = 0; i < value_len; i++) {
3102 if (c == ';' || g_ascii_isspace(c)) {
3104 * End of subtype - either
3105 * white space or a ";"
3106 * separating the subtype from
3113 * Map the character to lower case;
3114 * content types are case-insensitive.
3116 eh_ptr->content_type[i] = g_ascii_tolower(eh_ptr->content_type[i]);
3118 eh_ptr->content_type[i] = '\0';
3120 * Now find the start of the optional parameters;
3121 * skip the optional white space and the semicolon
3122 * if this has not been done before.
3125 while (i < value_len) {
3126 c = eh_ptr->content_type[i];
3127 if (c == ';' || g_ascii_isspace(c))
3128 /* Skip till start of parameters */
3134 eh_ptr->content_type_parameters = eh_ptr->content_type + i;
3136 eh_ptr->content_type_parameters = NULL;
3139 case HDR_CONTENT_LENGTH:
3141 eh_ptr->content_length = g_ascii_strtoll(value, &p, 10);
3143 if (eh_ptr->content_length < 0 ||
3146 (*up != '\0' && !g_ascii_isspace(*up))) {
3148 * Content length not valid; pretend
3151 eh_ptr->have_content_length = FALSE;
3153 proto_tree *header_tree;
3154 proto_item *tree_item;
3156 * We do have a valid content length.
3158 eh_ptr->have_content_length = TRUE;
3159 header_tree = proto_item_add_subtree(hdr_item, ett_http_header_item);
3160 tree_item = proto_tree_add_uint64(header_tree, hf_http_content_length,
3161 tvb, offset, len, eh_ptr->content_length);
3162 PROTO_ITEM_SET_GENERATED(tree_item);
3163 if (eh_ptr->transfer_encoding != HTTP_TE_NONE) {
3164 expert_add_info(pinfo, hdr_item, &ei_http_te_and_length);
3169 case HDR_CONTENT_ENCODING:
3170 eh_ptr->content_encoding = wmem_strndup(wmem_packet_scope(), value, value_len);
3173 case HDR_TRANSFER_ENCODING:
3174 if (eh_ptr->have_content_length) {
3175 expert_add_info(pinfo, hdr_item, &ei_http_te_and_length);
3177 if (!http_parse_transfer_coding(value, eh_ptr)) {
3178 expert_add_info(pinfo, hdr_item, &ei_http_te_unknown);
3183 stat_info->http_host = wmem_strndup(wmem_packet_scope(), value, value_len);
3184 conv_data->http_host = wmem_strndup(wmem_file_scope(), value, value_len);
3188 eh_ptr->upgrade = wmem_ascii_strdown(wmem_packet_scope(), value, value_len);
3193 proto_tree *cookie_tree;
3194 char *part, *part_end;
3197 cookie_tree = proto_item_add_subtree(hdr_item, ett_http_header_item);
3198 for (i = 0; i < value_len; ) {
3199 /* skip whitespace and ';' (terminates at '\0' or earlier) */
3201 while (c == ';' || g_ascii_isspace(c))
3207 /* find "cookie=foo " in "cookie=foo ; bar" */
3209 part_end = (char *)memchr(part, ';', value_len - i);
3211 part_len =(int)(part_end - part);
3213 part_len = value_len - i;
3215 /* finally add cookie to tree */
3216 proto_tree_add_item(cookie_tree, hf_http_cookie_pair,
3217 tvb, value_offset + i, part_len, ENC_NA|ENC_ASCII);
3223 case HDR_WEBSOCKET_PROTOCOL:
3224 if (http_type == HTTP_RESPONSE) {
3225 conv_data->websocket_protocol = wmem_strndup(wmem_file_scope(), value, value_len);
3229 case HDR_WEBSOCKET_EXTENSIONS:
3230 if (http_type == HTTP_RESPONSE) {
3231 conv_data->websocket_extensions = wmem_strndup(wmem_file_scope(), value, value_len);
3236 stat_info->referer_uri = wmem_strndup(wmem_packet_scope(), value, value_len);
3240 if (conv_data->request_uri){
3241 stat_info->location_target = wmem_strndup(wmem_packet_scope(), value, value_len);
3242 stat_info->location_base_uri = wmem_strdup(wmem_packet_scope(), conv_data->full_uri);
3249 /* Returns index of header tag in headers */
3251 find_header_hf_value(tvbuff_t *tvb, int offset, guint header_len)
3255 for (i = 0; i < array_length(headers); i++) {
3256 if (header_len == strlen(headers[i].name) &&
3257 tvb_strncaseeql(tvb, offset,
3258 headers[i].name, header_len) == 0)
3266 * Dissect Microsoft's abomination called NTLMSSP over HTTP.
3269 check_auth_ntlmssp(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo, gchar *value)
3271 static const char *ntlm_headers[] = {
3276 const char **header;
3278 proto_tree *hdr_tree;
3281 * Check for NTLM credentials and challenge; those can
3282 * occur with WWW-Authenticate.
3284 for (header = &ntlm_headers[0]; *header != NULL; header++) {
3285 hdrlen = strlen(*header);
3286 if (strncmp(value, *header, hdrlen) == 0) {
3287 if (hdr_item != NULL) {
3288 hdr_tree = proto_item_add_subtree(hdr_item,
3293 dissect_http_ntlmssp(tvb, pinfo, hdr_tree, value);
3301 * Dissect HTTP Basic authorization.
3304 check_auth_basic(proto_item *hdr_item, tvbuff_t *tvb, gchar *value)
3306 static const char *basic_headers[] = {
3310 const char **header;
3312 proto_tree *hdr_tree;
3315 for (header = &basic_headers[0]; *header != NULL; header++) {
3316 hdrlen = strlen(*header);
3317 if (strncmp(value, *header, hdrlen) == 0) {
3318 if (hdr_item != NULL) {
3319 hdr_tree = proto_item_add_subtree(hdr_item,
3325 if (strlen(value) > 1) {
3326 g_base64_decode_inplace(value, &len);
3329 proto_tree_add_string(hdr_tree, hf_http_basic, tvb,
3339 * Dissect HTTP CitrixAGBasic authorization.
3342 check_auth_citrixbasic(proto_item *hdr_item, tvbuff_t *tvb, gchar *value, int offset)
3344 static const char *basic_headers[] = {
3348 const char **header;
3350 proto_tree *hdr_tree;
3354 proto_item *hidden_item;
3358 for (header = &basic_headers[0]; *header != NULL; header++) {
3359 hdrlen = strlen(*header);
3360 if (strncmp(value, *header, hdrlen) == 0) {
3361 if (hdr_item != NULL) {
3362 hdr_tree = proto_item_add_subtree(hdr_item,
3367 offset += (int)hdrlen + 15;
3368 hidden_item = proto_tree_add_boolean(hdr_tree,
3369 hf_http_citrix, tvb, 0, 0, 1);
3370 PROTO_ITEM_SET_HIDDEN(hidden_item);
3372 if(strncmp(value, "username=\"", 10) == 0) {
3375 ch_ptr = strchr(value, '"');
3376 if ( ch_ptr != NULL ) {
3377 data_len = (int)(ch_ptr - value + 1);
3378 data_val = wmem_strndup(wmem_packet_scope(), value, data_len);
3380 g_base64_decode_inplace(data_val, &len);
3383 pi = proto_tree_add_string(hdr_tree, hf_http_citrix_user, tvb,
3384 offset , data_len - 1, data_val);
3385 PROTO_ITEM_SET_GENERATED(pi);
3390 if(strncmp(value, "; domain=\"", 10) == 0) {
3393 ch_ptr = strchr(value, '"');
3394 if ( ch_ptr != NULL ) {
3395 data_len = (int)(ch_ptr - value + 1);
3396 data_val = wmem_strndup(wmem_packet_scope(), value, data_len);
3398 g_base64_decode_inplace(data_val, &len);
3401 pi = proto_tree_add_string(hdr_tree, hf_http_citrix_domain, tvb,
3402 offset, data_len - 1, data_val);
3403 PROTO_ITEM_SET_GENERATED(pi);
3408 if(strncmp(value, "; password=\"", 12) == 0) {
3411 ch_ptr = strchr(value, '"');
3412 if ( ch_ptr != NULL ) {
3413 data_len = (int)(ch_ptr - value + 1);
3414 data_val = wmem_strndup(wmem_packet_scope(), value, data_len);
3416 g_base64_decode_inplace(data_val, &len);
3419 pi = proto_tree_add_string(hdr_tree, hf_http_citrix_passwd, tvb,
3420 offset, data_len - 1, data_val);
3421 PROTO_ITEM_SET_GENERATED(pi);
3426 if(strncmp(value, "; AGESessionId=\"", 16) == 0) {
3429 ch_ptr = strchr(value, '"');
3430 if ( ch_ptr != NULL ) {
3431 data_len = (int)(ch_ptr - value + 1);
3432 data_val = wmem_strndup(wmem_packet_scope(), value, data_len);
3434 g_base64_decode_inplace(data_val, &len);
3437 pi = proto_tree_add_string(hdr_tree, hf_http_citrix_session, tvb,
3438 offset, data_len - 1, data_val);
3439 PROTO_ITEM_SET_GENERATED(pi);
3449 check_auth_kerberos(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo, const gchar *value)
3451 proto_tree *hdr_tree;
3453 if (strncmp(value, "Kerberos ", 9) == 0) {
3454 if (hdr_item != NULL) {
3455 hdr_tree = proto_item_add_subtree(hdr_item, ett_http_kerberos);
3459 dissect_http_kerberos(tvb, pinfo, hdr_tree, value);
3466 dissect_http_on_stream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
3467 http_conv_t *conv_data, gboolean end_of_stream)
3472 while (tvb_reported_length_remaining(tvb, offset) > 0) {
3473 /* Switch protocol if the data starts after response headers. */
3474 if (conv_data->startframe &&
3475 (pinfo->num > conv_data->startframe ||
3476 (pinfo->num == conv_data->startframe && offset >= conv_data->startoffset))) {
3477 /* Increase pinfo->can_desegment because we are traversing
3478 * http and want to preserve desegmentation functionality for
3479 * the proxied protocol
3481 if (pinfo->can_desegment > 0)
3482 pinfo->can_desegment++;
3483 if (conv_data->next_handle) {
3484 call_dissector_only(conv_data->next_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
3486 call_data_dissector(tvb_new_subset_remaining(tvb, offset), pinfo, tree);
3490 len = dissect_http_message(tvb, offset, pinfo, tree, conv_data, "HTTP", proto_http, end_of_stream);
3496 * OK, we've set the Protocol and Info columns for the
3497 * first HTTP message; set a fence so that subsequent
3498 * HTTP messages don't overwrite the Info column.
3500 col_set_fence(pinfo->cinfo, COL_INFO);
3505 dissect_http_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
3507 struct tcpinfo *tcpinfo = (struct tcpinfo *)data;
3508 conversation_t *conversation;
3509 http_conv_t *conv_data;
3510 gboolean end_of_stream;
3512 conv_data = get_http_conversation_data(pinfo, &conversation);
3514 /* Call HTTP2 dissector directly when detected via heuristics, but not
3515 * when it was upgraded (the conversation started with HTTP). */
3516 if (conversation_get_proto_data(conversation, proto_http2) &&
3517 !conv_data->startframe) {
3518 if (pinfo->can_desegment > 0)
3519 pinfo->can_desegment++;
3520 return call_dissector_only(http2_handle, tvb, pinfo, tree, data);
3524 * Check if this is proxied connection and if so, hand of dissection to the
3525 * payload-dissector.
3526 * Response code 200 means "OK" and strncmp() == 0 means the strings match exactly */
3527 if(pinfo->num >= conv_data->startframe &&
3528 conv_data->response_code == 200 &&
3529 conv_data->request_method &&
3530 strncmp(conv_data->request_method, "CONNECT", 7) == 0 &&
3531 conv_data->request_uri) {
3532 if (conv_data->startframe == 0 && !PINFO_FD_VISITED(pinfo)) {
3533 conv_data->startframe = pinfo->num;
3534 conv_data->startoffset = 0;
3535 copy_address_wmem(wmem_file_scope(), &conv_data->server_addr, &pinfo->dst);
3536 conv_data->server_port = pinfo->destport;
3538 http_payload_subdissector(tvb, tree, pinfo, conv_data, data);
3540 return tvb_captured_length(tvb);
3543 /* XXX - how to detect end-of-stream without tcpinfo */
3544 end_of_stream = (tcpinfo && IS_TH_FIN(tcpinfo->flags));
3545 dissect_http_on_stream(tvb, pinfo, tree, conv_data, end_of_stream);
3546 return tvb_captured_length(tvb);
3550 dissect_http_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
3552 gint offset = 0, next_offset, linelen;
3553 conversation_t *conversation;
3556 /* Check if we have a line terminated by CRLF
3557 * Return the length of the line (not counting the line terminator at
3558 * the end), or, if we don't find a line terminator:
3560 * if "deseg" is true, return -1;
3562 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, TRUE);
3563 if((linelen == -1)||(linelen == 8)){
3567 /* Check if the line start or ends with the HTTP token */
3568 if((tvb_strncaseeql(tvb, linelen-8, "HTTP/1.", 7) == 0)||(tvb_strncaseeql(tvb, 0, "HTTP/1.", 7) == 0)){
3569 conversation = find_or_create_conversation(pinfo);
3570 conversation_set_dissector_from_frame_number(conversation, pinfo->num, http_tcp_handle);
3571 dissect_http_tcp(tvb, pinfo, tree, data);
3579 dissect_http_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
3581 conversation_t *conversation;
3582 http_conv_t *conv_data;
3584 conv_data = get_http_conversation_data(pinfo, &conversation);
3587 * XXX - we need to provide an end-of-stream indication.
3589 dissect_http_on_stream(tvb, pinfo, tree, conv_data, FALSE);
3590 return tvb_captured_length(tvb);
3594 dissect_http_sctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
3596 conversation_t *conversation;
3597 http_conv_t *conv_data;
3599 conv_data = get_http_conversation_data(pinfo, &conversation);
3602 * XXX - we need to provide an end-of-stream indication.
3604 dissect_http_on_stream(tvb, pinfo, tree, conv_data, FALSE);
3605 return tvb_captured_length(tvb);
3609 dissect_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
3611 conversation_t *conversation;
3612 http_conv_t *conv_data;
3614 conv_data = get_http_conversation_data(pinfo, &conversation);
3617 * XXX - what should be done about reassembly, pipelining, etc.
3620 dissect_http_on_stream(tvb, pinfo, tree, conv_data, FALSE);
3621 return tvb_captured_length(tvb);
3625 dissect_ssdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
3627 conversation_t *conversation;
3628 http_conv_t *conv_data;
3630 conv_data = get_http_conversation_data(pinfo, &conversation);
3631 dissect_http_message(tvb, 0, pinfo, tree, conv_data, "SSDP", proto_ssdp, FALSE);
3632 return tvb_captured_length(tvb);
3636 range_delete_http_ssl_callback(guint32 port, gpointer ptr _U_) {
3637 ssl_dissector_delete(port, http_tls_handle);
3641 range_add_http_ssl_callback(guint32 port, gpointer ptr _U_) {
3642 ssl_dissector_add(port, http_tls_handle);
3645 static void reinit_http(void) {
3646 http_tcp_range = prefs_get_range_value("http", "tcp.port");
3648 dissector_delete_uint_range("sctp.port", http_sctp_range, http_sctp_handle);
3649 wmem_free(wmem_epan_scope(), http_sctp_range);
3650 http_sctp_range = range_copy(wmem_epan_scope(), global_http_sctp_range);
3651 dissector_add_uint_range("sctp.port", http_sctp_range, http_sctp_handle);
3653 range_foreach(http_ssl_range, range_delete_http_ssl_callback, NULL);
3654 wmem_free(wmem_epan_scope(), http_ssl_range);
3655 http_ssl_range = range_copy(wmem_epan_scope(), global_http_ssl_range);
3656 range_foreach(http_ssl_range, range_add_http_ssl_callback, NULL);
3660 proto_register_http(void)
3662 static hf_register_info hf[] = {
3663 { &hf_http_notification,
3664 { "Notification", "http.notification",
3665 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
3666 "TRUE if HTTP notification", HFILL }},
3667 { &hf_http_response,
3668 { "Response", "http.response",
3669 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
3670 "TRUE if HTTP response", HFILL }},
3672 { "Request", "http.request",
3673 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
3674 "TRUE if HTTP request", HFILL }},
3675 { &hf_http_response_number,
3676 { "Response number", "http.response_number",
3677 FT_UINT32, BASE_DEC, NULL, 0x0,
3679 { &hf_http_request_number,
3680 { "Request number", "http.request_number",
3681 FT_UINT32, BASE_DEC, NULL, 0x0,
3684 { "Credentials", "http.authbasic",
3685 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
3687 { "Citrix AG Auth", "http.authcitrix",
3688 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
3689 "TRUE if CitrixAGBasic Auth", HFILL }},
3690 { &hf_http_citrix_user,
3691 { "Citrix AG Username", "http.authcitrix.user",
3692 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
3693 { &hf_http_citrix_domain,
3694 { "Citrix AG Domain", "http.authcitrix.domain",
3695 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
3696 { &hf_http_citrix_passwd,
3697 { "Citrix AG Password", "http.authcitrix.password",
3698 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
3699 { &hf_http_citrix_session,
3700 { "Citrix AG Session ID", "http.authcitrix.session",
3701 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
3702 { &hf_http_response_line,
3703 { "Response line", "http.response.line",
3704 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
3705 { &hf_http_request_line,
3706 { "Request line", "http.request.line",
3707 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
3708 { &hf_http_request_method,
3709 { "Request Method", "http.request.method",
3710 FT_STRING, BASE_NONE, NULL, 0x0,
3711 "HTTP Request Method", HFILL }},
3712 { &hf_http_request_uri,
3713 { "Request URI", "http.request.uri",
3714 FT_STRING, STR_UNICODE, NULL, 0x0,
3715 "HTTP Request-URI", HFILL }},
3716 { &hf_http_request_path,
3717 { "Request URI Path", "http.request.uri.path",
3718 FT_STRING, STR_UNICODE, NULL, 0x0,
3719 "HTTP Request-URI Path", HFILL }},
3720 { &hf_http_request_query,
3721 { "Request URI Query", "http.request.uri.query",
3722 FT_STRING, STR_UNICODE, NULL, 0x0,
3723 "HTTP Request-URI Query", HFILL }},
3724 { &hf_http_request_query_parameter,
3725 { "Request URI Query Parameter", "http.request.uri.query.parameter",
3726 FT_STRING, STR_UNICODE, NULL, 0x0,
3727 "HTTP Request-URI Query Parameter", HFILL }},
3728 { &hf_http_request_version,
3729 { "Request Version", "http.request.version",
3730 FT_STRING, BASE_NONE, NULL, 0x0,
3731 "HTTP Request HTTP-Version", HFILL }},
3732 { &hf_http_response_version,
3733 { "Response Version", "http.response.version",
3734 FT_STRING, BASE_NONE, NULL, 0x0,
3735 "HTTP Response HTTP-Version", HFILL }},
3736 { &hf_http_request_full_uri,
3737 { "Full request URI", "http.request.full_uri",
3738 FT_STRING, BASE_NONE, NULL, 0x0,
3739 "The full requested URI (including host name)", HFILL }},
3740 { &hf_http_response_code,
3741 { "Status Code", "http.response.code",
3742 FT_UINT16, BASE_DEC, NULL, 0x0,
3743 "HTTP Response Status Code", HFILL }},
3744 { &hf_http_response_code_desc,
3745 { "Status Code Description", "http.response.code.desc",
3746 FT_STRING, BASE_NONE, NULL, 0x0,
3747 "HTTP Response Status Code Description", HFILL }},
3748 { &hf_http_response_phrase,
3749 { "Response Phrase", "http.response.phrase",
3750 FT_STRING, BASE_NONE, NULL, 0x0,
3751 "HTTP Response Reason Phrase", HFILL }},
3752 { &hf_http_authorization,
3753 { "Authorization", "http.authorization",
3754 FT_STRING, BASE_NONE, NULL, 0x0,
3755 "HTTP Authorization header", HFILL }},
3756 { &hf_http_proxy_authenticate,
3757 { "Proxy-Authenticate", "http.proxy_authenticate",
3758 FT_STRING, BASE_NONE, NULL, 0x0,
3759 "HTTP Proxy-Authenticate header", HFILL }},
3760 { &hf_http_proxy_authorization,
3761 { "Proxy-Authorization", "http.proxy_authorization",
3762 FT_STRING, BASE_NONE, NULL, 0x0,
3763 "HTTP Proxy-Authorization header", HFILL }},
3764 { &hf_http_proxy_connect_host,
3765 { "Proxy-Connect-Hostname", "http.proxy_connect_host",
3766 FT_STRING, BASE_NONE, NULL, 0x0,
3767 "HTTP Proxy Connect Hostname", HFILL }},
3768 { &hf_http_proxy_connect_port,
3769 { "Proxy-Connect-Port", "http.proxy_connect_port",
3770 FT_UINT16, BASE_DEC, NULL, 0x0,
3771 "HTTP Proxy Connect Port", HFILL }},
3772 { &hf_http_www_authenticate,
3773 { "WWW-Authenticate", "http.www_authenticate",
3774 FT_STRING, BASE_NONE, NULL, 0x0,
3775 "HTTP WWW-Authenticate header", HFILL }},
3776 { &hf_http_content_type,
3777 { "Content-Type", "http.content_type",
3778 FT_STRING, BASE_NONE, NULL, 0x0,
3779 "HTTP Content-Type header", HFILL }},
3780 { &hf_http_content_length_header,
3781 { "Content-Length", "http.content_length_header",
3782 FT_STRING, BASE_NONE, NULL, 0x0,
3783 "HTTP Content-Length header", HFILL }},
3784 { &hf_http_content_length,
3785 { "Content length", "http.content_length",
3786 FT_UINT64, BASE_DEC, NULL, 0x0,
3788 { &hf_http_content_encoding,
3789 { "Content-Encoding", "http.content_encoding",
3790 FT_STRING, BASE_NONE, NULL, 0x0,
3791 "HTTP Content-Encoding header", HFILL }},
3792 { &hf_http_transfer_encoding,
3793 { "Transfer-Encoding", "http.transfer_encoding",
3794 FT_STRING, BASE_NONE, NULL, 0x0,
3795 "HTTP Transfer-Encoding header", HFILL }},
3797 { "Upgrade", "http.upgrade",
3798 FT_STRING, BASE_NONE, NULL, 0x0,
3799 "HTTP Upgrade header", HFILL }},
3800 { &hf_http_user_agent,
3801 { "User-Agent", "http.user_agent",
3802 FT_STRING, BASE_NONE, NULL, 0x0,
3803 "HTTP User-Agent header", HFILL }},
3805 { "Host", "http.host",
3806 FT_STRING, BASE_NONE, NULL, 0x0,
3807 "HTTP Host", HFILL }},
3808 { &hf_http_connection,
3809 { "Connection", "http.connection",
3810 FT_STRING, BASE_NONE, NULL, 0x0,
3811 "HTTP Connection", HFILL }},
3813 { "Cookie", "http.cookie",
3814 FT_STRING, BASE_NONE, NULL, 0x0,
3815 "HTTP Cookie", HFILL }},
3816 { &hf_http_cookie_pair,
3817 { "Cookie pair", "http.cookie_pair",
3818 FT_STRING, BASE_NONE, NULL, 0x0,
3819 "A name/value HTTP cookie pair", HFILL }},
3821 { "Accept", "http.accept",
3822 FT_STRING, BASE_NONE, NULL, 0x0,
3823 "HTTP Accept", HFILL }},
3825 { "Referer", "http.referer",
3826 FT_STRING, BASE_NONE, NULL, 0x0,
3827 "HTTP Referer", HFILL }},
3828 { &hf_http_accept_language,
3829 { "Accept-Language", "http.accept_language",
3830 FT_STRING, BASE_NONE, NULL, 0x0,
3831 "HTTP Accept Language", HFILL }},
3832 { &hf_http_accept_encoding,
3833 { "Accept Encoding", "http.accept_encoding",
3834 FT_STRING, BASE_NONE, NULL, 0x0,
3835 "HTTP Accept Encoding", HFILL }},
3837 { "Date", "http.date",
3838 FT_STRING, BASE_NONE, NULL, 0x0,
3839 "HTTP Date", HFILL }},
3840 { &hf_http_cache_control,
3841 { "Cache-Control", "http.cache_control",
3842 FT_STRING, BASE_NONE, NULL, 0x0,
3843 "HTTP Cache Control", HFILL }},
3845 { "Server", "http.server",
3846 FT_STRING, BASE_NONE, NULL, 0x0,
3847 "HTTP Server", HFILL }},
3848 { &hf_http_location,
3849 { "Location", "http.location",
3850 FT_STRING, BASE_NONE, NULL, 0x0,
3851 "HTTP Location", HFILL }},
3852 { &hf_http_sec_websocket_accept,
3853 { "Sec-WebSocket-Accept", "http.sec_websocket_accept",
3854 FT_STRING, BASE_NONE, NULL, 0x0,
3856 { &hf_http_sec_websocket_extensions,
3857 { "Sec-WebSocket-Extensions", "http.sec_websocket_extensions",
3858 FT_STRING, BASE_NONE, NULL, 0x0,
3860 { &hf_http_sec_websocket_key,
3861 { "Sec-WebSocket-Key", "http.sec_websocket_key",
3862 FT_STRING, BASE_NONE, NULL, 0x0,
3864 { &hf_http_sec_websocket_protocol,
3865 { "Sec-WebSocket-Protocol", "http.sec_websocket_protocol",
3866 FT_STRING, BASE_NONE, NULL, 0x0,
3868 { &hf_http_sec_websocket_version,
3869 { "Sec-WebSocket-Version", "http.sec_websocket_version",
3870 FT_STRING, BASE_NONE, NULL, 0x0,
3872 { &hf_http_set_cookie,
3873 { "Set-Cookie", "http.set_cookie",
3874 FT_STRING, BASE_NONE, NULL, 0x0,
3875 "HTTP Set Cookie", HFILL }},
3876 { &hf_http_last_modified,
3877 { "Last-Modified", "http.last_modified",
3878 FT_STRING, BASE_NONE, NULL, 0x0,
3879 "HTTP Last Modified", HFILL }},
3880 { &hf_http_x_forwarded_for,
3881 { "X-Forwarded-For", "http.x_forwarded_for",
3882 FT_STRING, BASE_NONE, NULL, 0x0,
3883 "HTTP X-Forwarded-For", HFILL }},
3884 { &hf_http_request_in,
3885 { "Request in frame", "http.request_in",
3886 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0,
3887 "This packet is a response to the packet with this number", HFILL }},
3888 { &hf_http_response_in,
3889 { "Response in frame","http.response_in",
3890 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0,
3891 "This packet will be responded in the packet with this number", HFILL }},
3892 { &hf_http_next_request_in,
3893 { "Next request in frame", "http.next_request_in",
3894 FT_FRAMENUM, BASE_NONE, NULL, 0,
3895 "The next HTTP request starts in packet number", HFILL }},
3896 { &hf_http_next_response_in,
3897 { "Next response in frame","http.next_response_in",
3898 FT_FRAMENUM, BASE_NONE, NULL, 0,
3899 "The next HTTP response starts in packet number", HFILL }},
3900 { &hf_http_prev_request_in,
3901 { "Prev request in frame", "http.prev_request_in",
3902 FT_FRAMENUM, BASE_NONE, NULL, 0,
3903 "The previous HTTP request starts in packet number", HFILL }},
3904 { &hf_http_prev_response_in,
3905 { "Prev response in frame","http.prev_response_in",
3906 FT_FRAMENUM, BASE_NONE, NULL, 0,
3907 "The previous HTTP response starts in packet number", HFILL }},
3909 { "Time since request", "http.time",
3910 FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
3911 "Time since the request was sent", HFILL }},
3912 { &hf_http_chunked_trailer_part,
3913 { "trailer-part", "http.chunked_trailer_part",
3914 FT_STRING, BASE_NONE, NULL, 0,
3915 "Optional trailer in a chunked body", HFILL }},
3916 { &hf_http_chunk_boundary,
3917 { "Chunk boundary", "http.chunk_boundary",
3918 FT_BYTES, BASE_NONE, NULL, 0,
3920 { &hf_http_chunk_size,
3921 { "Chunk size", "http.chunk_size",
3922 FT_UINT32, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0,
3924 { &hf_http_file_data,
3925 { "File Data", "http.file_data",
3926 FT_STRING, STR_UNICODE, NULL, 0,
3928 { &hf_http_unknown_header,
3929 { "Unknown header", "http.unknown_header",
3930 FT_STRING, BASE_NONE, NULL, 0,
3933 static gint *ett[] = {
3938 &ett_http_request_path,
3939 &ett_http_request_query,
3940 &ett_http_chunked_response,
3941 &ett_http_chunk_data,
3942 &ett_http_encoded_entity,
3943 &ett_http_header_item
3946 static ei_register_info ei[] = {
3947 { &ei_http_chat, { "http.chat", PI_SEQUENCE, PI_CHAT, "Formatted text", EXPFILL }},
3948 { &ei_http_te_and_length, { "http.te_and_length", PI_MALFORMED, PI_WARN, "The Content-Length and Transfer-Encoding header must not be set together", EXPFILL }},
3949 { &ei_http_te_unknown, { "http.te_unknown", PI_UNDECODED, PI_WARN, "Unknown transfer coding name in Transfer-Encoding header", EXPFILL }},
3950 { &ei_http_subdissector_failed, { "http.subdissector_failed", PI_MALFORMED, PI_NOTE, "HTTP body subdissector failed, trying heuristic subdissector", EXPFILL }},
3951 { &ei_http_ssl_port, { "http.ssl_port", PI_SECURITY, PI_WARN, "Unencrypted HTTP protocol detected over encrypted port, could indicate a dangerous misconfiguration.", EXPFILL }},
3952 { &ei_http_leading_crlf, { "http.leading_crlf", PI_MALFORMED, PI_ERROR, "Leading CRLF previous message in the stream may have extra CRLF", EXPFILL }},
3953 { &ei_http_bad_header_name, { "http.bad_header_name", PI_PROTOCOL, PI_WARN, "Illegal characters found in header name", EXPFILL }},
3956 /* UAT for header fields */
3957 static uat_field_t custom_header_uat_fields[] = {
3958 UAT_FLD_CSTRING(header_fields, header_name, "Header name", "HTTP header name"),
3959 UAT_FLD_CSTRING(header_fields, header_desc, "Field desc", "Description of the value contained in the header"),
3963 module_t *http_module;
3964 expert_module_t* expert_http;
3967 proto_http = proto_register_protocol("Hypertext Transfer Protocol", "HTTP", "http");
3968 proto_ssdp = proto_register_protocol("Simple Service Discovery Protocol", "SSDP", "ssdp");
3970 proto_register_field_array(proto_http, hf, array_length(hf));
3971 proto_register_subtree_array(ett, array_length(ett));
3972 expert_http = expert_register_protocol(proto_http);
3973 expert_register_field_array(expert_http, ei, array_length(ei));
3975 http_handle = register_dissector("http", dissect_http, proto_http);
3976 http_tcp_handle = register_dissector("http-over-tcp", dissect_http_tcp, proto_http);
3977 http_tls_handle = register_dissector("http-over-tls", dissect_http_ssl, proto_http); /* RFC 2818 */
3978 http_sctp_handle = register_dissector("http-over-sctp", dissect_http_sctp, proto_http);
3980 http_module = prefs_register_protocol(proto_http, reinit_http);
3981 prefs_register_bool_preference(http_module, "desegment_headers",
3982 "Reassemble HTTP headers spanning multiple TCP segments",
3983 "Whether the HTTP dissector should reassemble headers "
3984 "of a request spanning multiple TCP segments. "
3985 "To use this option, you must also enable "
3986 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
3987 &http_desegment_headers);
3988 prefs_register_bool_preference(http_module, "desegment_body",
3989 "Reassemble HTTP bodies spanning multiple TCP segments",
3990 "Whether the HTTP dissector should use the "
3991 "\"Content-length:\" value, if present, to reassemble "
3992 "the body of a request spanning multiple TCP segments, "
3993 "and reassemble chunked data spanning multiple TCP segments. "
3994 "To use this option, you must also enable "
3995 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
3996 &http_desegment_body);
3997 prefs_register_bool_preference(http_module, "dechunk_body",
3998 "Reassemble chunked transfer-coded bodies",
3999 "Whether to reassemble bodies of entities that are transferred "
4000 "using the \"Transfer-Encoding: chunked\" method",
4001 &http_dechunk_body);
4003 prefs_register_bool_preference(http_module, "decompress_body",
4004 "Uncompress entity bodies",
4005 "Whether to uncompress entity bodies that are compressed "
4006 "using \"Content-Encoding: \"",
4007 &http_decompress_body);
4009 prefs_register_obsolete_preference(http_module, "tcp_alternate_port");
4011 range_convert_str(wmem_epan_scope(), &global_http_sctp_range, SCTP_DEFAULT_RANGE, 65535);
4012 prefs_register_range_preference(http_module, "sctp.port", "SCTP Ports",
4014 &global_http_sctp_range, 65535);
4016 range_convert_str(wmem_epan_scope(), &global_http_ssl_range, SSL_DEFAULT_RANGE, 65535);
4017 prefs_register_range_preference(http_module, "tls.port", "SSL/TLS Ports",
4018 "SSL/TLS Ports range",
4019 &global_http_ssl_range, 65535);
4021 headers_uat = uat_new("Custom HTTP Header Fields",
4022 sizeof(header_field_t),
4023 "custom_http_header_fields",
4027 /* specifies named fields, so affects dissection
4028 and the set of named fields */
4029 UAT_AFFECTS_DISSECTION|UAT_AFFECTS_FIELDS,
4031 header_fields_copy_cb,
4032 header_fields_update_cb,
4033 header_fields_free_cb,
4034 header_fields_post_update_cb,
4035 header_fields_reset_cb,
4036 custom_header_uat_fields
4039 prefs_register_uat_preference(http_module, "custom_http_header_fields", "Custom HTTP header fields",
4040 "A table to define custom HTTP header for which fields can be setup and used for filtering/data extraction etc.",
4044 * Dissectors shouldn't register themselves in this table;
4045 * instead, they should call "http_tcp_dissector_add()", and
4046 * we'll register the port number they specify as a port
4047 * for HTTP, and register them in our subdissector table.
4049 * This only works for protocols such as IPP that run over
4050 * HTTP on a specific non-HTTP port.
4052 port_subdissector_table = register_dissector_table("http.port",
4053 "TCP port for protocols using HTTP", proto_http, FT_UINT16, BASE_DEC);
4056 * Dissectors can register themselves in this table.
4057 * It's just "media_type", not "http.content_type", because
4058 * it's an Internet media type, usable by other protocols as well.
4060 media_type_subdissector_table =
4061 register_dissector_table("media_type",
4062 "Internet media type", proto_http, FT_STRING, BASE_NONE);
4065 * Maps the lowercase Upgrade header value.
4066 * https://tools.ietf.org/html/rfc7230#section-8.6
4068 upgrade_subdissector_table = register_dissector_table("http.upgrade", "HTTP Upgrade", proto_http, FT_STRING, BASE_NONE);
4071 * Heuristic dissectors SHOULD register themselves in
4072 * this table using the standard heur_dissector_add()
4075 heur_subdissector_list = register_heur_dissector_list("http", proto_http);
4078 * Register for tapping
4080 http_tap = register_tap("http"); /* HTTP statistics tap */
4081 http_follow_tap = register_tap("http_follow"); /* HTTP Follow tap */
4083 register_follow_stream(proto_http, "http_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter,
4084 tcp_port_to_display, follow_tvb_tap_listener);
4085 http_eo_tap = register_export_object(proto_http, http_eo_packet, NULL);
4089 * Called by dissectors for protocols that run atop HTTP/TCP.
4092 http_tcp_dissector_add(guint32 port, dissector_handle_t handle)
4095 * Register ourselves as the handler for that port number
4096 * over TCP. "Auto-preference" not needed
4098 dissector_add_uint("tcp.port", port, http_tcp_handle);
4101 * And register them in *our* table for that port.
4103 dissector_add_uint("http.port", port, handle);
4107 void http_tcp_dissector_delete(guint32 port)
4110 * Unregister ourselves as the handler for that port number
4111 * over TCP. "Auto-preference" not needed
4113 dissector_delete_uint("tcp.port", port, NULL);
4116 * And unregister them in *our* table for that port.
4118 dissector_delete_uint("http.port", port, NULL);
4122 http_tcp_port_add(guint32 port)
4125 * Register ourselves as the handler for that port number
4126 * over TCP. We rely on our caller having registered
4127 * themselves for the appropriate media type.
4128 * No "auto-preference" used.
4130 dissector_add_uint("tcp.port", port, http_tcp_handle);
4134 proto_reg_handoff_http(void)
4136 dissector_handle_t ssdp_handle;
4138 media_handle = find_dissector_add_dependency("media", proto_http);
4139 http2_handle = find_dissector("http2");
4141 * XXX - is there anything to dissect in the body of an SSDP
4142 * request or reply? I.e., should there be an SSDP dissector?
4144 ssdp_handle = create_dissector_handle(dissect_ssdp, proto_ssdp);
4145 dissector_add_uint_with_preference("udp.port", UDP_PORT_SSDP, ssdp_handle);
4148 * SSL/TLS Application-Layer Protocol Negotiation (ALPN) protocol
4151 dissector_add_string("tls.handshake.extensions_alpn_str", "http/1.1", http_tls_handle);
4153 ntlmssp_handle = find_dissector_add_dependency("ntlmssp", proto_http);
4154 gssapi_handle = find_dissector_add_dependency("gssapi", proto_http);
4155 sstp_handle = find_dissector_add_dependency("sstp", proto_http);
4157 stats_tree_register("http", "http", "HTTP/Packet Counter", 0, http_stats_tree_packet, http_stats_tree_init, NULL );
4158 stats_tree_register("http", "http_req", "HTTP/Requests", 0, http_req_stats_tree_packet, http_req_stats_tree_init, NULL );
4159 stats_tree_register("http", "http_srv", "HTTP/Load Distribution",0, http_reqs_stats_tree_packet, http_reqs_stats_tree_init, NULL );
4160 stats_tree_register("http", "http_seq", "HTTP/Request Sequences",0, http_seq_stats_tree_packet, http_seq_stats_tree_init, NULL );
4164 * Content-Type: message/http
4167 static gint proto_message_http = -1;
4168 static gint ett_message_http = -1;
4171 dissect_message_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
4173 proto_tree *subtree;
4175 gint offset = 0, next_offset;
4178 col_append_str(pinfo->cinfo, COL_INFO, " (message/http)");
4180 ti = proto_tree_add_item(tree, proto_message_http,
4181 tvb, 0, -1, ENC_NA);
4182 subtree = proto_item_add_subtree(ti, ett_message_http);
4183 while (tvb_offset_exists(tvb, offset)) {
4184 len = tvb_find_line_end(tvb, offset,
4185 tvb_ensure_captured_length_remaining(tvb, offset),
4186 &next_offset, FALSE);
4189 proto_tree_add_format_text(subtree, tvb, offset, len);
4190 offset = next_offset;
4193 return tvb_captured_length(tvb);
4197 proto_register_message_http(void)
4199 static gint *ett[] = {
4203 proto_message_http = proto_register_protocol(
4204 "Media Type: message/http",
4208 proto_register_subtree_array(ett, array_length(ett));
4212 proto_reg_handoff_message_http(void)
4214 dissector_handle_t message_http_handle;
4216 message_http_handle = create_dissector_handle(dissect_message_http,
4217 proto_message_http);
4219 dissector_add_string("media_type", "message/http", message_http_handle);
4221 heur_dissector_add("tcp", dissect_http_heur_tcp, "HTTP over TCP", "http_tcp", proto_http, HEURISTIC_ENABLE);
4223 proto_http2 = proto_get_id_by_filter_name("http2");
4225 dissector_add_uint_range_with_preference("tcp.port", TCP_DEFAULT_RANGE, http_tcp_handle);
4231 * Editor modelines - http://www.wireshark.org/tools/modelines.html
4236 * indent-tabs-mode: t
4239 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
4240 * :indentSize=8:tabSize=8:noTabs=false: