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/charsets.h>
31 #include <epan/strutil.h>
32 #include <epan/stats_tree.h>
33 #include <epan/to_str.h>
34 #include <epan/req_resp_hdrs.h>
35 #include <epan/proto_data.h>
36 #include <epan/export_object.h>
37 #include <epan/exceptions.h>
38 #include <epan/show_exception.h>
39 #include <epan/unit_strings.h>
41 #include "packet-http.h"
42 #include "packet-http2.h"
43 #include "packet-tcp.h"
44 #include "packet-tls.h"
45 #include "packet-acdr.h"
46 #include "packet-media-type.h"
48 #include <ui/tap-credentials.h>
50 void proto_register_http(void);
51 void proto_reg_handoff_http(void);
52 void proto_register_message_http(void);
53 void proto_reg_handoff_message_http(void);
56 static int http_eo_tap
;
57 static int http_follow_tap
;
58 static int credentials_tap
;
60 static int proto_http
;
61 static int proto_http2
;
62 static int proto_ssdp
;
63 static int hf_http_notification
;
64 static int hf_http_response
;
65 static int hf_http_request
;
66 static int hf_http_response_line
;
67 static int hf_http_request_line
;
68 static int hf_http_basic
;
69 static int hf_http_citrix
;
70 static int hf_http_citrix_user
;
71 static int hf_http_citrix_domain
;
72 static int hf_http_citrix_passwd
;
73 static int hf_http_citrix_session
;
74 static int hf_http_request_method
;
75 static int hf_http_request_uri
;
76 static int hf_http_request_full_uri
;
77 static int hf_http_request_path
;
78 static int hf_http_request_path_segment
;
79 static int hf_http_request_query
;
80 static int hf_http_request_query_parameter
;
81 static int hf_http_request_version
;
82 static int hf_http_response_version
;
83 static int hf_http_response_code
;
84 static int hf_http_response_code_desc
;
85 static int hf_http_response_phrase
;
86 static int hf_http_authorization
;
87 static int hf_http_proxy_authenticate
;
88 static int hf_http_proxy_authorization
;
89 static int hf_http_proxy_connect_host
;
90 static int hf_http_proxy_connect_port
;
91 static int hf_http_www_authenticate
;
92 static int hf_http_content_type
;
93 static int hf_http_content_length_header
;
94 static int hf_http_content_length
;
95 static int hf_http_content_encoding
;
96 static int hf_http_transfer_encoding
;
97 static int hf_http_upgrade
;
98 static int hf_http_user_agent
;
99 static int hf_http_host
;
100 static int hf_http_range
;
101 static int hf_http_content_range
;
102 static int hf_http_connection
;
103 static int hf_http_cookie
;
104 static int hf_http_cookie_pair
;
105 static int hf_http_accept
;
106 static int hf_http_referer
;
107 static int hf_http_accept_language
;
108 static int hf_http_accept_encoding
;
109 static int hf_http_date
;
110 static int hf_http_cache_control
;
111 static int hf_http_server
;
112 static int hf_http_location
;
113 static int hf_http_sec_websocket_accept
;
114 static int hf_http_sec_websocket_extensions
;
115 static int hf_http_sec_websocket_key
;
116 static int hf_http_sec_websocket_protocol
;
117 static int hf_http_sec_websocket_version
;
118 static int hf_http_set_cookie
;
119 static int hf_http_last_modified
;
120 static int hf_http_x_forwarded_for
;
121 static int hf_http_http2_settings
;
122 static int hf_http_request_in
;
123 static int hf_http_response_in
;
124 /*static int hf_http_next_request_in;
125 static int hf_http_next_response_in;
126 static int hf_http_prev_request_in;
127 static int hf_http_prev_response_in; */
128 static int hf_http_time
;
129 static int hf_http_chunk_size
;
130 static int hf_http_chunk_data
;
131 static int hf_http_chunk_boundary
;
132 static int hf_http_chunked_trailer_part
;
133 static int hf_http_file_data
;
134 static int hf_http_unknown_header
;
135 static int hf_http_http2_settings_uri
;
138 static int ett_http_ntlmssp
;
139 static int ett_http_kerberos
;
140 static int ett_http_request
;
141 static int ett_http_request_uri
;
142 static int ett_http_request_path
;
143 static int ett_http_request_query
;
144 static int ett_http_chunked_response
;
145 static int ett_http_chunk_data
;
146 static int ett_http_encoded_entity
;
147 static int ett_http_header_item
;
148 static int ett_http_http2_settings_item
;
150 static expert_field ei_http_te_and_length
;
151 static expert_field ei_http_te_unknown
;
152 static expert_field ei_http_subdissector_failed
;
153 static expert_field ei_http_tls_port
;
154 static expert_field ei_http_leading_crlf
;
155 static expert_field ei_http_excess_data
;
156 static expert_field ei_http_bad_header_name
;
157 static expert_field ei_http_decompression_failed
;
158 static expert_field ei_http_decompression_disabled
;
160 static dissector_handle_t http_handle
;
161 static dissector_handle_t http_tcp_handle
;
162 static dissector_handle_t http_tls_handle
;
163 static dissector_handle_t http_sctp_handle
;
165 static dissector_handle_t media_handle
;
166 static dissector_handle_t http2_handle
;
167 static dissector_handle_t sstp_handle
;
168 static dissector_handle_t ntlmssp_handle
;
169 static dissector_handle_t gssapi_handle
;
171 /* RFC 3986 Ch 2.2 Reserved characters*/
172 /* patterns used for tvb_ws_mempbrk_pattern_uint8 */
173 static ws_mempbrk_pattern pbrk_gen_delims
;
174 static ws_mempbrk_pattern pbrk_sub_delims
;
176 /* reassembly table for streaming chunk mode */
177 static reassembly_table http_streaming_reassembly_table
;
179 REASSEMBLE_ITEMS_DEFINE(http_body
, "HTTP Chunked Body");
181 /* HTTP chunk virtual frame number (similar to HTTP2 frame num) */
182 #define get_http_chunk_frame_num get_virtual_frame_num64
184 /* Stuff for generation/handling of fields for custom HTTP headers */
185 typedef struct _header_field_t
{
190 static header_field_t
* header_fields
;
191 static unsigned num_header_fields
;
193 static GHashTable
* header_fields_hash
;
194 static hf_register_info
* dynamic_hf
;
195 static unsigned dynamic_hf_size
;
198 header_fields_update_cb(void *r
, char **err
)
200 header_field_t
*rec
= (header_field_t
*)r
;
203 if (rec
->header_name
== NULL
) {
204 *err
= g_strdup("Header name can't be empty");
208 g_strstrip(rec
->header_name
);
209 if (rec
->header_name
[0] == 0) {
210 *err
= g_strdup("Header name can't be empty");
214 /* Check for invalid characters (to avoid asserting out when
215 * registering the field).
217 c
= proto_check_field_name(rec
->header_name
);
219 *err
= ws_strdup_printf("Header name can't contain '%c'", c
);
228 header_fields_copy_cb(void* n
, const void* o
, size_t siz _U_
)
230 header_field_t
* new_rec
= (header_field_t
*)n
;
231 const header_field_t
* old_rec
= (const header_field_t
*)o
;
233 new_rec
->header_name
= g_strdup(old_rec
->header_name
);
234 new_rec
->header_desc
= g_strdup(old_rec
->header_desc
);
240 header_fields_free_cb(void*r
)
242 header_field_t
* rec
= (header_field_t
*)r
;
244 g_free(rec
->header_name
);
245 g_free(rec
->header_desc
);
248 UAT_CSTRING_CB_DEF(header_fields
, header_name
, header_field_t
)
249 UAT_CSTRING_CB_DEF(header_fields
, header_desc
, header_field_t
)
252 * desegmentation of HTTP headers
253 * (when we are over TCP or another protocol providing the desegmentation API)
255 static bool http_desegment_headers
= true;
258 * desegmentation of HTTP bodies
259 * (when we are over TCP or another protocol providing the desegmentation API)
260 * TODO let the user filter on content-type the bodies he wants desegmented
262 static bool http_desegment_body
= true;
265 * De-chunking of content-encoding: chunk entity bodies.
267 static bool http_dechunk_body
= true;
270 * Decompression of compressed content-encoded entities.
272 static bool http_decompress_body
= true;
275 * Extra checks for valid ASCII data in HTTP headers.
277 static bool http_check_ascii_headers
= false;
279 /* Simple Service Discovery Protocol
280 * SSDP is implemented atop HTTP (yes, it really *does* run over UDP).
281 * SSDP is the discovery protocol of Universal Plug and Play
282 * UPnP http://www.upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf
284 #define TCP_PORT_SSDP 1900
285 #define UDP_PORT_SSDP 1900
290 * 2710 is the XBT BitTorrent tracker
293 #define TCP_DEFAULT_RANGE "80,3128,3132,5985,8080,8088,11371,1900,2869,2710"
294 #define SCTP_DEFAULT_RANGE "80"
295 #define TLS_DEFAULT_RANGE "443"
297 static range_t
*global_http_tls_range
;
299 static range_t
*http_tcp_range
;
300 static range_t
*http_sctp_range
;
301 static range_t
*http_tls_range
;
303 typedef void (*ReqRespDissector
)(packet_info
*, tvbuff_t
*, proto_tree
*, int, const unsigned char*,
304 const unsigned char*, http_conv_t
*, http_req_res_t
*);
307 * Transfer codings from
308 * https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#transfer-coding
309 * Note: chunked encoding is handled separately.
311 typedef enum _http_transfer_coding
{
312 HTTP_TE_NONE
, /* Dummy value for header which is not set */
313 /* HTTP_TE_CHUNKED, */
318 HTTP_TE_UNKNOWN
, /* Header was set, but no valid name was found */
319 } http_transfer_coding
;
322 * Structure holding information from headers needed by main
323 * HTTP dissector code.
327 char *content_type_parameters
;
328 bool have_content_length
;
329 int64_t content_length
;
330 char *content_encoding
;
331 bool transfer_encoding_chunked
;
332 http_transfer_coding transfer_encoding
;
336 /* request or response streaming reassembly data */
338 /* reassembly information only for request or response with chunked and streaming data */
339 streaming_reassembly_info_t
* streaming_reassembly_info
;
340 /* subdissector handler for request or response with chunked and streaming data */
341 dissector_handle_t streaming_handle
;
342 /* message being passed to subdissector if the request or response has chunked and streaming data */
343 media_content_info_t
* content_info
;
344 headers_t
* main_headers
;
345 } http_streaming_reassembly_data_t
;
347 /* http request or response private data */
349 /* direction of request message */
351 /* request or response streaming reassembly data */
352 http_streaming_reassembly_data_t
* req_streaming_reassembly_data
;
353 http_streaming_reassembly_data_t
* res_streaming_reassembly_data
;
354 } http_req_res_private_data_t
;
356 typedef struct _request_trans_t
{
357 uint64_t first_range_num
;
363 typedef struct _match_trans_t
{
371 static int parse_http_status_code(const unsigned char *line
, const unsigned char *lineend
);
372 static int is_http_request_or_reply(packet_info
*pinfo
, const char *data
, int linelen
,
373 media_container_type_t
*type
, ReqRespDissector
374 *reqresp_dissector
, http_conv_t
*conv_data
);
375 static unsigned chunked_encoding_dissector(tvbuff_t
**tvb_ptr
, packet_info
*pinfo
,
376 proto_tree
*tree
, int offset
);
377 static bool valid_header_name(const unsigned char *line
, int header_len
);
378 static bool process_header(tvbuff_t
*tvb
, int offset
, int next_offset
,
379 const unsigned char *line
, int linelen
, int colon_offset
,
380 packet_info
*pinfo
, proto_tree
*tree
,
381 headers_t
*eh_ptr
, http_conv_t
*conv_data
,
382 media_container_type_t http_type
, wmem_map_t
*header_value_map
, bool streaming_chunk_mode
);
383 static int find_header_hf_value(tvbuff_t
*tvb
, int offset
, unsigned header_len
);
384 static bool check_auth_ntlmssp(proto_item
*hdr_item
, tvbuff_t
*tvb
,
385 packet_info
*pinfo
, char *value
);
386 static bool check_auth_basic(proto_item
*hdr_item
, tvbuff_t
*tvb
,
387 packet_info
*pinfo
, char *value
);
388 static bool check_auth_digest(proto_item
* hdr_item
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
, char* value
, int offset
, int len
);
389 static bool check_auth_citrixbasic(proto_item
*hdr_item
, tvbuff_t
*tvb
, packet_info
*pinfo
,
390 char *value
, int offset
);
391 static bool check_auth_kerberos(proto_item
*hdr_item
, tvbuff_t
*tvb
,
392 packet_info
*pinfo
, const char *value
);
394 static dissector_table_t port_subdissector_table
;
395 static dissector_table_t media_type_subdissector_table
;
396 static dissector_table_t streaming_content_type_dissector_table
;
397 static dissector_table_t upgrade_subdissector_table
;
398 static heur_dissector_list_t heur_subdissector_list
;
400 static tap_packet_status
401 http_eo_packet(void *tapdata
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *data
, tap_flags_t flags _U_
)
403 export_object_list_t
*object_list
= (export_object_list_t
*)tapdata
;
404 const http_eo_t
*eo_info
= (const http_eo_t
*)data
;
405 export_object_entry_t
*entry
;
407 if(eo_info
) { /* We have data waiting for us */
408 /* These values will be freed when the Export Object window
410 entry
= g_new(export_object_entry_t
, 1);
412 entry
->pkt_num
= pinfo
->num
;
413 /* XXX: Should this remove the port, if any? It's only
414 * for display, so probably not. */
415 entry
->hostname
= g_strdup(eo_info
->hostname
);
416 entry
->content_type
= g_strdup(eo_info
->content_type
);
417 /* XXX: Should this remove the query portion, if any, from
418 * the path? (Or should that be done in the dissector?) */
419 entry
->filename
= eo_info
->filename
? g_path_get_basename(eo_info
->filename
) : NULL
;
420 entry
->payload_len
= tvb_captured_length(eo_info
->payload
);
421 entry
->payload_data
= (uint8_t *)tvb_memdup(NULL
, eo_info
->payload
, 0, entry
->payload_len
);
423 object_list
->add_entry(object_list
->gui_data
, entry
);
425 return TAP_PACKET_REDRAW
; /* State changed - window should be redrawn */
427 return TAP_PACKET_DONT_REDRAW
; /* State unchanged - no window updates needed */
431 /* --- HTTP Status Codes */
432 /* Note: The reference for uncommented entries is RFC 2616 */
433 const value_string vals_http_status_code
[] = {
435 { 101, "Switching Protocols" },
436 { 102, "Processing" }, /* RFC 2518 */
437 { 103, "Early Hints" }, /* RFC-ietf-httpbis-early-hints-05 */
438 { 199, "Informational - Others" },
443 { 203, "Non-authoritative Information"},
444 { 204, "No Content"},
445 { 205, "Reset Content"},
446 { 206, "Partial Content"},
447 { 207, "Multi-Status"}, /* RFC 4918 */
448 { 208, "Already Reported"}, /* RFC 5842 */
449 { 226, "IM Used"}, /* RFC 3229 */
450 { 299, "Success - Others"},
452 { 300, "Multiple Choices"},
453 { 301, "Moved Permanently"},
456 { 304, "Not Modified"},
458 { 307, "Temporary Redirect"},
459 { 308, "Permanent Redirect"}, /* RFC 7538 */
460 { 399, "Redirection - Others"},
462 { 400, "Bad Request"},
463 { 401, "Unauthorized"},
464 { 402, "Payment Required"},
467 { 405, "Method Not Allowed"},
468 { 406, "Not Acceptable"},
469 { 407, "Proxy Authentication Required"},
470 { 408, "Request Time-out"},
473 { 411, "Length Required"},
474 { 412, "Precondition Failed"},
475 { 413, "Request Entity Too Large"},
476 { 414, "Request-URI Too Long"},
477 { 415, "Unsupported Media Type"},
478 { 416, "Requested Range Not Satisfiable"},
479 { 417, "Expectation Failed"},
480 { 418, "I'm a teapot"}, /* RFC 2324 */
481 { 421, "Misdirected Request"}, /* RFC 7540 */
482 { 422, "Unprocessable Entity"}, /* RFC 4918 */
483 { 423, "Locked"}, /* RFC 4918 */
484 { 424, "Failed Dependency"}, /* RFC 4918 */
485 { 425, "Too Early"}, /* RFC 8470 */
486 { 426, "Upgrade Required"}, /* RFC 2817 */
487 { 428, "Precondition Required"}, /* RFC 6585 */
488 { 429, "Too Many Requests"}, /* RFC 6585 */
489 { 431, "Request Header Fields Too Large"}, /* RFC 6585 */
490 { 451, "Unavailable For Legal Reasons"}, /* RFC 7725 */
491 { 499, "Client Error - Others"},
493 { 500, "Internal Server Error"},
494 { 501, "Not Implemented"},
495 { 502, "Bad Gateway"},
496 { 503, "Service Unavailable"},
497 { 504, "Gateway Time-out"},
498 { 505, "HTTP Version not supported"},
499 { 506, "Variant Also Negotiates"}, /* RFC 2295 */
500 { 507, "Insufficient Storage"}, /* RFC 4918 */
501 { 508, "Loop Detected"}, /* RFC 5842 */
502 { 510, "Not Extended"}, /* RFC 2774 */
503 { 511, "Network Authentication Required"}, /* RFC 6585 */
504 { 599, "Server Error - Others"},
509 static const char* st_str_reqs
= "HTTP Requests by Server";
510 static const char* st_str_reqs_by_srv_addr
= "HTTP Requests by Server Address";
511 static const char* st_str_reqs_by_http_host
= "HTTP Requests by HTTP Host";
512 static const char* st_str_resps_by_srv_addr
= "HTTP Responses by Server Address";
514 static int st_node_reqs
= -1;
515 static int st_node_reqs_by_srv_addr
= -1;
516 static int st_node_reqs_by_http_host
= -1;
517 static int st_node_resps_by_srv_addr
= -1;
519 /* Parse HTTP path sub components RFC3986 Ch 3.3, 3.4 */
521 http_add_path_components_to_tree(tvbuff_t
* tvb
, packet_info
* pinfo _U_
, proto_item
* item
, int offset
, int length
)
524 proto_tree
* uri_tree
;
525 int end_offset
, end_path_offset
, query_offset
, path_len
, query_len
, parameter_offset
;
526 end_offset
= offset
+ length
;
527 /* The Content-Location (and Referer) headers in HTTP 1.1, and the
528 * :path header in HTTP/2 can be an absolute-URI or a partial-URI;
529 * i.e. that they can include a path and a query, but not a fragment.
530 * RFC 7230 2.7 Uniform Request Identifiers, RFC 7231 Appendices C and D,
531 * RFC 7540 8.1.2.3. Request Pseudo-Header Fields
532 * Look for a ? to mark a query.
534 query_offset
= tvb_find_uint8(tvb
, offset
, length
, '?');
535 end_path_offset
= (query_offset
== -1) ? end_offset
: query_offset
;
536 parameter_offset
= tvb_ws_mempbrk_pattern_uint8(tvb
, offset
+ 1, end_path_offset
- offset
- 1, &pbrk_sub_delims
, NULL
);
537 if (query_offset
== -1 && parameter_offset
== -1) {
538 /* Nothing interesting, no need to split. */
541 uri_tree
= proto_item_add_subtree(item
, ett_http_request_uri
);
542 path_len
= end_path_offset
- offset
;
543 proto_tree_add_item(uri_tree
, hf_http_request_path
, tvb
, offset
, path_len
, ENC_ASCII
);
544 parameter_offset
= tvb_ws_mempbrk_pattern_uint8(tvb
, offset
+ 1, end_path_offset
- offset
- 1, &pbrk_sub_delims
, NULL
);
545 if (parameter_offset
!= -1) {
546 proto_tree
* path_tree
= proto_item_add_subtree(item
, ett_http_request_path
);
547 while (offset
< end_path_offset
) {
548 parameter_offset
= tvb_ws_mempbrk_pattern_uint8(tvb
, offset
+ 1, end_path_offset
- offset
- 1, &pbrk_sub_delims
, NULL
);
549 if (parameter_offset
== -1) {
550 parameter_offset
= end_path_offset
;
552 proto_tree_add_item(path_tree
, hf_http_request_path_segment
, tvb
, offset
, parameter_offset
- offset
, ENC_ASCII
);
553 offset
= parameter_offset
+ 1;
556 if (query_offset
== -1) {
559 /* Skip past the delimiter. */
561 query_len
= end_offset
- query_offset
;
562 offset
= query_offset
;
563 ti
= proto_tree_add_item(uri_tree
, hf_http_request_query
, tvb
, query_offset
, query_len
, ENC_ASCII
);
564 proto_tree
*query_tree
= proto_item_add_subtree(ti
, ett_http_request_query
);
565 while (offset
< end_offset
) {
566 parameter_offset
= tvb_ws_mempbrk_pattern_uint8(tvb
, offset
+ 1, end_offset
- offset
- 1, &pbrk_sub_delims
, NULL
);
567 if (parameter_offset
== -1) {
568 parameter_offset
= end_offset
;
570 proto_tree_add_item(query_tree
, hf_http_request_query_parameter
, tvb
, offset
, parameter_offset
- offset
, ENC_ASCII
);
571 offset
= parameter_offset
+ 1;
575 /* HTTP/Load Distribution stats init function */
577 http_reqs_stats_tree_init(stats_tree
* st
)
579 st_node_reqs
= stats_tree_create_node(st
, st_str_reqs
, 0, STAT_DT_INT
, true);
580 st_node_reqs_by_srv_addr
= stats_tree_create_node(st
, st_str_reqs_by_srv_addr
, st_node_reqs
, STAT_DT_INT
, true);
581 st_node_reqs_by_http_host
= stats_tree_create_node(st
, st_str_reqs_by_http_host
, st_node_reqs
, STAT_DT_INT
, true);
582 st_node_resps_by_srv_addr
= stats_tree_create_node(st
, st_str_resps_by_srv_addr
, 0, STAT_DT_INT
, true);
585 /* HTTP/Load Distribution stats packet function */
586 static tap_packet_status
587 http_reqs_stats_tree_packet(stats_tree
* st
, packet_info
* pinfo
, epan_dissect_t
* edt _U_
, const void* p
, tap_flags_t flags _U_
)
589 const http_info_value_t
* v
= (const http_info_value_t
*)p
;
590 int reqs_by_this_host
;
591 int reqs_by_this_addr
;
592 int resps_by_this_addr
;
593 int i
= v
->response_code
;
597 if (v
->request_method
) {
598 ip_str
= address_to_str(NULL
, &pinfo
->dst
);
600 tick_stat_node(st
, st_str_reqs
, 0, false);
601 tick_stat_node(st
, st_str_reqs_by_srv_addr
, st_node_reqs
, true);
602 tick_stat_node(st
, st_str_reqs_by_http_host
, st_node_reqs
, true);
603 reqs_by_this_addr
= tick_stat_node(st
, ip_str
, st_node_reqs_by_srv_addr
, true);
606 reqs_by_this_host
= tick_stat_node(st
, v
->http_host
, st_node_reqs_by_http_host
, true);
607 tick_stat_node(st
, ip_str
, reqs_by_this_host
, false);
609 tick_stat_node(st
, v
->http_host
, reqs_by_this_addr
, false);
612 wmem_free(NULL
, ip_str
);
614 return TAP_PACKET_REDRAW
;
617 ip_str
= address_to_str(NULL
, &pinfo
->src
);
619 tick_stat_node(st
, st_str_resps_by_srv_addr
, 0, false);
620 resps_by_this_addr
= tick_stat_node(st
, ip_str
, st_node_resps_by_srv_addr
, true);
622 if ( (i
>=100)&&(i
<400) ) {
623 tick_stat_node(st
, "OK", resps_by_this_addr
, false);
625 tick_stat_node(st
, "Error", resps_by_this_addr
, false);
628 wmem_free(NULL
, ip_str
);
630 return TAP_PACKET_REDRAW
;
633 return TAP_PACKET_DONT_REDRAW
;
637 static int st_node_requests_by_host
= -1;
638 static const char *st_str_requests_by_host
= "HTTP Requests by HTTP Host";
640 /* HTTP/Requests stats init function */
642 http_req_stats_tree_init(stats_tree
* st
)
644 st_node_requests_by_host
= stats_tree_create_node(st
, st_str_requests_by_host
, 0, STAT_DT_INT
, true);
647 /* HTTP/Requests stats packet function */
648 static tap_packet_status
649 http_req_stats_tree_packet(stats_tree
* st
, packet_info
* pinfo _U_
, epan_dissect_t
* edt _U_
, const void* p
, tap_flags_t flags _U_
)
651 const http_info_value_t
* v
= (const http_info_value_t
*)p
;
652 int reqs_by_this_host
;
654 if (v
->request_method
) {
655 tick_stat_node(st
, st_str_requests_by_host
, 0, false);
658 reqs_by_this_host
= tick_stat_node(st
, v
->http_host
, st_node_requests_by_host
, true);
660 if (v
->request_uri
) {
661 tick_stat_node(st
, v
->request_uri
, reqs_by_this_host
, true);
665 return TAP_PACKET_REDRAW
;
668 return TAP_PACKET_DONT_REDRAW
;
671 static const char *st_str_packets
= "Total HTTP Packets";
672 static const char *st_str_requests
= "HTTP Request Packets";
673 static const char *st_str_responses
= "HTTP Response Packets";
674 static const char *st_str_resp_broken
= "???: broken";
675 static const char *st_str_resp_100
= "1xx: Informational";
676 static const char *st_str_resp_200
= "2xx: Success";
677 static const char *st_str_resp_300
= "3xx: Redirection";
678 static const char *st_str_resp_400
= "4xx: Client Error";
679 static const char *st_str_resp_500
= "5xx: Server Error";
680 static const char *st_str_other
= "Other HTTP Packets";
682 static int st_node_packets
= -1;
683 static int st_node_requests
= -1;
684 static int st_node_responses
= -1;
685 static int st_node_resp_broken
= -1;
686 static int st_node_resp_100
= -1;
687 static int st_node_resp_200
= -1;
688 static int st_node_resp_300
= -1;
689 static int st_node_resp_400
= -1;
690 static int st_node_resp_500
= -1;
691 static int st_node_other
= -1;
694 /* HTTP/Packet Counter stats init function */
696 http_stats_tree_init(stats_tree
* st
)
698 st_node_packets
= stats_tree_create_node(st
, st_str_packets
, 0, STAT_DT_INT
, true);
699 st_node_requests
= stats_tree_create_pivot(st
, st_str_requests
, st_node_packets
);
700 st_node_responses
= stats_tree_create_node(st
, st_str_responses
, st_node_packets
, STAT_DT_INT
, true);
701 st_node_resp_broken
= stats_tree_create_node(st
, st_str_resp_broken
, st_node_responses
, STAT_DT_INT
, true);
702 st_node_resp_100
= stats_tree_create_node(st
, st_str_resp_100
, st_node_responses
, STAT_DT_INT
, true);
703 st_node_resp_200
= stats_tree_create_node(st
, st_str_resp_200
, st_node_responses
, STAT_DT_INT
, true);
704 st_node_resp_300
= stats_tree_create_node(st
, st_str_resp_300
, st_node_responses
, STAT_DT_INT
, true);
705 st_node_resp_400
= stats_tree_create_node(st
, st_str_resp_400
, st_node_responses
, STAT_DT_INT
, true);
706 st_node_resp_500
= stats_tree_create_node(st
, st_str_resp_500
, st_node_responses
, STAT_DT_INT
, true);
707 st_node_other
= stats_tree_create_node(st
, st_str_other
, st_node_packets
, STAT_DT_INT
, false);
710 /* HTTP/Packet Counter stats packet function */
711 static tap_packet_status
712 http_stats_tree_packet(stats_tree
* st
, packet_info
* pinfo _U_
, epan_dissect_t
* edt _U_
, const void* p
, tap_flags_t flags _U_
)
714 const http_info_value_t
* v
= (const http_info_value_t
*)p
;
715 unsigned i
= v
->response_code
;
717 const char *resp_str
;
720 tick_stat_node(st
, st_str_packets
, 0, false);
723 tick_stat_node(st
, st_str_responses
, st_node_packets
, false);
725 if ( (i
<100)||(i
>=600) ) {
726 resp_grp
= st_node_resp_broken
;
727 resp_str
= st_str_resp_broken
;
729 resp_grp
= st_node_resp_100
;
730 resp_str
= st_str_resp_100
;
732 resp_grp
= st_node_resp_200
;
733 resp_str
= st_str_resp_200
;
735 resp_grp
= st_node_resp_300
;
736 resp_str
= st_str_resp_300
;
738 resp_grp
= st_node_resp_400
;
739 resp_str
= st_str_resp_400
;
741 resp_grp
= st_node_resp_500
;
742 resp_str
= st_str_resp_500
;
745 tick_stat_node(st
, resp_str
, st_node_responses
, false);
747 snprintf(str
, sizeof(str
), "%u %s", i
,
748 val_to_str(i
, vals_http_status_code
, "Unknown (%d)"));
749 tick_stat_node(st
, str
, resp_grp
, false);
750 } else if (v
->request_method
) {
751 stats_tree_tick_pivot(st
,st_node_requests
,v
->request_method
);
753 tick_stat_node(st
, st_str_other
, st_node_packets
, false);
756 return TAP_PACKET_REDRAW
;
760 Generates a referer tree - a best-effort representation of which web request led to which.
763 A user can be forwarded to a single sites from multiple sources. For example,
764 google.com -> foo.com and bing.com -> foo.com. A URI alone is not unique.
766 Additionally, if a user has a subsequent request to foo.com -> bar.com, the
767 full chain could either be:
768 google.com -> foo.com -> bar.com, or
769 bing.com -> foo.com -> bar.com,
771 This indicates that a URI and its referer are not unique. Only a URI and its
772 full referer chain are unique. However, HTTP requests only contain the URI
773 and the immediate referer. This means that any attempt at generating a
774 referer tree is inherently going to be a best-effort approach.
776 This code assumes that the referer in a request is from the most-recent request
779 * To maintain readability of the statistics, whenever a site is visited, all
780 prior referers are 'ticked' as well, so that one can easily see the breakdown.
783 /* Root node for all referer statistics */
784 static int st_node_requests_by_referer
= -1;
785 /* Referer statistics root node's text */
786 static const char *st_str_request_sequences
= "HTTP Request Sequences";
788 /* Mapping of URIs to the most-recently seen node id */
789 static wmem_map_t
* refstats_uri_to_node_id_hash
;
790 /* Mapping of node ids to the node's URI ('name' value) */
791 static wmem_map_t
* refstats_node_id_to_uri_hash
;
792 /* Mapping of node ids to the parent node id */
793 static wmem_map_t
* refstats_node_id_to_parent_node_id_hash
;
796 /* HTTP/Request Sequences stats init function */
798 http_seq_stats_tree_init(stats_tree
* st
)
800 int root_node_id
= 0;
801 void *root_node_id_p
= GINT_TO_POINTER(root_node_id
);
802 void *node_id_p
= NULL
;
805 refstats_node_id_to_parent_node_id_hash
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
806 refstats_node_id_to_uri_hash
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
807 refstats_uri_to_node_id_hash
= wmem_map_new(wmem_file_scope(), wmem_str_hash
, g_str_equal
);
809 /* Add the root node and its mappings */
810 st_node_requests_by_referer
= stats_tree_create_node(st
, st_str_request_sequences
, root_node_id
, STAT_DT_INT
, true);
811 node_id_p
= GINT_TO_POINTER(st_node_requests_by_referer
);
812 uri
= wmem_strdup(wmem_file_scope(), st_str_request_sequences
);
814 wmem_map_insert(refstats_uri_to_node_id_hash
, uri
, node_id_p
);
815 wmem_map_insert(refstats_node_id_to_uri_hash
, node_id_p
, uri
);
816 wmem_map_insert(refstats_node_id_to_parent_node_id_hash
, node_id_p
, root_node_id_p
);
820 http_seq_stats_tick_referer(stats_tree
* st
, const char* arg_referer_uri
)
822 int root_node_id
= st_node_requests_by_referer
;
823 void *root_node_id_p
= GINT_TO_POINTER(st_node_requests_by_referer
);
825 void *referer_node_id_p
;
826 int referer_parent_node_id
;
827 void *referer_parent_node_id_p
;
830 /* Tick the referer's URI */
831 /* Does the node exist? */
832 if (!wmem_map_lookup_extended(refstats_uri_to_node_id_hash
, arg_referer_uri
, NULL
, &referer_node_id_p
)) {
833 /* The node for the referer didn't already exist, create the mappings */
834 referer_node_id
= tick_stat_node(st
, arg_referer_uri
, root_node_id
, true);
835 referer_node_id_p
= GINT_TO_POINTER(referer_node_id
);
836 referer_parent_node_id_p
= root_node_id_p
;
838 referer_uri
= wmem_strdup(wmem_file_scope(), arg_referer_uri
);
839 wmem_map_insert(refstats_uri_to_node_id_hash
, referer_uri
, referer_node_id_p
);
840 wmem_map_insert(refstats_node_id_to_uri_hash
, referer_node_id_p
, referer_uri
);
841 wmem_map_insert(refstats_node_id_to_parent_node_id_hash
, referer_node_id_p
, referer_parent_node_id_p
);
843 /* The node for the referer already exists, tick it */
844 referer_parent_node_id_p
= wmem_map_lookup(refstats_node_id_to_parent_node_id_hash
, referer_node_id_p
);
845 referer_parent_node_id
= GPOINTER_TO_INT(referer_parent_node_id_p
);
846 referer_node_id
= tick_stat_node(st
, arg_referer_uri
, referer_parent_node_id
, true);
848 return referer_node_id
;
852 http_seq_stats_tick_request(stats_tree
* st
, const char* arg_full_uri
, int referer_node_id
)
854 void *referer_node_id_p
= GINT_TO_POINTER(referer_node_id
);
859 node_id
= tick_stat_node(st
, arg_full_uri
, referer_node_id
, true);
860 node_id_p
= GINT_TO_POINTER(node_id
);
862 /* Update the mappings. Even if the URI was already seen, the URI->node mapping may need to be updated */
864 /* Is this a new node? */
865 uri
= (char *) wmem_map_lookup(refstats_node_id_to_uri_hash
, node_id_p
);
867 /* node not found, add mappings for the node and uri */
868 uri
= wmem_strdup(wmem_file_scope(), arg_full_uri
);
870 wmem_map_insert(refstats_uri_to_node_id_hash
, uri
, node_id_p
);
871 wmem_map_insert(refstats_node_id_to_uri_hash
, node_id_p
, uri
);
872 wmem_map_insert(refstats_node_id_to_parent_node_id_hash
, node_id_p
, referer_node_id_p
);
874 /* We've seen the node id before. Update the URI mapping refer to this node id*/
875 wmem_map_insert(refstats_uri_to_node_id_hash
, uri
, node_id_p
);
880 determine_http_location_target(wmem_allocator_t
*scope
, const char *base_url
, const char * location_url
)
882 /* Resolving a base URI + relative URI to an absolute URI ("Relative Resolution")
883 is complicated. Because of that, we take shortcuts that may result in
884 inaccurate results, but is also significantly simpler.
885 It would be best to use an external library to do this for us.
886 For reference, the RFC is located at https://tools.ietf.org/html/rfc3986#section-5.4
888 Returns NULL if the resolution fails
892 /* base_url must be an absolute URL.*/
893 if (strstr(base_url
, "://") == NULL
){
898 if (location_url
[0] == '\0') {
899 final_target
= wmem_strdup(scope
, base_url
);
902 /* Protocol Relative */
903 else if (g_str_has_prefix(location_url
, "//") ) {
904 char *base_scheme
= g_uri_parse_scheme(base_url
);
905 if (base_scheme
== NULL
) {
908 final_target
= wmem_strdup_printf(scope
, "%s:%s", base_scheme
, location_url
);
913 else if (strstr(location_url
, "://") != NULL
) {
914 final_target
= wmem_strdup(scope
, location_url
);
919 char *start_fragment
= strstr(base_url
, "#");
920 char *start_query
= NULL
;
921 char *base_url_no_fragment
= NULL
;
922 char *base_url_no_query
= NULL
;
924 /* Strip off the fragment (which should never be present)*/
925 if (start_fragment
== NULL
) {
926 base_url_no_fragment
= wmem_strdup(scope
, base_url
);
929 base_url_no_fragment
= wmem_strndup(scope
, base_url
, start_fragment
- base_url
);
932 /* Strip off the query (Queries are stripped from all relative URIs) */
933 start_query
= strstr(base_url_no_fragment
, "?");
934 if (start_query
== NULL
) {
935 base_url_no_query
= wmem_strdup(scope
, base_url_no_fragment
);
938 base_url_no_query
= wmem_strndup(scope
, base_url_no_fragment
, start_query
- base_url_no_fragment
);
941 /* A leading question mark (?) means to replace the old query with the new*/
942 if (g_str_has_prefix(location_url
, "?")) {
943 final_target
= wmem_strdup_printf(scope
, "%s%s", base_url_no_query
, location_url
);
946 /* A leading slash means to put the location after the netloc */
947 else if (g_str_has_prefix(location_url
, "/")) {
948 /* We have already tested strstr(base_url) above */
953 scheme_end
= strstr(base_url_no_query
, "://");
957 scheme_end
+= strlen("://");
958 if (!(*scheme_end
)) {
961 netloc_end
= strstr(scheme_end
, "/");
965 netloc_length
= (int) (netloc_end
- base_url_no_query
);
966 final_target
= wmem_strdup_printf(scope
, "%.*s%s", netloc_length
, base_url_no_query
, location_url
);
969 /* Otherwise, it replaces the last element in the URI */
971 char *scheme_end
= strstr(base_url_no_query
, "://") + 3;
972 char *end_of_path
= g_strrstr(scheme_end
, "/");
974 if (end_of_path
!= NULL
) {
975 int base_through_path
= (int) (end_of_path
- base_url_no_query
);
976 final_target
= wmem_strdup_printf(scope
, "%.*s/%s", base_through_path
, base_url_no_query
, location_url
);
979 final_target
= wmem_strdup_printf(scope
, "%s/%s", base_url_no_query
, location_url
);
988 /* HTTP/Request Sequences stats packet function */
989 static tap_packet_status
990 http_seq_stats_tree_packet(stats_tree
* st
, packet_info
* pinfo
, epan_dissect_t
* edt _U_
, const void* p
, tap_flags_t flags _U_
)
992 const http_info_value_t
* v
= (const http_info_value_t
*)p
;
994 /* Track HTTP Redirects */
995 if (v
->location_target
&& v
->location_base_uri
) {
998 void *parent_node_id_p
;
999 void *current_node_id_p
;
1002 char *absolute_target
= determine_http_location_target(pinfo
->pool
, v
->location_base_uri
, v
->location_target
);
1003 /* absolute_target is NULL if the resolution fails */
1004 if (absolute_target
!= NULL
) {
1005 /* We assume the user makes the request to the absolute_target */
1006 /* Tick the base URI */
1007 referer_node_id
= http_seq_stats_tick_referer(st
, v
->location_base_uri
);
1009 /* Tick the location header's resolved URI */
1010 http_seq_stats_tick_request(st
, absolute_target
, referer_node_id
);
1012 /* Tick all stats nodes above the location */
1013 current_node_id_p
= GINT_TO_POINTER(referer_node_id
);
1014 while (wmem_map_lookup_extended(refstats_node_id_to_parent_node_id_hash
, current_node_id_p
, NULL
, &parent_node_id_p
)) {
1015 parent_node_id
= GPOINTER_TO_INT(parent_node_id_p
);
1016 uri
= (char *) wmem_map_lookup(refstats_node_id_to_uri_hash
, current_node_id_p
);
1017 tick_stat_node(st
, uri
, parent_node_id
, true);
1018 current_node_id_p
= parent_node_id_p
;
1023 /* Track HTTP Requests/Referers */
1024 if (v
->request_method
&& v
->referer_uri
&& v
->full_uri
) {
1025 int referer_node_id
;
1027 void *parent_node_id_p
;
1028 void *current_node_id_p
;
1030 /* Tick the referer's URI */
1031 referer_node_id
= http_seq_stats_tick_referer(st
, v
->referer_uri
);
1033 /* Tick the request's URI */
1034 http_seq_stats_tick_request(st
, v
->full_uri
, referer_node_id
);
1036 /* Tick all stats nodes above the referer */
1037 current_node_id_p
= GINT_TO_POINTER(referer_node_id
);
1038 while (wmem_map_lookup_extended(refstats_node_id_to_parent_node_id_hash
, current_node_id_p
, NULL
, &parent_node_id_p
)) {
1039 parent_node_id
= GPOINTER_TO_INT(parent_node_id_p
);
1040 uri
= (char *) wmem_map_lookup(refstats_node_id_to_uri_hash
, current_node_id_p
);
1041 tick_stat_node(st
, uri
, parent_node_id
, true);
1042 current_node_id_p
= parent_node_id_p
;
1045 return TAP_PACKET_DONT_REDRAW
;
1050 dissect_http_ntlmssp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
1053 tvbuff_t
*ntlmssp_tvb
;
1055 ntlmssp_tvb
= base64_to_tvb(tvb
, line
);
1056 add_new_data_source(pinfo
, ntlmssp_tvb
, "NTLMSSP / GSSAPI Data");
1057 if (tvb_strneql(ntlmssp_tvb
, 0, "NTLMSSP", 7) == 0)
1058 call_dissector(ntlmssp_handle
, ntlmssp_tvb
, pinfo
, tree
);
1060 call_dissector(gssapi_handle
, ntlmssp_tvb
, pinfo
, tree
);
1064 dissect_http_kerberos(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
1067 tvbuff_t
*kerberos_tvb
;
1069 kerberos_tvb
= base64_to_tvb(tvb
, line
+ 9); /* skip 'Kerberos ' which is 9 chars */
1070 add_new_data_source(pinfo
, kerberos_tvb
, "Kerberos Data");
1071 call_dissector(gssapi_handle
, kerberos_tvb
, pinfo
, tree
);
1076 static http_conv_t
*
1077 get_http_conversation_data(packet_info
*pinfo
, conversation_t
**conversation
)
1079 http_conv_t
*conv_data
;
1081 *conversation
= find_or_create_conversation(pinfo
);
1083 /* Retrieve information from conversation
1084 * or add it if it isn't there yet
1086 conv_data
= (http_conv_t
*)conversation_get_proto_data(*conversation
, proto_http
);
1088 /* Setup the conversation structure itself */
1089 conv_data
= wmem_new0(wmem_file_scope(), http_conv_t
);
1090 conv_data
->chunk_offsets_fwd
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
1091 conv_data
->chunk_offsets_rev
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
1092 conv_data
->req_list
= NULL
;
1093 conv_data
->matches_table
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
1095 conversation_add_proto_data(*conversation
, proto_http
,
1103 * create a new http_req_res_t and add it to the conversation.
1104 * @return the new allocated object which is already added to the linked list
1106 static http_req_res_t
*
1107 push_req_res(http_conv_t
*conv_data
)
1109 http_req_res_t
*req_res
= wmem_new0(wmem_file_scope(), http_req_res_t
);
1111 nstime_set_unset(&(req_res
->req_ts
));
1112 conv_data
->req_res_tail
= req_res
;
1113 req_res
->private_data
= wmem_new0(wmem_file_scope(), http_req_res_private_data_t
);
1119 * push a request frame number and its time stamp to the conversation data.
1121 static http_req_res_t
*
1122 push_req(http_conv_t
*conv_data
, packet_info
*pinfo
)
1124 /* a request will always create a new http_req_res_t object */
1125 http_req_res_t
*req_res
= push_req_res(conv_data
);
1127 req_res
->req_framenum
= pinfo
->num
;
1128 req_res
->req_ts
= pinfo
->abs_ts
;
1130 /* XXX: Using the same proto key for the frame doesn't work well
1131 * with HTTP 1.1 pipelining, or other situations where more
1132 * than one request can appear in a frame.
1134 p_add_proto_data(wmem_file_scope(), pinfo
, proto_http
, HTTP_PROTO_DATA_REQRES
, req_res
);
1140 * push a response frame number to the conversation data.
1142 static http_req_res_t
*
1143 push_res(http_conv_t
*conv_data
, packet_info
*pinfo
)
1145 /* a response will create a new http_req_res_t object: if no
1146 object exists, or if the most recent one is already for
1147 a different response. (Exception: If the previous response
1148 code was in the Informational 1xx category, then it was
1149 an interim response, and this response could be for the same
1150 request.) In both cases the corresponding request was not
1151 detected/included in the conversation. In all other cases
1152 the http_req_res_t object created by the request is
1154 /* XXX: This finds the only most recent request and doesn't support
1155 * HTTP 1.1 pipelining. This limitation has been addressed for
1156 * HTTP GETS if Range Requests are supported.
1158 http_req_res_t
*req_res
= conv_data
->req_res_tail
;
1159 if (!req_res
|| (req_res
->res_framenum
> 0 && req_res
->response_code
>= 200)) {
1160 req_res
= push_req_res(conv_data
);
1162 req_res
->res_framenum
= pinfo
->num
;
1163 /* XXX: Using the same proto key for the frame doesn't work well
1164 * with HTTP 1.1 pipelining, or other situations where more
1165 * than one request can appear in a frame and multiple outstanding
1166 * GET requests. The latter has been addressed with matches_table."
1168 p_add_proto_data(wmem_file_scope(), pinfo
, proto_http
, HTTP_PROTO_DATA_REQRES
, req_res
);
1174 dissect_http_message(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
,
1175 proto_tree
*tree
, http_conv_t
*conv_data
,
1176 const char* proto_tag
, int proto
, bool end_of_stream
,
1177 const uint32_t* const seq
)
1179 proto_tree
*http_tree
= NULL
;
1180 proto_item
*ti
= NULL
;
1181 proto_item
*hidden_item
;
1182 const unsigned char *line
, *firstline
;
1184 const unsigned char *linep
, *lineend
;
1185 int orig_offset
= offset
;
1186 int first_linelen
, linelen
;
1187 bool is_request_or_reply
, is_tls
= false;
1188 bool saw_req_resp_or_header
;
1189 media_container_type_t http_type
;
1190 proto_item
*hdr_item
= NULL
;
1191 ReqRespDissector reqresp_dissector
;
1192 proto_tree
*req_tree
;
1194 headers_t
*headers
= NULL
;
1196 int reported_datalen
= -1;
1197 dissector_handle_t handle
= NULL
;
1198 bool dissected
= false;
1199 bool first_loop
= true;
1200 bool have_seen_http
= false;
1202 /*http_info_value_t *si;*/
1204 heur_dtbl_entry_t
*hdtbl_entry
;
1205 int reported_length
;
1207 bool leading_crlf
= false;
1208 bool excess_data
= false;
1209 media_content_info_t
* content_info
= NULL
;
1210 wmem_map_t
* header_value_map
= NULL
;
1211 int chunk_offset
= 0;
1212 wmem_map_t
*chunk_map
= NULL
;
1214 * For supporting dissecting chunked data in streaming reassembly mode.
1216 * If a HTTP request or response is chunked encoding (the transfer-encoding
1217 * header is 'chunked') and its content-type matching a subdissector in
1218 * "streaming_content_type" dissector table, then we switch to dissect in
1219 * streaming chunk mode. In streaming chunk mode, we dissect the data as soon
1220 * as possible, unlike normal mode, we don't start reassembling until the end
1221 * of the request or response message or at the end of the TCP stream. In
1222 * streaming chunk mode, the first reassembled PDU contains HTTP headers
1223 * and at least one completed chunk of this request or response message. And
1224 * subsequent PDUs consist of one or more chunks:
1226 * ----- +-- Reassembled Streaming Content PDU(s) --+-- Reassembled Streaming Content PDU(s) --+--- Reassembled ...
1227 * HLProtos | 1*high-proto-pdu | 1*high-proto-pdu | 1*high-proto-pdu
1228 * ----- +-------------------------------------+----+--------------------------------+---------+-------------------
1229 * | de-chunked-data | de-chunked-data | de-chunked-data
1230 * HTTP +-------- First Reassembled HTTP PDU -----------+--- Second Reassembled HTTP PDU -----+- Third PDU -+ +- Fourth ---
1231 * | headers and 1*chunk | 1*chunk | 1*chunk | | 1*chunk ...
1232 * ----- +--------- TCP segment ---------+ +-----------TCP segment -----------+ +---- TCP segment ---------+ +------------
1233 * TCP | headers | *chunk | part-chunk | | part-chunk | *chunk | part-chunk | | part-chunk | 1*chunk | | 1*chunk ...
1234 * ----- +---------+--------+------------+ +------------+--------+------------+ +------------+-------------+ +------------
1237 * - headers HTTP headers of a request or response message.
1238 * - part-chunk The front or rear part of a HTTP chunk.
1239 * - *chunk Zero or more completed HTTP chunks of a HTTP message.
1240 * - 1*chunk One or more completed HTTP chunks of a HTTP message.
1241 * - de-chunked-data De-chunked HTTP body data based on one or more completed chunks.
1242 * - 1*high-proto-pdu One or more high level protocol (on top of HTTP) PDUs.
1243 * - HLProtos High Level Protocols like GRPC-Web.
1245 * The headers and content_info of the req_res are allocated in file scope that
1246 * helps to provide information for dissecting subsequent PDUs which only
1247 * contains chunks without headers.
1249 bool streaming_chunk_mode
= false;
1250 bool begin_with_chunk
= false;
1251 http_streaming_reassembly_data_t
* streaming_reassembly_data
= NULL
;
1253 http_req_res_t
*curr
= (http_req_res_t
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_http
, HTTP_PROTO_DATA_REQRES
);
1254 http_info_value_t
*stat_info
= NULL
;
1255 http_req_res_private_data_t
* prv_data
= curr
? (http_req_res_private_data_t
*)curr
->private_data
: NULL
;
1256 http_req_res_private_data_t
* tail_prv_data
= NULL
;
1258 /* Determine the direction as in the TCP dissector, but don't call
1259 * get_tcp_conversation_data because we don't want to create a new
1260 * TCP stream if it doesn't exist (e.g., SSDP over UDP.)
1262 int direction
= cmp_address(&pinfo
->src
, &pinfo
->dst
);
1263 /* if the addresses are equal, match the ports instead */
1264 if (direction
== 0) {
1265 direction
= (pinfo
->srcport
> pinfo
->destport
) ? 1 : -1;
1267 if (direction
>= 0) {
1268 chunk_map
= conv_data
->chunk_offsets_fwd
;
1270 chunk_map
= conv_data
->chunk_offsets_rev
;
1273 if (seq
&& chunk_map
) {
1274 chunk_offset
= GPOINTER_TO_INT(wmem_map_lookup(chunk_map
, GUINT_TO_POINTER(*seq
)));
1275 /* Returns 0 when there is no entry in the map, as we want. */
1278 reported_length
= tvb_reported_length_remaining(tvb
, offset
);
1279 if (reported_length
< 1) {
1284 * In the interest of robustness, servers SHOULD ignore any empty
1285 * line(s) received where a Request-Line is expected. In other words, if
1286 * the server is reading the protocol stream at the beginning of a
1287 * message and receives a CRLF first, it should ignore the CRLF.
1289 if (reported_length
> 3) {
1290 word
= tvb_get_ntohs(tvb
,offset
);
1291 if (word
== 0x0d0a) {
1292 leading_crlf
= true;
1298 * If we previously dissected an HTTP request in this conversation then
1299 * we should be pretty sure that whatever we got in this TVB is
1300 * actually HTTP (even if what we have here is part of a file being
1301 * transferred over HTTP).
1303 if (conv_data
->req_res_tail
)
1304 have_seen_http
= true;
1307 * If this is binary data then there's no point in doing all the string
1308 * operations below: they'll just be slow on this data.
1310 if (!g_ascii_isprint(tvb_get_uint8(tvb
, offset
))) {
1312 * But, if we've seen some real HTTP then we're sure this is
1313 * an HTTP conversation, and this is binary file data.
1316 if (have_seen_http
) {
1320 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, proto_tag
);
1321 col_set_str(pinfo
->cinfo
, COL_INFO
, "Continuation");
1322 ti
= proto_tree_add_item(tree
, proto
, tvb
, offset
, -1, ENC_NA
);
1323 http_tree
= proto_item_add_subtree(ti
, ett_http
);
1325 next_tvb
= tvb_new_subset_remaining(tvb
, orig_offset
);
1326 /* If orig_offset > 0, this isn't the first message
1327 * dissected in this TCP segment, which means we had
1328 * a Content-Length, but more data after that body.
1330 if (orig_offset
> 0) {
1331 proto_tree_add_expert(http_tree
, pinfo
, &ei_http_excess_data
, next_tvb
, 0, tvb_captured_length(next_tvb
));
1333 /* Send it to Follow HTTP Stream and mark as file data */
1334 if(have_tap_listener(http_follow_tap
)) {
1335 tap_queue_packet(http_follow_tap
, pinfo
, next_tvb
);
1337 data_len
= tvb_captured_length(next_tvb
);
1338 proto_tree_add_bytes_format_value(http_tree
, hf_http_file_data
,
1339 next_tvb
, 0, data_len
, NULL
, "%u byte%s", data_len
, plurality(data_len
, "", "s"));
1340 call_data_dissector(next_tvb
, pinfo
, http_tree
);
1346 * Is this a request or response?
1348 * Note that "tvb_find_line_end()" will return a value that
1349 * is not longer than what's in the buffer, so the
1350 * "tvb_get_ptr()" call won't throw an exception.
1352 first_linelen
= tvb_find_line_end(tvb
, offset
,
1353 tvb_ensure_captured_length_remaining(tvb
, offset
), &next_offset
,
1356 if (first_linelen
== -1) {
1357 /* No complete line was found in this segment, do
1358 * desegmentation if we're told to.
1360 if (!req_resp_hdrs_do_reassembly(tvb
, offset
, pinfo
,
1361 http_desegment_headers
, http_desegment_body
, false, &chunk_offset
,
1362 streaming_content_type_dissector_table
, &handle
)) {
1364 * More data needed for desegmentation.
1370 if (!PINFO_FD_VISITED(pinfo
) && conv_data
->req_res_tail
&& conv_data
->req_res_tail
->private_data
) {
1371 tail_prv_data
= (http_req_res_private_data_t
*) conv_data
->req_res_tail
->private_data
;
1374 /* Check whether the first line is the beginning of a chunk. If it is the beginning
1375 * of a chunk, the headers and at least one chunk of HTTP request or response should
1376 * be dissected in the previous packets, and now we are processing subsequent chunks.
1378 if (http_desegment_body
&& http_dechunk_body
) {
1379 begin_with_chunk
= starts_with_chunk_size(tvb
, offset
, pinfo
);
1381 if (begin_with_chunk
&&
1382 ((prv_data
&& ( /* This packet has been parsed */
1383 /* and now we are in a HTTP request chunk stream */
1384 (prv_data
->req_fwd_flow
== direction
&& prv_data
->req_streaming_reassembly_data
) ||
1385 /* and now we are in a HTTP response chunk stream */
1386 (prv_data
->req_fwd_flow
!= direction
&& prv_data
->res_streaming_reassembly_data
)))
1388 (tail_prv_data
&& ( /* This packet has not been parsed and headers info in conv_data->req_res_tail */
1389 /* and now we are in a HTTP request chunk stream */
1390 (tail_prv_data
->req_fwd_flow
== direction
&& tail_prv_data
->req_streaming_reassembly_data
) ||
1391 /* and now we are in a HTTP response chunk stream */
1392 (tail_prv_data
->req_fwd_flow
!= direction
&& tail_prv_data
->res_streaming_reassembly_data
)))))
1394 streaming_chunk_mode
= true;
1399 * Is the first line a request or response?
1401 * Note that "tvb_find_line_end()" will return a value that
1402 * is not longer than what's in the buffer, so the
1403 * "tvb_get_ptr()" call won't throw an exception.
1405 firstline
= tvb_get_ptr(tvb
, offset
, first_linelen
);
1406 http_type
= MEDIA_CONTAINER_HTTP_OTHERS
; /* type not known yet */
1407 is_request_or_reply
= is_http_request_or_reply(pinfo
, (const char *)firstline
,
1408 first_linelen
, &http_type
, NULL
, conv_data
);
1409 if (is_request_or_reply
|| streaming_chunk_mode
) {
1410 bool try_desegment_body
;
1412 if (streaming_chunk_mode
&& begin_with_chunk
) {
1413 col_set_str(pinfo
->cinfo
, COL_INFO
, "Chunk Stream ");
1416 * Yes, it's a request or response.
1417 * Put the first line from the buffer into the summary
1418 * (but leave out the line terminator).
1420 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s ", format_text(pinfo
->pool
, firstline
, first_linelen
));
1424 * Do header desegmentation if we've been told to,
1425 * and do body desegmentation if we've been told to and
1426 * we find a Content-Length header in requests.
1428 * The following cases (from RFC 7230, Section 3.3) never have a
1429 * response body, so do not attempt to desegment the body for:
1430 * * Responses to HEAD requests.
1431 * * 2xx responses to CONNECT requests.
1432 * * 1xx, 204 No Content, 304 Not Modified responses.
1434 * Additionally if we are at the end of stream, no more segments
1435 * will be added so disable body segmentation too in that case.
1437 try_desegment_body
= (http_desegment_body
&& !end_of_stream
);
1438 if (try_desegment_body
&& http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
&& !streaming_chunk_mode
) {
1440 * The response_code is not yet set, so extract
1441 * the response code from the current line.
1443 int response_code
= parse_http_status_code(firstline
, firstline
+ first_linelen
);
1445 * On a second pass, we should have already associated
1446 * the response with the request. On a first sequential
1447 * pass, we haven't done so yet (as we don't know if we
1448 * need more data), so get the request method from the
1449 * most recent request, if it exists.
1451 char* request_method
= NULL
;
1453 request_method
= curr
->request_method
;
1454 } else if (!PINFO_FD_VISITED(pinfo
) && conv_data
->req_res_tail
) {
1455 request_method
= conv_data
->req_res_tail
->request_method
;
1457 if ((g_strcmp0(request_method
, "HEAD") == 0 ||
1458 (response_code
/ 100 == 2 &&
1459 (g_strcmp0(request_method
, "CONNECT") == 0 ||
1460 g_strcmp0(request_method
, "SSTP_DUPLEX_POST") == 0)) ||
1461 response_code
/ 100 == 1 ||
1462 response_code
== 204 ||
1463 response_code
== 304)) {
1464 /* No response body is present. */
1465 try_desegment_body
= false;
1468 if (!req_resp_hdrs_do_reassembly(tvb
, offset
, pinfo
,
1469 http_desegment_headers
, try_desegment_body
, http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
, &chunk_offset
,
1470 streaming_content_type_dissector_table
, &handle
)) {
1472 * More data needed for desegmentation.
1474 if (seq
&& chunk_map
&& chunk_offset
) {
1475 wmem_map_insert(chunk_map
, GUINT_TO_POINTER(*seq
), GINT_TO_POINTER(chunk_offset
));
1480 if (handle
&& http_desegment_body
&& http_dechunk_body
) {
1481 /* This handle is set because there is a header 'Transfer-Encoding: chunked', and
1482 * a streaming mode reassembly supported subdissector is found according to the
1483 * header of Content-Type.
1485 streaming_chunk_mode
= true;
1487 } else if (have_seen_http
) {
1489 * If we know this is HTTP then call it continuation.
1491 /* If orig_offset > 0, this isn't the first message dissected
1492 * in this segment, which means we had a Content-Length, but
1493 * more data after the body. If this isn't a request or reply,
1494 * that's bogus, and probably means the Content-Length was
1497 if (orig_offset
> 0) {
1500 col_set_str(pinfo
->cinfo
, COL_INFO
, "Continuation");
1503 if (is_request_or_reply
|| have_seen_http
|| streaming_chunk_mode
) {
1505 * Now set COL_PROTOCOL and create the http tree for the
1506 * cases where we set COL_INFO above.
1508 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, proto_tag
);
1509 ti
= proto_tree_add_item(tree
, proto
, tvb
, offset
, -1, ENC_NA
);
1510 http_tree
= proto_item_add_subtree(ti
, ett_http
);
1513 proto_tree_add_expert(http_tree
, pinfo
, &ei_http_leading_crlf
, tvb
, offset
-2, 2);
1516 proto_tree_add_expert(http_tree
, pinfo
, &ei_http_excess_data
, tvb
, offset
, tvb_captured_length_remaining(tvb
, offset
));
1520 is_tls
= proto_is_frame_protocol(pinfo
->layers
, "tls");
1522 if (!PINFO_FD_VISITED(pinfo
) && begin_with_chunk
1523 && streaming_chunk_mode
&& conv_data
->req_res_tail
) {
1524 /* point this packet beginning with a chunk to req_res info created in previous packet. */
1525 curr
= conv_data
->req_res_tail
;
1526 prv_data
= (http_req_res_private_data_t
*)curr
->private_data
;
1527 p_set_proto_data(wmem_file_scope(), pinfo
, proto_http
, HTTP_PROTO_DATA_REQRES
, curr
);
1531 if (prv_data
->req_fwd_flow
== direction
&& prv_data
->req_streaming_reassembly_data
) {
1532 /* in request flow */
1533 streaming_reassembly_data
= prv_data
->req_streaming_reassembly_data
;
1534 } else if (prv_data
->req_fwd_flow
!= direction
&& prv_data
->res_streaming_reassembly_data
) {
1535 /* in response flow */
1536 streaming_reassembly_data
= prv_data
->res_streaming_reassembly_data
;
1539 if (streaming_reassembly_data
) {
1540 streaming_chunk_mode
= true;
1541 headers
= streaming_reassembly_data
->main_headers
;
1542 handle
= streaming_reassembly_data
->streaming_handle
;
1543 content_info
= streaming_reassembly_data
->content_info
;
1544 header_value_map
= (wmem_map_t
*) content_info
->data
;
1548 // Ensure headers is valid before the `goto dissecting_body` below.
1549 if (headers
== NULL
) {
1550 DISSECTOR_ASSERT_HINT(!PINFO_FD_VISITED(pinfo
) || (PINFO_FD_VISITED(pinfo
) && !streaming_chunk_mode
),
1551 "The headers variable should not be NULL if it is in streaming mode during a non first scan.");
1552 DISSECTOR_ASSERT_HINT(header_value_map
== NULL
, "The header_value_map variable should be NULL while headers is NULL.");
1554 headers
= wmem_new0((streaming_chunk_mode
? wmem_file_scope() : pinfo
->pool
), headers_t
);
1555 header_value_map
= wmem_map_new((streaming_chunk_mode
? wmem_file_scope() : pinfo
->pool
), g_str_hash
, g_str_equal
);
1558 if (streaming_chunk_mode
&& begin_with_chunk
) {
1559 datalen
= reported_length
;
1560 goto dissecting_body
;
1563 stat_info
= wmem_new(pinfo
->pool
, http_info_value_t
);
1564 stat_info
->framenum
= pinfo
->num
;
1565 stat_info
->response_code
= 0;
1566 stat_info
->request_method
= NULL
;
1567 stat_info
->request_uri
= NULL
;
1568 stat_info
->referer_uri
= NULL
;
1569 stat_info
->http_host
= NULL
;
1570 stat_info
->full_uri
= NULL
;
1571 stat_info
->location_target
= NULL
;
1572 stat_info
->location_base_uri
= NULL
;
1573 p_set_proto_data(pinfo
->pool
, pinfo
, proto_http
, HTTP_PROTO_DATA_INFO
, (void *)stat_info
);
1576 * Process the packet data, a line at a time.
1578 http_type
= MEDIA_CONTAINER_HTTP_OTHERS
; /* type not known yet */
1580 saw_req_resp_or_header
= false; /* haven't seen anything yet */
1581 while (tvb_offset_exists(tvb
, offset
)) {
1583 * Find the end of the line.
1584 * XXX - what if we don't find it because the packet
1585 * is cut short by a snapshot length or the header is
1586 * split across TCP segments? How much dissection should
1589 linelen
= tvb_find_line_end(tvb
, offset
,
1590 tvb_ensure_captured_length_remaining(tvb
, offset
), &next_offset
,
1596 * Get a buffer that refers to the line.
1598 * Note that "tvb_find_line_end()" will return a value that
1599 * is not longer than what's in the buffer, so the
1600 * "tvb_get_ptr()" call won't throw an exception.
1602 line
= tvb_get_ptr(tvb
, offset
, linelen
);
1603 lineend
= line
+ linelen
;
1607 * OK, does it look like an HTTP request or response?
1609 reqresp_dissector
= NULL
;
1610 is_request_or_reply
=
1611 is_http_request_or_reply(pinfo
, (const char *)line
,
1612 linelen
, &http_type
, &reqresp_dissector
, conv_data
);
1613 if (is_request_or_reply
)
1617 * No. Does it look like a blank line (as would appear
1618 * at the end of an HTTP request)?
1621 goto is_http
; /* Yes. */
1624 * No. Does it look like a header?
1626 colon_offset
= offset
;
1628 linep
= (const unsigned char *)memchr(line
, ':', linelen
);
1631 * Colon found, assume it is a header if we've seen a
1632 * valid line before. Check a little more if not.
1634 if (saw_req_resp_or_header
|| valid_header_name(line
, (int)(linep
- line
))) {
1635 colon_offset
+= (int)(linep
- line
);
1636 if (http_check_ascii_headers
) {
1638 for (i
= 0; i
< linelen
; i
++) {
1639 if (line
[i
] & 0x80) {
1641 * Non-ASCII! Return -2 for invalid
1642 * HTTP, distinct from -1 for possible
1643 * reassembly required.
1654 * We haven't seen the colon yet.
1656 * If we've already seen an HTTP request or response
1657 * line, or a header line, and we're at the end of
1658 * the tvbuff, we assume this is an incomplete header
1659 * line. (We quit this loop after seeing a blank line,
1660 * so if we've seen a request or response line, or a
1661 * header line, this is probably more of the request
1662 * or response we're presumably seeing. There is some
1663 * risk of false positives, but the same applies for
1664 * full request or response lines or header lines,
1665 * although that's less likely.)
1667 * We throw an exception in that case, by checking for
1668 * the existence of the next byte after the last one
1669 * in the line. If it exists, "tvb_ensure_bytes_exist()"
1670 * throws no exception, and we fall through to the
1671 * "not HTTP" case. If it doesn't exist,
1672 * "tvb_ensure_bytes_exist()" will throw the appropriate
1675 if (saw_req_resp_or_header
)
1676 tvb_ensure_bytes_exist(tvb
, offset
, linelen
+ 1);
1679 * We don't consider this part of an HTTP request or
1680 * reply, so we don't display it.
1681 * (Yeah, that means we don't display, say, a text/http
1682 * page, but you can get that from the data pane.)
1687 if ((tree
) && (http_tree
== NULL
)) {
1688 ti
= proto_tree_add_item(tree
, proto
, tvb
, orig_offset
, -1, ENC_NA
);
1689 http_tree
= proto_item_add_subtree(ti
, ett_http
);
1691 proto_tree_add_expert(http_tree
, pinfo
, &ei_http_leading_crlf
, tvb
, orig_offset
-2, 2);
1695 if (first_loop
&& !is_tls
&& pinfo
->ptype
== PT_TCP
&&
1696 (pinfo
->srcport
== 443 || pinfo
->destport
== 443)) {
1697 expert_add_info(pinfo
, ti
, &ei_http_tls_port
);
1703 * Process this line.
1708 * This is a blank line, which means that
1709 * whatever follows it isn't part of this
1712 proto_tree_add_format_text(http_tree
, tvb
, offset
, next_offset
- offset
);
1713 offset
= next_offset
;
1718 * Not a blank line - either a request, a reply, or a header
1721 saw_req_resp_or_header
= true;
1722 if (is_request_or_reply
) {
1723 char *text
= tvb_format_text(pinfo
->pool
, tvb
, offset
, next_offset
- offset
);
1725 req_tree
= proto_tree_add_subtree(http_tree
, tvb
,
1726 offset
, next_offset
- offset
, ett_http_request
, &hdr_item
, text
);
1728 if (!PINFO_FD_VISITED(pinfo
)) {
1729 if (http_type
== MEDIA_CONTAINER_HTTP_REQUEST
) {
1730 curr
= push_req(conv_data
, pinfo
);
1731 curr
->request_method
= wmem_strdup(wmem_file_scope(), stat_info
->request_method
);
1732 prv_data
= curr
->private_data
;
1733 prv_data
->req_fwd_flow
= direction
;
1734 } else if (http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
1735 curr
= push_res(conv_data
, pinfo
);
1736 prv_data
= curr
->private_data
;
1737 prv_data
->req_fwd_flow
= -direction
;
1740 if (reqresp_dissector
) {
1741 reqresp_dissector(pinfo
, tvb
, req_tree
, offset
, line
,
1742 lineend
, conv_data
, curr
);
1748 bool good_header
= process_header(tvb
, offset
, next_offset
, line
, linelen
,
1749 colon_offset
, pinfo
, http_tree
, headers
, conv_data
,
1750 http_type
, header_value_map
, streaming_chunk_mode
);
1751 if (http_check_ascii_headers
&& !good_header
) {
1753 * Line is not a good HTTP header.
1754 * Return -2 to mark as invalid HTTP;
1755 * this is distinct from returning -1 when
1756 * it may be HTTP but in need of reassembly.
1761 offset
= next_offset
;
1763 if (stat_info
->http_host
&& stat_info
->request_uri
) {
1766 if ((g_ascii_strncasecmp(stat_info
->request_uri
, "http://", 7) == 0) ||
1767 (g_ascii_strncasecmp(stat_info
->request_uri
, "https://", 8) == 0) ||
1768 (g_ascii_strncasecmp(stat_info
->request_method
, "CONNECT", 7) == 0)) {
1769 uri
= wmem_strdup(pinfo
->pool
, stat_info
->request_uri
);
1772 uri
= wmem_strdup_printf(pinfo
->pool
, "%s://%s%s",
1773 is_tls
? "https" : "http",
1774 g_strstrip(wmem_strdup(pinfo
->pool
, stat_info
->http_host
)), stat_info
->request_uri
);
1776 stat_info
->full_uri
= wmem_strdup(pinfo
->pool
, uri
);
1777 if (!PINFO_FD_VISITED(pinfo
) && curr
) {
1778 curr
->full_uri
= wmem_strdup(wmem_file_scope(), uri
);
1782 /* If the request has a range, this is, or potentially is, asynchronous I/O thus
1783 * full_uri must be reinitialized because it is set to that of the last request. */
1784 if (curr
&& curr
->req_has_range
)
1785 curr
->full_uri
= NULL
;
1790 switch (http_type
) {
1792 case MEDIA_CONTAINER_HTTP_NOTIFICATION
:
1793 hidden_item
= proto_tree_add_boolean(http_tree
,
1794 hf_http_notification
, tvb
, 0, 0, 1);
1795 proto_item_set_hidden(hidden_item
);
1798 case MEDIA_CONTAINER_HTTP_RESPONSE
:
1799 hidden_item
= proto_tree_add_boolean(http_tree
,
1800 hf_http_response
, tvb
, 0, 0, 1);
1801 proto_item_set_hidden(hidden_item
);
1803 match_trans_t
*match_trans
= NULL
;
1805 if (curr
&& curr
->response_code
== 206 && curr
->resp_has_range
) {
1806 /* The conv_data->matches_table is only used for GET requests with ranges and
1807 * response_codes of 206 (Partial Content). (Note: only GETs use ranges.)
1809 match_trans
= (match_trans_t
*)wmem_map_lookup(conv_data
->matches_table
,
1810 GUINT_TO_POINTER(pinfo
->num
));
1812 pi
= proto_tree_add_uint(http_tree
, hf_http_request_in
, tvb
, 0, 0,
1813 match_trans
->req_frame
);
1814 proto_item_set_generated(pi
);
1816 pi
= proto_tree_add_time(http_tree
, hf_http_time
, tvb
, 0, 0,
1817 &match_trans
->delta_time
);
1818 proto_item_set_generated(pi
);
1820 pi
= proto_tree_add_string(http_tree
, hf_http_request_uri
, tvb
, 0, 0,
1821 match_trans
->request_uri
);
1822 proto_item_set_generated(pi
);
1825 uri
= wmem_strdup_printf(pinfo
->pool
, "%s://%s%s",
1826 is_tls
? "https" : "http",
1827 g_strstrip(wmem_strdup(pinfo
->pool
, match_trans
->http_host
)), match_trans
->request_uri
);
1829 pi
= proto_tree_add_string(http_tree
, hf_http_request_full_uri
, tvb
, 0, 0,
1831 proto_item_set_url(pi
);
1832 proto_item_set_generated(pi
);
1837 /* If responses don't have a range, the I/O is synchronous in which case a request is
1838 * matched with the following response. If a request or response is missing from the
1839 * capture file, correct matching resumes at the next request. */
1842 && !curr
->resp_has_range
1843 && curr
->req_framenum
) {
1844 pi
= proto_tree_add_uint(http_tree
, hf_http_request_in
, tvb
, 0, 0, curr
->req_framenum
);
1845 proto_item_set_generated(pi
);
1847 if (! nstime_is_unset(&(curr
->req_ts
))) {
1850 nstime_delta(&delta
, &pinfo
->abs_ts
, &(curr
->req_ts
));
1851 pi
= proto_tree_add_time(http_tree
, hf_http_time
, tvb
, 0, 0, &delta
);
1852 proto_item_set_generated(pi
);
1854 if (curr
->request_uri
) {
1855 pi
= proto_tree_add_string(http_tree
, hf_http_request_uri
, tvb
, 0, 0,
1857 proto_item_set_generated(pi
);
1859 if (curr
->full_uri
) {
1860 pi
= proto_tree_add_string(http_tree
, hf_http_request_full_uri
, tvb
, 0, 0,
1862 proto_item_set_url(pi
);
1863 proto_item_set_generated(pi
);
1867 case MEDIA_CONTAINER_HTTP_REQUEST
:
1869 int size
= wmem_map_size(conv_data
->matches_table
);
1871 hidden_item
= proto_tree_add_boolean(http_tree
, hf_http_request
, tvb
, 0, 0, 1);
1872 proto_item_set_hidden(hidden_item
);
1876 if (size
> 0 && curr
->req_has_range
) {
1877 match_trans
= (match_trans_t
*)wmem_map_lookup(conv_data
->matches_table
,
1878 GUINT_TO_POINTER(pinfo
->num
));
1880 pi
= proto_tree_add_uint(http_tree
, hf_http_response_in
,
1881 tvb
, 0, 0, match_trans
->resp_frame
);
1882 proto_item_set_generated(pi
);
1887 && !curr
->resp_has_range
1888 && curr
->res_framenum
) {
1889 pi
= proto_tree_add_uint(http_tree
, hf_http_response_in
, tvb
, 0, 0, curr
->res_framenum
);
1890 proto_item_set_generated(pi
);
1895 if (curr
->full_uri
) {
1896 pi
= proto_tree_add_string(http_tree
, hf_http_request_full_uri
, tvb
, 0, 0,
1898 proto_item_set_url(pi
);
1899 proto_item_set_generated(pi
);
1901 else if (stat_info
->full_uri
){
1902 pi
= proto_tree_add_string(http_tree
, hf_http_request_full_uri
, tvb
, 0, 0,
1903 stat_info
->full_uri
);
1904 proto_item_set_url(pi
);
1905 proto_item_set_generated(pi
);
1911 case MEDIA_CONTAINER_HTTP_OTHERS
:
1917 /* Give the follow tap what we've currently dissected */
1918 if(have_tap_listener(http_follow_tap
)) {
1919 tap_queue_packet(http_follow_tap
, pinfo
, tvb_new_subset_length(tvb
, orig_offset
, offset
-orig_offset
));
1922 reported_datalen
= tvb_reported_length_remaining(tvb
, offset
);
1923 datalen
= tvb_captured_length_remaining(tvb
, offset
);
1926 * If a content length was supplied, the amount of data to be
1927 * processed as HTTP payload is the minimum of the content
1928 * length and the amount of data remaining in the frame.
1930 * If a message is received with both a Transfer-Encoding
1931 * header field and a Content-Length header field, the latter
1934 * If no content length was supplied (or if a bad content length
1935 * was supplied), the amount of data to be processed is the amount
1936 * of data remaining in the frame.
1938 * If there was no Content-Length entity header, we should
1939 * accumulate all data until the end of the connection.
1940 * That'd require that the TCP dissector call subdissectors
1941 * for all frames with FIN, even if they contain no data,
1942 * which would require subdissectors to deal intelligently
1943 * with empty segments.
1945 * According to RFC 2616, however, 1xx responses, 204 responses,
1946 * and 304 responses MUST NOT include a message body; if no
1947 * content length is specified for them, we don't attempt to
1950 * XXX - it says the same about responses to HEAD requests;
1951 * unless there's a way to determine from the response
1952 * whether it's a response to a HEAD request, we have to
1953 * keep information about the request and associate that with
1954 * the response in order to handle that.
1956 if (headers
->have_content_length
&&
1957 headers
->transfer_encoding
== HTTP_TE_NONE
) {
1958 if (datalen
> headers
->content_length
)
1959 datalen
= (int)headers
->content_length
;
1962 * XXX - limit the reported length in the tvbuff we'll
1963 * hand to a subdissector to be no greater than the
1966 * We really need both unreassembled and "how long it'd
1967 * be if it were reassembled" lengths for tvbuffs, so
1968 * that we throw the appropriate exceptions for
1969 * "not enough data captured" (running past the length),
1970 * "packet needed reassembly" (within the length but
1971 * running past the unreassembled length), and
1972 * "packet is malformed" (running past the reassembled
1975 if (reported_datalen
> headers
->content_length
)
1976 reported_datalen
= (int)headers
->content_length
;
1978 switch (http_type
) {
1980 case MEDIA_CONTAINER_HTTP_REQUEST
:
1982 * Requests have no content if there's no
1983 * Content-Length header and no Transfer-Encoding
1986 if (headers
->transfer_encoding
== HTTP_TE_NONE
)
1989 reported_datalen
= -1;
1992 case MEDIA_CONTAINER_HTTP_RESPONSE
:
1993 if ((stat_info
->response_code
/100) == 1 ||
1994 stat_info
->response_code
== 204 ||
1995 stat_info
->response_code
== 304)
1996 datalen
= 0; /* no content! */
1999 * XXX - responses to HEAD requests,
2000 * and possibly other responses,
2001 * "MUST NOT" include a
2004 reported_datalen
= -1;
2010 * XXX - what about MEDIA_CONTAINER_HTTP_NOTIFICATION?
2012 reported_datalen
= -1;
2017 if (!PINFO_FD_VISITED(pinfo
) && streaming_chunk_mode
&& streaming_reassembly_data
== NULL
) {
2018 DISSECTOR_ASSERT(!begin_with_chunk
&& handle
&& http_dechunk_body
&& http_desegment_body
2019 && headers
->content_type
&& header_value_map
);
2021 content_info
= wmem_new0(wmem_file_scope(), media_content_info_t
);
2022 content_info
->media_str
= headers
->content_type_parameters
;
2023 content_info
->type
= http_type
;
2024 content_info
->data
= header_value_map
;
2026 streaming_reassembly_data
= wmem_new0(wmem_file_scope(), http_streaming_reassembly_data_t
);
2027 streaming_reassembly_data
->streaming_handle
= handle
;
2028 streaming_reassembly_data
->streaming_reassembly_info
= streaming_reassembly_info_new();
2029 streaming_reassembly_data
->content_info
= content_info
;
2030 streaming_reassembly_data
->main_headers
= headers
;
2032 if (prv_data
->req_fwd_flow
== direction
) {
2033 prv_data
->req_streaming_reassembly_data
= streaming_reassembly_data
;
2035 prv_data
->res_streaming_reassembly_data
= streaming_reassembly_data
;
2039 if (content_info
== NULL
) {
2040 content_info
= wmem_new0(pinfo
->pool
, media_content_info_t
);
2041 content_info
->media_str
= headers
->content_type_parameters
;
2042 content_info
->type
= http_type
;
2043 content_info
->data
= header_value_map
;
2050 * There's stuff left over; process it.
2053 unsigned chunked_datalen
= 0;
2057 * Create a tvbuff for the payload.
2059 * The amount of data to be processed that's
2060 * available in the tvbuff is "datalen", which
2061 * is the minimum of the amount of data left in
2062 * the tvbuff and any specified content length.
2064 * The amount of data to be processed that's in
2065 * this frame, regardless of whether it was
2066 * captured or not, is "reported_datalen",
2067 * which, if no content length was specified,
2068 * is -1, i.e. "to the end of the frame.
2070 next_tvb
= tvb_new_subset_length_caplen(tvb
, offset
, datalen
,
2074 * Handle *transfer* encodings.
2076 if (headers
->transfer_encoding_chunked
) {
2077 if (!http_dechunk_body
) {
2078 /* Chunking disabled, cannot dissect further. */
2079 /* XXX: Should this be sent to the follow tap? */
2080 call_data_dissector(next_tvb
, pinfo
, http_tree
);
2081 goto body_dissected
;
2084 chunked_datalen
= chunked_encoding_dissector(
2085 &next_tvb
, pinfo
, http_tree
, 0);
2087 if (chunked_datalen
== 0) {
2089 * The chunks weren't reassembled,
2090 * or there was a single zero
2093 goto body_dissected
;
2096 * Add a new data source for the
2099 #if 0 /* Handled in chunked_encoding_dissector() */
2100 tvb_set_child_real_data_tvbuff(tvb
,
2103 add_new_data_source(pinfo
, next_tvb
,
2104 "De-chunked entity body");
2105 /* chunked-body might be smaller than
2107 datalen
= chunked_datalen
;
2110 /* Handle other transfer codings after de-chunking. */
2111 switch (headers
->transfer_encoding
) {
2112 case HTTP_TE_COMPRESS
:
2113 case HTTP_TE_DEFLATE
:
2116 * We currently can't handle, for example, "gzip",
2117 * "compress", or "deflate" as *transfer* encodings;
2118 * just handle them as data for now.
2119 * XXX: Should this be sent to the follow tap?
2121 call_data_dissector(next_tvb
, pinfo
, http_tree
);
2122 goto body_dissected
;
2124 /* Nothing to do for "identity" or when header is
2125 * missing or invalid. */
2129 * At this point, any chunked *transfer* coding has been removed
2130 * (the entity body has been dechunked) so it can be presented
2131 * for the following operation (*content* encoding), or it has
2132 * been handed off to the data dissector.
2134 * Handle *content* encodings other than "identity" (which
2135 * shouldn't appear in a Content-Encoding header, but
2136 * we handle it in any case).
2138 if (headers
->content_encoding
!= NULL
&&
2139 g_ascii_strcasecmp(headers
->content_encoding
, "identity") != 0) {
2141 * We currently don't handle, for example, "compress";
2142 * just handle them as data for now.
2144 * After July 7, 2004 the LZW patent expired, so
2145 * support could be added. However, I don't think
2146 * that anybody ever really implemented "compress",
2147 * due to the aforementioned patent.
2149 tvbuff_t
*uncomp_tvb
= NULL
;
2150 proto_item
*e_ti
= NULL
;
2151 proto_tree
*e_tree
= NULL
;
2153 #if defined(HAVE_ZLIB) || defined(HAVE_ZLIBNG)
2154 if (http_decompress_body
&&
2155 (g_ascii_strcasecmp(headers
->content_encoding
, "gzip") == 0 ||
2156 g_ascii_strcasecmp(headers
->content_encoding
, "deflate") == 0 ||
2157 g_ascii_strcasecmp(headers
->content_encoding
, "x-gzip") == 0 ||
2158 g_ascii_strcasecmp(headers
->content_encoding
, "x-deflate") == 0))
2160 uncomp_tvb
= tvb_child_uncompress_zlib(tvb
, next_tvb
, 0,
2161 tvb_captured_length(next_tvb
));
2166 if (http_decompress_body
&&
2167 g_ascii_strcasecmp(headers
->content_encoding
, "br") == 0)
2169 uncomp_tvb
= tvb_child_uncompress_brotli(tvb
, next_tvb
, 0,
2170 tvb_captured_length(next_tvb
));
2175 if (http_decompress_body
&&
2176 g_ascii_strcasecmp(headers
->content_encoding
, "snappy") == 0)
2178 uncomp_tvb
= tvb_child_uncompress_snappy(tvb
, next_tvb
, 0,
2179 tvb_captured_length(next_tvb
));
2184 if (http_decompress_body
&&
2185 g_ascii_strcasecmp(headers
->content_encoding
, "zstd") == 0)
2187 uncomp_tvb
= tvb_child_uncompress_zstd(tvb
, next_tvb
, 0,
2188 tvb_captured_length(next_tvb
));
2192 if (http_decompress_body
&&
2193 g_ascii_strcasecmp(headers
->content_encoding
, "xpress") == 0)
2196 * [MS-WUSP] 2.1.1 Xpress Compression
2197 * Segmented into a series of blocks and compressed with the
2198 * Plain LZ77 variant of [MS-XCA] Xpress Compression Algorithm.
2200 * XXX - Does Microsoft use any other variants of [MS-XCA]
2201 * for Content-Encoding: xpress in any other situations
2202 * besides Windows Update Services?
2204 int comp_offset
= 0;
2206 tvbuff_t
*block_tvb
;
2207 while (tvb_captured_length_remaining(next_tvb
, comp_offset
) >= 8) {
2208 comp_offset
+= 4; // original length
2209 compressed_len
= tvb_get_int32(next_tvb
, comp_offset
, ENC_LITTLE_ENDIAN
);
2211 * "The compressed size of each block MUST NOT be greater
2212 * than 65535 bytes."
2214 if (compressed_len
<= 0 || compressed_len
> 65535) {
2217 if (!tvb_bytes_exist(next_tvb
, comp_offset
, compressed_len
)) {
2221 block_tvb
= tvb_child_uncompress_lz77(tvb
,
2222 tvb_new_subset_length(next_tvb
, comp_offset
, compressed_len
),
2225 if (uncomp_tvb
== NULL
) {
2226 uncomp_tvb
= tvb_new_composite();
2228 tvb_composite_append(uncomp_tvb
, block_tvb
);
2232 comp_offset
+= compressed_len
;
2234 if (uncomp_tvb
!= NULL
) {
2236 * XXX - Should we add an expert info for partial
2237 * decompression if we didn't finish? I.e., if
2238 * tvb_captured_length_remaining(next_tvb, comp_offset) > 0
2240 tvb_composite_finalize(uncomp_tvb
);
2245 * Add the encoded entity to the protocol tree
2247 e_tree
= proto_tree_add_subtree_format(http_tree
, next_tvb
,
2248 0, tvb_captured_length(next_tvb
), ett_http_encoded_entity
, &e_ti
,
2249 "Content-encoded entity body (%s): %u bytes",
2250 headers
->content_encoding
,
2251 tvb_captured_length(next_tvb
));
2253 if (uncomp_tvb
!= NULL
) {
2255 * Decompression worked
2258 /* XXX - Don't free this, since it's possible
2259 * that the data was only partially
2260 * decompressed, such as when desegmentation
2265 proto_item_append_text(e_ti
, " -> %u bytes", tvb_captured_length(uncomp_tvb
));
2266 next_tvb
= uncomp_tvb
;
2267 add_new_data_source(pinfo
, next_tvb
,
2268 "Uncompressed entity body");
2270 if (http_decompress_body
) {
2271 /* XXX - We should distinguish between "failed", "unsupported
2272 * only because support wasn't compiled in", and "unsupported
2273 * by Wireshark", to indicate whether the problem is with
2274 * the capture file, the build, or Wireshark.
2276 expert_add_info(pinfo
, e_ti
, &ei_http_decompression_failed
);
2279 expert_add_info(pinfo
, e_ti
, &ei_http_decompression_disabled
);
2281 /* XXX: Should this be sent to the follow tap? */
2282 call_data_dissector(next_tvb
, pinfo
, e_tree
);
2284 goto body_dissected
;
2288 * Note that a new data source is added for the entity body
2289 * only if it was content-encoded and/or transfer-encoded.
2292 /* Save values for the Export Object GUI feature if we have
2293 * an active listener to process it (which happens when
2294 * the export object window is open). */
2295 /* XXX: Do we really want to send it to Export Object if we didn't
2296 * get the headers, so that this is just a fragment of Continuation
2297 * Data and not a complete object?
2299 if(have_tap_listener(http_eo_tap
)) {
2300 eo_info
= wmem_new0(pinfo
->pool
, http_eo_t
);
2303 eo_info
->hostname
= curr
->http_host
;
2304 eo_info
->filename
= curr
->request_uri
;
2306 eo_info
->content_type
= headers
->content_type
;
2307 eo_info
->payload
= next_tvb
;
2309 tap_queue_packet(http_eo_tap
, pinfo
, eo_info
);
2312 /* Send it to Follow HTTP Stream and mark as file data */
2313 if(have_tap_listener(http_follow_tap
)) {
2314 tap_queue_packet(http_follow_tap
, pinfo
, next_tvb
);
2316 data_len
= tvb_captured_length(next_tvb
);
2317 proto_tree_add_bytes_format_value(http_tree
, hf_http_file_data
,
2318 next_tvb
, 0, data_len
, NULL
, "%u byte%s", data_len
, plurality(data_len
, "", "s"));
2320 if (tvb_captured_length(next_tvb
) == 0)
2321 goto body_dissected
;
2324 * Do subdissector checks.
2326 * First, if we have a Content-Type value, check whether
2327 * there's a subdissector for that media type.
2329 if (headers
->content_type
!= NULL
&& handle
== NULL
) {
2331 * We didn't find any subdissector that
2332 * registered for the port, and we have a
2333 * Content-Type value. Is there any subdissector
2334 * for that content type?
2338 * Calling the string handle for the media type
2339 * dissector table will set pinfo->match_string
2340 * to headers->content_type for us.
2342 pinfo
->match_string
= headers
->content_type
;
2343 handle
= dissector_get_string_handle(
2344 media_type_subdissector_table
,
2345 headers
->content_type
);
2346 if (handle
== NULL
&&
2347 strncmp(headers
->content_type
, "multipart/", sizeof("multipart/")-1) == 0) {
2348 /* Try to decode the unknown multipart subtype anyway */
2349 handle
= dissector_get_string_handle(
2350 media_type_subdissector_table
,
2356 * Now, if we didn't find such a subdissector, check
2357 * whether some subdissector asked that they be called
2358 * if HTTP traffic was on some particular port. This
2359 * handles protocols that use HTTP syntax but don't have
2360 * a media type and instead use a specified port.
2362 if (handle
== NULL
) {
2363 /* If the HTTP dissector was called heuristically
2364 * (or the HTTP dissector was called from the TLS
2365 * dissector, which was called heuristically), then
2366 * match_uint doesn't get set (or is likely set to
2367 * 6 for IP_PROTO_TCP.) Some protocols (e.g., IPP)
2368 * use the same specified port for both HTTP and
2369 * HTTP over TLS, and one will be a heuristic match.
2370 * In those cases, look at the src or dest port.
2372 if (pinfo
->match_uint
== pinfo
->srcport
|| pinfo
->match_uint
== pinfo
->destport
) {
2373 handle
= dissector_get_uint_handle(port_subdissector_table
,
2375 } else if (http_type
== MEDIA_CONTAINER_HTTP_REQUEST
) {
2376 handle
= dissector_get_uint_handle(port_subdissector_table
,
2378 } else if (http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
2379 handle
= dissector_get_uint_handle(port_subdissector_table
,
2384 if (handle
!= NULL
) {
2386 * We have a subdissector - call it.
2388 if (streaming_chunk_mode
) {
2389 pinfo
->match_string
= headers
->content_type
;
2390 /* reassemble and call subdissector */
2391 dissected
= (bool)reassemble_streaming_data_and_call_subdissector(next_tvb
, pinfo
, 0,
2392 tvb_reported_length_remaining(next_tvb
, 0), http_tree
, proto_tree_get_parent_tree(tree
),
2393 http_streaming_reassembly_table
, streaming_reassembly_data
->streaming_reassembly_info
,
2394 get_http_chunk_frame_num(tvb
, pinfo
, offset
), handle
,
2395 proto_tree_get_parent_tree(tree
), content_info
,
2396 "HTTP", &http_body_fragment_items
, hf_http_body_segment
);
2398 dissected
= (bool)call_dissector_only(handle
, next_tvb
, pinfo
, tree
, content_info
);
2401 expert_add_info(pinfo
, http_tree
, &ei_http_subdissector_failed
);
2406 * We don't have a subdissector or we have one and it did not
2407 * dissect the payload - try the heuristic subdissectors.
2409 uint16_t save_can_desegment
= pinfo
->can_desegment
;
2410 if (!(is_request_or_reply
|| streaming_chunk_mode
)) {
2411 /* If this isn't a request or reply, and we're not
2412 * in streaming chunk mode, then we didn't try to
2413 * desegment the body. (We think this is file data
2414 * in the middle of a connection.) Allow the heuristic
2415 * dissectors to desegment, if possible.
2417 pinfo
->can_desegment
= pinfo
->saved_can_desegment
;
2419 dissected
= dissector_try_heuristic(heur_subdissector_list
,
2420 next_tvb
, pinfo
, tree
, &hdtbl_entry
, content_info
);
2421 pinfo
->can_desegment
= save_can_desegment
;
2426 * The subdissector dissected the body.
2427 * Fix up the top-level item so that it doesn't
2428 * include the stuff for that protocol.
2431 proto_item_set_len(ti
, offset
);
2433 if (headers
->content_type
!= NULL
) {
2435 * Calling the default media handle if there is a content-type that
2436 * wasn't handled above.
2438 call_dissector_with_data(media_handle
, next_tvb
, pinfo
, tree
, content_info
);
2440 /* Call the default data dissector */
2441 call_data_dissector(next_tvb
, pinfo
, http_tree
);
2447 * We've processed "datalen" bytes worth of data
2448 * (which may be no data at all); advance the
2449 * offset past whatever data we've processed.
2454 /* Detect protocol changes after receiving full response headers. */
2455 if (http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
&& curr
&& pinfo
->desegment_offset
<= 0 && pinfo
->desegment_len
<= 0) {
2456 dissector_handle_t next_handle
= NULL
;
2457 bool server_acked
= false;
2460 * SSTP uses a special request method (instead of the Upgrade
2461 * header) and expects a 200 response to set up the session.
2463 if (g_strcmp0(curr
->request_method
, "SSTP_DUPLEX_POST") == 0 && curr
->response_code
== 200) {
2464 next_handle
= sstp_handle
;
2465 server_acked
= true;
2469 * An HTTP/1.1 upgrade only proceeds if the server responds
2470 * with 101 Switching Protocols. See RFC 7230 Section 6.7.
2472 if (headers
->upgrade
&& curr
->response_code
== 101) {
2473 next_handle
= dissector_get_string_handle(upgrade_subdissector_table
, headers
->upgrade
);
2475 char *slash_pos
= strchr(headers
->upgrade
, '/');
2477 /* Try again without version suffix. */
2478 next_handle
= dissector_get_string_handle(upgrade_subdissector_table
,
2479 wmem_strndup(pinfo
->pool
, headers
->upgrade
, slash_pos
- headers
->upgrade
));
2482 server_acked
= true;
2485 if (server_acked
&& !PINFO_FD_VISITED(pinfo
)) {
2486 conv_data
->startframe
= pinfo
->num
;
2487 conv_data
->startoffset
= offset
;
2488 conv_data
->next_handle
= next_handle
;
2489 copy_address_wmem(wmem_file_scope(), &conv_data
->server_addr
, &pinfo
->src
);
2490 conv_data
->server_port
= pinfo
->srcport
;
2495 tap_queue_packet(http_tap
, pinfo
, stat_info
);
2497 return offset
- orig_offset
;
2500 /* This can be used to dissect an HTTP request until such time
2501 * that a more complete dissector is written for that HTTP request.
2502 * This simple dissector only puts the request method, URI, and
2503 * protocol version into a sub-tree.
2506 basic_request_dissector(packet_info
*pinfo
, tvbuff_t
*tvb
, proto_tree
*tree
,
2507 int offset
, const unsigned char *line
, const unsigned char *lineend
,
2508 http_conv_t
*conv_data _U_
, http_req_res_t
*curr
)
2510 const unsigned char *next_token
;
2511 const char *request_uri
;
2514 http_info_value_t
*stat_info
= p_get_proto_data(pinfo
->pool
, pinfo
, proto_http
, HTTP_PROTO_DATA_INFO
);
2516 /* The first token is the method. */
2517 tokenlen
= get_token_len(line
, lineend
, &next_token
);
2520 proto_tree_add_item(tree
, hf_http_request_method
, tvb
, offset
, tokenlen
,
2522 if ((next_token
- line
) > 2 && next_token
[-1] == ' ' && next_token
[-2] == ' ') {
2523 /* Two spaces in a now indicates empty URI, so roll back one here */
2526 offset
+= (int) (next_token
- line
);
2529 /* The next token is the URI. */
2530 tokenlen
= get_token_len(line
, lineend
, &next_token
);
2532 /* Save the request URI for various later uses */
2533 request_uri
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, tokenlen
, ENC_ASCII
);
2535 if (request_uri
== NULL
&& curr
)
2536 request_uri
= curr
->request_uri
;
2538 stat_info
->request_uri
= wmem_strdup(pinfo
->pool
, request_uri
);
2539 if (!PINFO_FD_VISITED(pinfo
) && curr
) {
2540 curr
->request_uri
= wmem_strdup(wmem_file_scope(), request_uri
);
2542 ti
= proto_tree_add_string(tree
, hf_http_request_uri
, tvb
, offset
, tokenlen
, request_uri
);
2543 http_add_path_components_to_tree(tvb
, pinfo
, ti
, offset
, tokenlen
);
2544 offset
+= (int) (next_token
- line
);
2547 /* Everything to the end of the line is the version. */
2548 tokenlen
= (int) (lineend
- line
);
2549 proto_tree_add_item(tree
, hf_http_request_version
, tvb
, offset
, tokenlen
,
2554 parse_http_status_code(const unsigned char *line
, const unsigned char *lineend
)
2556 const unsigned char *next_token
;
2558 char response_code_chars
[4];
2559 int32_t status_code
= 0;
2562 * The first token is the HTTP Version.
2564 tokenlen
= get_token_len(line
, lineend
, &next_token
);
2570 * The second token is the Status Code.
2572 tokenlen
= get_token_len(line
, lineend
, &next_token
);
2576 memcpy(response_code_chars
, line
, 3);
2577 response_code_chars
[3] = '\0';
2578 if (!ws_strtoi32(response_code_chars
, NULL
, &status_code
))
2585 basic_response_dissector(packet_info
*pinfo
, tvbuff_t
*tvb
, proto_tree
*tree
,
2586 int offset
, const unsigned char *line
, const unsigned char *lineend
,
2587 http_conv_t
*conv_data _U_
, http_req_res_t
*curr
)
2589 const unsigned char *next_token
;
2591 char response_code_chars
[4];
2593 http_info_value_t
*stat_info
= p_get_proto_data(pinfo
->pool
, pinfo
, proto_http
, HTTP_PROTO_DATA_INFO
);
2596 * The first token is the HTTP Version.
2598 tokenlen
= get_token_len(line
, lineend
, &next_token
);
2601 proto_tree_add_item(tree
, hf_http_response_version
, tvb
, offset
, tokenlen
,
2603 /* Advance to the start of the next token. */
2604 offset
+= (int) (next_token
- line
);
2608 * The second token is the Status Code.
2610 tokenlen
= get_token_len(line
, lineend
, &next_token
);
2614 /* The Status Code characters must be copied into a null-terminated
2615 * buffer for strtoul() to parse them into an unsigned integer value.
2617 memcpy(response_code_chars
, line
, 3);
2618 response_code_chars
[3] = '\0';
2620 stat_info
->response_code
=
2621 (unsigned)strtoul(response_code_chars
, NULL
, 10);
2623 curr
->response_code
= stat_info
->response_code
;
2626 proto_tree_add_uint(tree
, hf_http_response_code
, tvb
, offset
, 3,
2627 stat_info
->response_code
);
2629 r_ti
= proto_tree_add_string(tree
, hf_http_response_code_desc
,
2630 tvb
, offset
, 3, val_to_str(stat_info
->response_code
,
2631 vals_http_status_code
, "Unknown (%d)"));
2633 proto_item_set_generated(r_ti
);
2635 /* Advance to the start of the next token. */
2636 offset
+= (int) (next_token
- line
);
2640 * The remaining tokens in the line comprise the Reason Phrase.
2642 tokenlen
= (int) (lineend
- line
);
2643 if (tokenlen
>= 1) {
2644 proto_tree_add_item(tree
, hf_http_response_phrase
, tvb
, offset
,
2645 tokenlen
, ENC_ASCII
);
2650 * Dissect the http data chunks and add them to the tree.
2653 chunked_encoding_dissector(tvbuff_t
**tvb_ptr
, packet_info
*pinfo
,
2654 proto_tree
*tree
, int offset
)
2658 uint32_t orig_datalen
;
2659 int chunked_data_size
;
2660 proto_tree
*subtree
;
2661 proto_item
*pi_chunked
= NULL
;
2664 int chunk_counter
= 0;
2665 int last_chunk_id
= -1;
2667 if ((tvb_ptr
== NULL
) || (*tvb_ptr
== NULL
)) {
2673 datalen
= tvb_reported_length_remaining(tvb
, offset
);
2675 subtree
= proto_tree_add_subtree(tree
, tvb
, offset
, datalen
,
2676 ett_http_chunked_response
, &pi_chunked
,
2677 "HTTP chunked response");
2679 /* Dechunk the "chunked response" to a new memory buffer */
2680 /* XXX: Composite tvbuffers do work now, so we should probably
2681 * use that to avoid the memcpys unless necessary.
2683 orig_datalen
= datalen
;
2684 raw_data
= (uint8_t *)wmem_alloc(pinfo
->pool
, datalen
);
2686 chunked_data_size
= 0;
2688 while (datalen
> 0) {
2689 uint32_t chunk_size
;
2691 uint8_t *chunk_string
;
2695 linelen
= tvb_find_line_end(tvb
, offset
, -1, &chunk_offset
, true);
2698 /* Can't get the chunk size line */
2702 chunk_string
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, linelen
, ENC_ASCII
);
2704 if (chunk_string
== NULL
) {
2705 /* Can't get the chunk size line */
2709 c
= (char*)chunk_string
;
2712 * We don't care about the extensions.
2714 if ((c
= strchr(c
, ';'))) {
2718 chunk_size
= (uint32_t)strtol((char*)chunk_string
, NULL
, 16);
2720 if (chunk_size
> datalen
) {
2722 * The chunk size is more than what's in the tvbuff,
2723 * so either the user hasn't enabled decoding, or all
2724 * of the segments weren't captured.
2726 chunk_size
= datalen
;
2729 chunked_data_size
+= chunk_size
;
2731 DISSECTOR_ASSERT((raw_len
+chunk_size
) <= orig_datalen
);
2732 tvb_memcpy(tvb
, (uint8_t *)(raw_data
+ raw_len
), chunk_offset
, chunk_size
);
2733 raw_len
+= chunk_size
;
2738 proto_tree
*chunk_subtree
;
2739 proto_item
*chunk_size_item
;
2741 if(chunk_size
== 0) {
2742 chunk_subtree
= proto_tree_add_subtree(subtree
, tvb
,
2744 chunk_offset
- offset
+ chunk_size
+ 2,
2745 ett_http_chunk_data
, NULL
,
2746 "End of chunked encoding");
2747 last_chunk_id
= chunk_counter
- 1;
2749 chunk_subtree
= proto_tree_add_subtree_format(subtree
, tvb
,
2751 chunk_offset
- offset
+ chunk_size
+ 2,
2752 ett_http_chunk_data
, NULL
,
2753 "Data chunk (%u octets)", chunk_size
);
2756 chunk_size_item
= proto_tree_add_uint(chunk_subtree
, hf_http_chunk_size
, tvb
, offset
,
2758 proto_item_set_len(chunk_size_item
, chunk_offset
- offset
);
2760 /* last-chunk does not have chunk-data CRLF. */
2761 if (chunk_size
> 0) {
2763 * Adding the chunk as FT_BYTES means that, in
2764 * TShark, you get the entire chunk dumped
2765 * out in hex, in addition to whatever
2766 * dissection is done on the reassembled data.
2768 proto_tree_add_item(chunk_subtree
, hf_http_chunk_data
, tvb
, chunk_offset
, chunk_size
, ENC_NA
);
2769 proto_tree_add_item(chunk_subtree
, hf_http_chunk_boundary
, tvb
,
2770 chunk_offset
+ chunk_size
, 2, ENC_NA
);
2774 offset
= chunk_offset
+ chunk_size
; /* beginning of next chunk */
2775 if (chunk_size
> 0) offset
+= 2; /* CRLF of chunk */
2776 datalen
= tvb_reported_length_remaining(tvb
, offset
);
2778 /* This is the last chunk */
2779 if (chunk_size
== 0) {
2780 /* Check for: trailer-part CRLF.
2781 * trailer-part = *( header-field CRLF ) */
2782 int trailer_offset
= offset
, trailer_len
;
2783 int header_field_len
;
2784 /* Skip all header-fields. */
2786 trailer_len
= trailer_offset
- offset
;
2787 header_field_len
= tvb_find_line_end(tvb
,
2789 datalen
- trailer_len
,
2790 &trailer_offset
, true);
2791 } while (header_field_len
> 0);
2792 if (trailer_len
> 0) {
2793 proto_tree_add_item(subtree
,
2794 hf_http_chunked_trailer_part
,
2795 tvb
, offset
, trailer_len
, ENC_ASCII
);
2796 offset
+= trailer_len
;
2797 datalen
-= trailer_len
;
2800 /* last CRLF of chunked-body is found. */
2801 if (header_field_len
== 0) {
2802 proto_tree_add_format_text(subtree
, tvb
, offset
,
2803 trailer_offset
- offset
);
2804 datalen
-= trailer_offset
- offset
;
2810 /* datalen is the remaining bytes that are available for consumption. If
2811 * smaller than orig_datalen, then bytes were consumed. */
2812 if (datalen
< orig_datalen
) {
2814 proto_item_set_len(pi_chunked
, orig_datalen
- datalen
);
2815 new_tvb
= tvb_new_child_real_data(tvb
, raw_data
, chunked_data_size
, chunked_data_size
);
2819 if (chunk_counter
> 0) {
2820 proto_item
* ti_http
= proto_tree_get_parent(tree
);
2821 proto_item_append_text(ti_http
, ", has %d chunk%s%s",
2822 chunk_counter
, plurality(chunk_counter
, "", "s"),
2823 (last_chunk_id
< 0 ? "" : " (including last chunk)"));
2825 if (last_chunk_id
== 0) {
2826 /* only append text to column while starting with last chunk */
2827 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, " ", "[Last Chunk]");
2831 /* Size of chunked-body or 0 if none was found. */
2832 return orig_datalen
- datalen
;
2836 conversation_dissector_is_http(conversation_t
*conv
, uint32_t frame_num
)
2838 dissector_handle_t conv_handle
;
2842 conv_handle
= conversation_get_dissector(conv
, frame_num
);
2843 return conv_handle
== http_handle
||
2844 conv_handle
== http_tcp_handle
||
2845 conv_handle
== http_sctp_handle
;
2848 /* Call a subdissector to handle HTTP CONNECT's traffic */
2850 http_payload_subdissector(tvbuff_t
*tvb
, proto_tree
*tree
,
2851 packet_info
*pinfo
, http_conv_t
*conv_data
, void* data
)
2853 uint32_t *ptr
= NULL
;
2854 uint32_t uri_port
, saved_port
, srcport
, destport
;
2855 char **strings
; /* An array for splitting the request URI into hostname and port */
2857 proto_tree
*proxy_tree
;
2858 conversation_t
*conv
;
2859 bool from_server
= pinfo
->srcport
== conv_data
->server_port
&&
2860 addresses_equal(&conv_data
->server_addr
, &pinfo
->src
);
2862 /* Grab the destination port number from the request URI to find the right subdissector */
2863 strings
= wmem_strsplit(pinfo
->pool
, conv_data
->req_res_tail
->request_uri
, ":", 2);
2865 if(strings
[0] != NULL
&& strings
[1] != NULL
) {
2867 * The string was successfully split in two
2868 * Create a proxy-connect subtree
2871 item
= proto_tree_add_item(tree
, proto_http
, tvb
, 0, -1, ENC_NA
);
2872 proxy_tree
= proto_item_add_subtree(item
, ett_http
);
2874 item
= proto_tree_add_string(proxy_tree
, hf_http_proxy_connect_host
,
2875 tvb
, 0, 0, strings
[0]);
2876 proto_item_set_generated(item
);
2878 item
= proto_tree_add_uint(proxy_tree
, hf_http_proxy_connect_port
,
2879 tvb
, 0, 0, (uint32_t)strtol(strings
[1], NULL
, 10) );
2880 proto_item_set_generated(item
);
2883 uri_port
= (int)strtol(strings
[1], NULL
, 10); /* Convert string to a base-10 integer */
2886 srcport
= pinfo
->srcport
;
2887 destport
= uri_port
;
2890 destport
= pinfo
->destport
;
2893 conv
= find_conversation(pinfo
->num
, &pinfo
->src
, &pinfo
->dst
, CONVERSATION_TCP
, srcport
, destport
, 0);
2895 /* We may get stuck in a recursion loop if we let process_tcp_payload() call us.
2896 * So, if the port in the URI is one we're registered for or we have set up a
2897 * conversation (e.g., one we detected heuristically or via Decode-As) call the data
2898 * dissector directly.
2900 if (value_is_in_range(http_tcp_range
, uri_port
) ||
2901 conversation_dissector_is_http(conv
, pinfo
->num
)) {
2902 call_data_dissector(tvb
, pinfo
, tree
);
2904 /* set pinfo->{src/dst port} and call the TCP sub-dissector lookup */
2906 ptr
= &pinfo
->destport
;
2908 ptr
= &pinfo
->srcport
;
2910 /* Increase pinfo->can_desegment because we are traversing
2911 * http and want to preserve desegmentation functionality for
2912 * the proxied protocol
2914 if( pinfo
->can_desegment
>0 )
2915 pinfo
->can_desegment
++;
2919 decode_tcp_ports(tvb
, 0, pinfo
, tree
,
2920 pinfo
->srcport
, pinfo
->destport
, NULL
,
2921 (struct tcpinfo
*)data
);
2930 * XXX - this won't handle HTTP 0.9 replies, but they're all data
2934 is_http_request_or_reply(packet_info
*pinfo
, const char *data
, int linelen
, media_container_type_t
*type
,
2935 ReqRespDissector
*reqresp_dissector
,
2936 http_conv_t
*conv_data _U_
)
2938 http_info_value_t
*stat_info
= p_get_proto_data(pinfo
->pool
, pinfo
, proto_http
, HTTP_PROTO_DATA_INFO
);
2939 int isHttpRequestOrReply
= false;
2942 * From RFC 2774 - An HTTP Extension Framework
2944 * Support the command prefix that identifies the presence of
2945 * a "mandatory" header.
2947 if (linelen
>= 2 && strncmp(data
, "M-", 2) == 0) {
2953 * From draft-cohen-gena-client-01.txt, available from the uPnP forum:
2954 * NOTIFY, SUBSCRIBE, UNSUBSCRIBE
2956 * From draft-ietf-dasl-protocol-00.txt, a now vanished Microsoft draft:
2959 if ((linelen
>= 5 && strncmp(data
, "HTTP/", 5) == 0) ||
2960 (linelen
>= 3 && strncmp(data
, "ICY", 3) == 0)) {
2961 *type
= MEDIA_CONTAINER_HTTP_RESPONSE
;
2962 isHttpRequestOrReply
= true; /* response */
2963 if (reqresp_dissector
)
2964 *reqresp_dissector
= basic_response_dissector
;
2966 const unsigned char * ptr
= (const unsigned char *)data
;
2969 /* Look for the space following the Method */
2970 while (indx
< linelen
) {
2979 /* Check the methods that have same length */
2983 if (strncmp(data
, "GET", indx
) == 0 ||
2984 strncmp(data
, "PUT", indx
) == 0) {
2985 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
2986 isHttpRequestOrReply
= true;
2991 if (strncmp(data
, "COPY", indx
) == 0 ||
2992 strncmp(data
, "HEAD", indx
) == 0 ||
2993 strncmp(data
, "LOCK", indx
) == 0 ||
2994 strncmp(data
, "MOVE", indx
) == 0 ||
2995 strncmp(data
, "POLL", indx
) == 0 ||
2996 strncmp(data
, "POST", indx
) == 0) {
2997 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
2998 isHttpRequestOrReply
= true;
3003 if (strncmp(data
, "BCOPY", indx
) == 0 ||
3004 strncmp(data
, "BMOVE", indx
) == 0 ||
3005 strncmp(data
, "MKCOL", indx
) == 0 ||
3006 strncmp(data
, "TRACE", indx
) == 0 ||
3007 strncmp(data
, "PATCH", indx
) == 0 || /* RFC 5789 */
3008 strncmp(data
, "LABEL", indx
) == 0 || /* RFC 3253 8.2 */
3009 strncmp(data
, "MERGE", indx
) == 0) { /* RFC 3253 11.2 */
3010 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3011 isHttpRequestOrReply
= true;
3016 if (strncmp(data
, "DELETE", indx
) == 0 ||
3017 strncmp(data
, "SEARCH", indx
) == 0 ||
3018 strncmp(data
, "UNLOCK", indx
) == 0 ||
3019 strncmp(data
, "REPORT", indx
) == 0 || /* RFC 3253 3.6 */
3020 strncmp(data
, "UPDATE", indx
) == 0) { /* RFC 3253 7.1 */
3021 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3022 isHttpRequestOrReply
= true;
3024 else if (strncmp(data
, "NOTIFY", indx
) == 0) {
3025 *type
= MEDIA_CONTAINER_HTTP_NOTIFICATION
;
3026 isHttpRequestOrReply
= true;
3031 if (strncmp(data
, "BDELETE", indx
) == 0 ||
3032 strncmp(data
, "CONNECT", indx
) == 0 ||
3033 strncmp(data
, "OPTIONS", indx
) == 0 ||
3034 strncmp(data
, "CHECKIN", indx
) == 0) { /* RFC 3253 4.4, 9.4 */
3035 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3036 isHttpRequestOrReply
= true;
3041 if (strncmp(data
, "PROPFIND", indx
) == 0 ||
3042 strncmp(data
, "CHECKOUT", indx
) == 0 || /* RFC 3253 4.3, 9.3 */
3043 strncmp(data
, "CCM_POST", indx
) == 0) {
3044 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3045 isHttpRequestOrReply
= true;
3050 if (strncmp(data
, "SUBSCRIBE", indx
) == 0) {
3051 *type
= MEDIA_CONTAINER_HTTP_NOTIFICATION
;
3052 isHttpRequestOrReply
= true;
3053 } else if (strncmp(data
, "PROPPATCH", indx
) == 0 ||
3054 strncmp(data
, "BPROPFIND", indx
) == 0) {
3055 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3056 isHttpRequestOrReply
= true;
3061 if (strncmp(data
, "BPROPPATCH", indx
) == 0 ||
3062 strncmp(data
, "UNCHECKOUT", indx
) == 0 || /* RFC 3253 4.5 */
3063 strncmp(data
, "MKACTIVITY", indx
) == 0) { /* RFC 3253 13.5 */
3064 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3065 isHttpRequestOrReply
= true;
3070 if (strncmp(data
, "MKWORKSPACE", indx
) == 0 || /* RFC 3253 6.3 */
3071 strncmp(data
, "RPC_CONNECT", indx
) == 0 || /* [MS-RPCH] 2.1.1.1.1 */
3072 strncmp(data
, "RPC_IN_DATA", indx
) == 0) { /* [MS-RPCH] 2.1.2.1.1 */
3073 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3074 isHttpRequestOrReply
= true;
3075 } else if (strncmp(data
, "UNSUBSCRIBE", indx
) == 0) {
3076 *type
= MEDIA_CONTAINER_HTTP_NOTIFICATION
;
3077 isHttpRequestOrReply
= true;
3082 if (strncmp(data
, "RPC_OUT_DATA", indx
) == 0) { /* [MS-RPCH] 2.1.2.1.2 */
3083 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3084 isHttpRequestOrReply
= true;
3089 if (strncmp(data
, "VERSION-CONTROL", indx
) == 0) { /* RFC 3253 3.5 */
3090 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3091 isHttpRequestOrReply
= true;
3096 if (strncmp(data
, "BASELINE-CONTROL", indx
) == 0) { /* RFC 3253 12.6 */
3097 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3098 isHttpRequestOrReply
= true;
3099 } else if (strncmp(data
, "SSTP_DUPLEX_POST", indx
) == 0) { /* MS SSTP */
3100 *type
= MEDIA_CONTAINER_HTTP_REQUEST
;
3101 isHttpRequestOrReply
= true;
3109 if (isHttpRequestOrReply
&& reqresp_dissector
) {
3110 *reqresp_dissector
= basic_request_dissector
;
3112 stat_info
->request_method
= wmem_strndup(pinfo
->pool
, data
, indx
);
3119 return isHttpRequestOrReply
;
3131 #define HDR_NO_SPECIAL 0
3132 #define HDR_AUTHORIZATION 1
3133 #define HDR_AUTHENTICATE 2
3134 #define HDR_CONTENT_TYPE 3
3135 #define HDR_CONTENT_LENGTH 4
3136 #define HDR_CONTENT_ENCODING 5
3137 #define HDR_TRANSFER_ENCODING 6
3139 #define HDR_UPGRADE 8
3140 #define HDR_COOKIE 9
3141 #define HDR_WEBSOCKET_PROTOCOL 10
3142 #define HDR_WEBSOCKET_EXTENSIONS 11
3143 #define HDR_REFERER 12
3144 #define HDR_LOCATION 13
3145 #define HDR_HTTP2_SETTINGS 14
3146 #define HDR_RANGE 15
3147 #define HDR_CONTENT_RANGE 16
3149 static const header_info headers
[] = {
3150 { "Authorization", &hf_http_authorization
, HDR_AUTHORIZATION
},
3151 { "Proxy-Authorization", &hf_http_proxy_authorization
, HDR_AUTHORIZATION
},
3152 { "Proxy-Authenticate", &hf_http_proxy_authenticate
, HDR_AUTHENTICATE
},
3153 { "WWW-Authenticate", &hf_http_www_authenticate
, HDR_AUTHENTICATE
},
3154 { "Content-Type", &hf_http_content_type
, HDR_CONTENT_TYPE
},
3155 { "Content-Length", &hf_http_content_length_header
, HDR_CONTENT_LENGTH
},
3156 { "Content-Encoding", &hf_http_content_encoding
, HDR_CONTENT_ENCODING
},
3157 { "Transfer-Encoding", &hf_http_transfer_encoding
, HDR_TRANSFER_ENCODING
},
3158 { "Upgrade", &hf_http_upgrade
, HDR_UPGRADE
},
3159 { "User-Agent", &hf_http_user_agent
, HDR_NO_SPECIAL
},
3160 { "Host", &hf_http_host
, HDR_HOST
},
3161 { "Range", &hf_http_range
, HDR_RANGE
},
3162 { "Content-Range", &hf_http_content_range
, HDR_CONTENT_RANGE
},
3163 { "Connection", &hf_http_connection
, HDR_NO_SPECIAL
},
3164 { "Cookie", &hf_http_cookie
, HDR_COOKIE
},
3165 { "Accept", &hf_http_accept
, HDR_NO_SPECIAL
},
3166 { "Referer", &hf_http_referer
, HDR_REFERER
},
3167 { "Accept-Language", &hf_http_accept_language
, HDR_NO_SPECIAL
},
3168 { "Accept-Encoding", &hf_http_accept_encoding
, HDR_NO_SPECIAL
},
3169 { "Date", &hf_http_date
, HDR_NO_SPECIAL
},
3170 { "Cache-Control", &hf_http_cache_control
, HDR_NO_SPECIAL
},
3171 { "Server", &hf_http_server
, HDR_NO_SPECIAL
},
3172 { "Location", &hf_http_location
, HDR_LOCATION
},
3173 { "Sec-WebSocket-Accept", &hf_http_sec_websocket_accept
, HDR_NO_SPECIAL
},
3174 { "Sec-WebSocket-Extensions", &hf_http_sec_websocket_extensions
, HDR_WEBSOCKET_EXTENSIONS
},
3175 { "Sec-WebSocket-Key", &hf_http_sec_websocket_key
, HDR_NO_SPECIAL
},
3176 { "Sec-WebSocket-Protocol", &hf_http_sec_websocket_protocol
, HDR_WEBSOCKET_PROTOCOL
},
3177 { "Sec-WebSocket-Version", &hf_http_sec_websocket_version
, HDR_NO_SPECIAL
},
3178 { "Set-Cookie", &hf_http_set_cookie
, HDR_NO_SPECIAL
},
3179 { "Last-Modified", &hf_http_last_modified
, HDR_NO_SPECIAL
},
3180 { "X-Forwarded-For", &hf_http_x_forwarded_for
, HDR_NO_SPECIAL
},
3181 { "HTTP2-Settings", &hf_http_http2_settings
, HDR_HTTP2_SETTINGS
},
3185 * Look up a header name (assume lower-case header_name).
3188 get_hf_for_header(char* header_name
)
3192 if (header_fields_hash
) {
3193 hf_id
= (int*) g_hash_table_lookup(header_fields_hash
, header_name
);
3205 deregister_header_fields(void)
3208 /* Deregister all fields */
3209 for (unsigned i
= 0; i
< dynamic_hf_size
; i
++) {
3210 proto_deregister_field (proto_http
, *(dynamic_hf
[i
].p_id
));
3211 g_free (dynamic_hf
[i
].p_id
);
3214 proto_add_deregistered_data (dynamic_hf
);
3216 dynamic_hf_size
= 0;
3219 if (header_fields_hash
) {
3220 g_hash_table_destroy (header_fields_hash
);
3221 header_fields_hash
= NULL
;
3226 header_fields_post_update_cb(void)
3230 char* header_name_key
;
3232 deregister_header_fields();
3234 if (num_header_fields
) {
3235 header_fields_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
3236 dynamic_hf
= g_new0(hf_register_info
, num_header_fields
);
3237 dynamic_hf_size
= num_header_fields
;
3239 for (unsigned i
= 0; i
< dynamic_hf_size
; i
++) {
3240 hf_id
= g_new(int,1);
3242 header_name
= g_strdup(header_fields
[i
].header_name
);
3243 header_name_key
= g_ascii_strdown(header_name
, -1);
3245 dynamic_hf
[i
].p_id
= hf_id
;
3246 dynamic_hf
[i
].hfinfo
.name
= header_name
;
3247 dynamic_hf
[i
].hfinfo
.abbrev
= ws_strdup_printf("http.header.%s", header_name
);
3248 dynamic_hf
[i
].hfinfo
.type
= FT_STRING
;
3249 dynamic_hf
[i
].hfinfo
.display
= BASE_NONE
;
3250 dynamic_hf
[i
].hfinfo
.strings
= NULL
;
3251 dynamic_hf
[i
].hfinfo
.bitmask
= 0;
3252 dynamic_hf
[i
].hfinfo
.blurb
= g_strdup(header_fields
[i
].header_desc
);
3253 HFILL_INIT(dynamic_hf
[i
]);
3255 g_hash_table_insert(header_fields_hash
, header_name_key
, hf_id
);
3258 proto_register_field_array(proto_http
, dynamic_hf
, dynamic_hf_size
);
3263 header_fields_reset_cb(void)
3265 deregister_header_fields();
3269 * Parses the transfer-coding, returning true if everything was fully understood
3270 * or false when unknown names were encountered.
3273 http_parse_transfer_coding(const char *value
, headers_t
*eh_ptr
)
3275 bool is_fully_parsed
= true;
3277 /* Mark header as set, but with unknown encoding. */
3278 eh_ptr
->transfer_encoding
= HTTP_TE_UNKNOWN
;
3281 /* skip OWS (SP / HTAB) and commas; stop at the end. */
3282 while (*value
== ' ' || *value
== '\t' || *value
== ',')
3287 if (g_str_has_prefix(value
, "chunked")) {
3288 eh_ptr
->transfer_encoding_chunked
= true;
3289 value
+= sizeof("chunked") - 1;
3293 /* For now assume that chunked can only combined with exactly
3294 * one other (compression) encoding. Anything else is
3296 if (eh_ptr
->transfer_encoding
!= HTTP_TE_UNKNOWN
) {
3297 /* No more transfer codings are expected. */
3298 is_fully_parsed
= false;
3302 if (g_str_has_prefix(value
, "compress")) {
3303 eh_ptr
->transfer_encoding
= HTTP_TE_COMPRESS
;
3304 value
+= sizeof("compress") - 1;
3305 } else if (g_str_has_prefix(value
, "deflate")) {
3306 eh_ptr
->transfer_encoding
= HTTP_TE_DEFLATE
;
3307 value
+= sizeof("deflate") - 1;
3308 } else if (g_str_has_prefix(value
, "gzip")) {
3309 eh_ptr
->transfer_encoding
= HTTP_TE_GZIP
;
3310 value
+= sizeof("gzip") - 1;
3311 } else if (g_str_has_prefix(value
, "identity")) {
3312 eh_ptr
->transfer_encoding
= HTTP_TE_IDENTITY
;
3313 value
+= sizeof("identity") - 1;
3314 } else if (g_str_has_prefix(value
, "x-compress")) {
3315 eh_ptr
->transfer_encoding
= HTTP_TE_COMPRESS
;
3316 value
+= sizeof("x-compress") - 1;
3317 } else if (g_str_has_prefix(value
, "x-gzip")) {
3318 eh_ptr
->transfer_encoding
= HTTP_TE_GZIP
;
3319 value
+= sizeof("x-gzip") - 1;
3321 /* Unknown transfer encoding, skip until next comma.
3322 * Stop when no more names are found. */
3323 is_fully_parsed
= false;
3324 value
= strchr(value
, ',');
3330 return is_fully_parsed
;
3334 is_token_char(char c
)
3336 /* tchar according to https://tools.ietf.org/html/rfc7230#section-3.2.6 */
3337 return strchr("!#$%&\\:*+-.^_`|~", c
) || g_ascii_isalnum(c
);
3341 valid_header_name(const unsigned char *line
, int header_len
)
3345 * Validate the header name. This allows no space between the field name
3346 * and colon (RFC 7230, Section. 3.2.4).
3348 if (header_len
== 0) {
3351 for (int i
= 0; i
< header_len
; i
++) {
3353 * NUL is not a valid character; treat it specially
3354 * due to C's notion that strings are NUL-terminated.
3356 if (line
[i
] == '\0') {
3359 if (!is_token_char(line
[i
])) {
3367 process_header(tvbuff_t
*tvb
, int offset
, int next_offset
,
3368 const unsigned char *line
, int linelen
, int colon_offset
,
3369 packet_info
*pinfo
, proto_tree
*tree
, headers_t
*eh_ptr
,
3370 http_conv_t
*conv_data
, media_container_type_t http_type
, wmem_map_t
*header_value_map
,
3371 bool streaming_chunk_mode
)
3374 int line_end_offset
;
3379 int value_len
, value_bytes_len
;
3380 uint8_t *value_bytes
;
3385 proto_item
*hdr_item
, *it
;
3388 tap_credential_t
* auth
;
3389 http_req_res_t
*curr_req_res
= (http_req_res_t
*)p_get_proto_data(wmem_file_scope(), pinfo
,
3390 proto_http
, HTTP_PROTO_DATA_REQRES
);
3391 http_info_value_t
*stat_info
= p_get_proto_data(pinfo
->pool
, pinfo
, proto_http
, HTTP_PROTO_DATA_INFO
);
3392 wmem_allocator_t
*scope
= (!PINFO_FD_VISITED(pinfo
) && streaming_chunk_mode
) ? wmem_file_scope() :
3393 ((PINFO_FD_VISITED(pinfo
) && streaming_chunk_mode
) ? NULL
: pinfo
->pool
);
3395 len
= next_offset
- offset
;
3396 line_end_offset
= offset
+ linelen
;
3397 header_len
= colon_offset
- offset
;
3400 * Not a valid header name? Just add a line plus expert info.
3402 if (!valid_header_name(line
, header_len
)) {
3403 if (http_check_ascii_headers
) {
3404 /* If we're offering the chance for other dissectors to parse,
3405 * we shouldn't add any tree items ourselves.
3409 if (http_type
== MEDIA_CONTAINER_HTTP_REQUEST
) {
3410 hf_index
= hf_http_request_line
;
3411 } else if (http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
3412 hf_index
= hf_http_response_line
;
3414 hf_index
= hf_http_unknown_header
;
3416 it
= proto_tree_add_item(tree
, hf_index
, tvb
, offset
, len
, ENC_NA
|ENC_ASCII
);
3417 proto_item_set_text(it
, "%s", format_text(pinfo
->pool
, line
, len
));
3418 expert_add_info(pinfo
, it
, &ei_http_bad_header_name
);
3423 * Make a null-terminated, all-lower-case version of the header
3426 header_name
= wmem_ascii_strdown(pinfo
->pool
, &line
[0], header_len
);
3428 hf_index
= find_header_hf_value(tvb
, offset
, header_len
);
3431 * Skip whitespace after the colon.
3433 value_offset
= colon_offset
+ 1;
3434 while (value_offset
< line_end_offset
3435 && ((c
= line
[value_offset
- offset
]) == ' ' || c
== '\t'))
3441 * XXX - RFC 9110 5.5 "Specification for newly defined fields
3442 * SHOULD limit their values to visible US-ASCII octets (VCHAR),
3443 * SP, and HTAB. A recipient SHOULD treat other allowed octets in
3444 * field content (i.e., obs-text [%x80-FF]) as opaque data...
3445 * Field values containing CR, LF, or NUL characters are invalid
3446 * and dangerous." (Up to RFC 7230, an obsolete "line-folding"
3447 * mechanism that included CRLF was allowed.)
3449 * So NUL is not allowed, and we should have one or more
3450 * expert infos if the field value has anything other than
3451 * ASCII printable + TAB. (Possibly different severities
3452 * depending on whether it contains obsolete characters
3453 * like \x80-\xFF vs characters never allowed like NUL.)
3454 * All known field types respect this (using Base64, etc.)
3455 * Unknown field types (possibly including those registered
3456 * through the UAT) should be treated like FT_BYTES with
3457 * BASE_SHOW_ASCII_PRINTABLE instead of FT_STRING, but it's
3458 * more difficult to do that with the custom formatting
3459 * that uses the header name.
3461 * Instead, for now for display purposes we will treat strings
3462 * as ASCII and pass the raw value to subdissectors via the
3463 * header_value_map. For the latter, we allocate a buffer that's
3464 * value_bytes_len+1 bytes long, copy value_bytes_len bytes, and
3465 * stick in a NUL terminator, so that the buffer for value actually
3466 * has value_bytes_len bytes in it.
3468 value_bytes_len
= line_end_offset
- value_offset
;
3469 value_bytes
= (char *)wmem_alloc((scope
? scope
: pinfo
->pool
), value_bytes_len
+1);
3470 memcpy(value_bytes
, &line
[value_offset
- offset
], value_bytes_len
);
3471 value_bytes
[value_bytes_len
] = '\0';
3472 value
= tvb_get_string_enc(pinfo
->pool
, tvb
, value_offset
, value_bytes_len
, ENC_ASCII
);
3473 /* The length of the value might change after UTF-8 sanitization */
3474 value_len
= (int)strlen(value
);
3476 if (scope
== pinfo
->pool
) {
3477 wmem_map_insert(header_value_map
, header_name
, value_bytes
);
3478 } else if (scope
) { /* (!PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */
3479 wmem_map_insert(header_value_map
, wmem_strdup(scope
, header_name
), value_bytes
);
3480 } /* else skip while (PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */
3482 if (hf_index
== -1) {
3484 * Not a header we know anything about.
3485 * Check if a HF generated from UAT information exists.
3487 hf_id
= get_hf_for_header(header_name
);
3491 if (http_type
== MEDIA_CONTAINER_HTTP_REQUEST
||
3492 http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
3493 it
= proto_tree_add_item(tree
,
3494 http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
?
3495 hf_http_response_line
:
3496 hf_http_request_line
,
3499 proto_item_set_text(it
, "%s",
3500 format_text(pinfo
->pool
, line
, len
));
3502 char* str
= format_text(pinfo
->pool
, line
, len
);
3503 proto_tree_add_string_format(tree
, hf_http_unknown_header
, tvb
, offset
,
3504 len
, str
, "%s", str
);
3508 proto_tree_add_string_format(tree
,
3509 *hf_id
, tvb
, offset
, len
, value
,
3510 "%s", format_text(pinfo
->pool
, line
, len
));
3511 if (http_type
== MEDIA_CONTAINER_HTTP_REQUEST
||
3512 http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
3513 it
= proto_tree_add_item(tree
,
3514 http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
?
3515 hf_http_response_line
:
3516 hf_http_request_line
,
3519 proto_item_set_text(it
, "%s",
3520 format_text(pinfo
->pool
, line
, len
));
3521 proto_item_set_hidden(it
);
3527 * Add it to the protocol tree as a particular field,
3528 * but display the line as is.
3531 header_field_info
*hfinfo
;
3534 hfinfo
= proto_registrar_get_nth(*headers
[hf_index
].hf
);
3535 switch(hfinfo
->type
){
3544 tmp
=(uint32_t)strtol(value
, NULL
, 10);
3545 hdr_item
= proto_tree_add_uint(tree
, *headers
[hf_index
].hf
, tvb
, offset
, len
, tmp
);
3546 if (http_type
== MEDIA_CONTAINER_HTTP_REQUEST
||
3547 http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
3548 it
= proto_tree_add_item(tree
,
3549 http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
?
3550 hf_http_response_line
:
3551 hf_http_request_line
,
3554 proto_item_set_text(it
, "%d", tmp
);
3555 proto_item_set_hidden(it
);
3559 hdr_item
= proto_tree_add_string_format(tree
,
3560 *headers
[hf_index
].hf
, tvb
, offset
, len
,
3562 "%s", format_text(pinfo
->pool
, line
, len
));
3563 if (http_type
== MEDIA_CONTAINER_HTTP_REQUEST
||
3564 http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
3565 it
= proto_tree_add_item(tree
,
3566 http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
?
3567 hf_http_response_line
:
3568 hf_http_request_line
,
3571 proto_item_set_text(it
, "%s",
3572 format_text(pinfo
->pool
, line
, len
));
3573 proto_item_set_hidden(it
);
3580 * Do any special processing that particular headers
3583 switch (headers
[hf_index
].special
) {
3585 case HDR_AUTHORIZATION
:
3586 if (check_auth_ntlmssp(hdr_item
, tvb
, pinfo
, value
))
3587 break; /* dissected NTLMSSP */
3588 if (check_auth_basic(hdr_item
, tvb
, pinfo
, value
))
3589 break; /* dissected basic auth */
3590 if (check_auth_citrixbasic(hdr_item
, tvb
, pinfo
, value
, offset
))
3591 break; /* dissected citrix basic auth */
3592 if (check_auth_kerberos(hdr_item
, tvb
, pinfo
, value
))
3594 if (check_auth_digest(hdr_item
, tvb
, pinfo
, value
, offset
, value_len
))
3595 break;/* dissected digest basic auth */
3596 auth
= wmem_new0(pinfo
->pool
, tap_credential_t
);
3597 auth
->num
= pinfo
->num
;
3598 auth
->password_hf_id
= *headers
[hf_index
].hf
;
3599 auth
->proto
= "HTTP header auth";
3600 auth
->username
= wmem_strdup(pinfo
->pool
, TAP_CREDENTIALS_PLACEHOLDER
);
3601 tap_queue_packet(credentials_tap
, pinfo
, auth
);
3604 case HDR_AUTHENTICATE
:
3605 if (check_auth_ntlmssp(hdr_item
, tvb
, pinfo
, value
))
3606 break; /* dissected NTLMSSP */
3607 check_auth_kerberos(hdr_item
, tvb
, pinfo
, value
);
3610 case HDR_CONTENT_TYPE
:
3611 if (scope
== NULL
) { /* identical to (PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */
3612 break; /* eh_ptr->content_type[_parameters] must have been set during first scan */
3614 eh_ptr
->content_type
= wmem_strdup(scope
, value
);
3616 for (f
= 0; f
< value_len
; f
++) {
3618 if (c
== ';' || g_ascii_isspace(c
)) {
3620 * End of subtype - either
3621 * white space or a ";"
3622 * separating the subtype from
3629 * Map the character to lower case;
3630 * content types are case-insensitive.
3632 eh_ptr
->content_type
[f
] = g_ascii_tolower(eh_ptr
->content_type
[f
]);
3634 eh_ptr
->content_type
[f
] = '\0';
3636 * Now find the start of the optional parameters;
3637 * skip the optional white space and the semicolon
3638 * if this has not been done before.
3641 while (f
< value_len
) {
3642 c
= eh_ptr
->content_type
[f
];
3643 if (c
== ';' || g_ascii_isspace(c
))
3644 /* Skip till start of parameters */
3650 eh_ptr
->content_type_parameters
= eh_ptr
->content_type
+ f
;
3652 eh_ptr
->content_type_parameters
= NULL
;
3655 case HDR_CONTENT_LENGTH
:
3656 DISSECTOR_ASSERT_HINT(!streaming_chunk_mode
, "In streaming chunk mode, there will never be content-length header.");
3658 eh_ptr
->content_length
= g_ascii_strtoll(value
, &p
, 10);
3659 up
= (unsigned char *)p
;
3660 if (eh_ptr
->content_length
< 0 ||
3663 (*up
!= '\0' && !g_ascii_isspace(*up
))) {
3665 * Content length not valid; pretend
3668 eh_ptr
->have_content_length
= false;
3670 proto_tree
*header_tree
;
3671 proto_item
*tree_item
;
3673 * We do have a valid content length.
3675 eh_ptr
->have_content_length
= true;
3676 header_tree
= proto_item_add_subtree(hdr_item
, ett_http_header_item
);
3677 tree_item
= proto_tree_add_uint64(header_tree
, hf_http_content_length
,
3678 tvb
, offset
, len
, eh_ptr
->content_length
);
3679 proto_item_set_generated(tree_item
);
3680 if (eh_ptr
->transfer_encoding
!= HTTP_TE_NONE
) {
3681 expert_add_info(pinfo
, hdr_item
, &ei_http_te_and_length
);
3686 case HDR_CONTENT_ENCODING
:
3687 if (scope
== NULL
) { /* identical to (PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */
3688 break; /* eh_ptr->content_encoding must have been set during first scan */
3690 eh_ptr
->content_encoding
= wmem_strndup(scope
, value
, value_len
);
3693 case HDR_TRANSFER_ENCODING
:
3694 if (eh_ptr
->have_content_length
) {
3695 expert_add_info(pinfo
, hdr_item
, &ei_http_te_and_length
);
3697 if (!http_parse_transfer_coding(value
, eh_ptr
)) {
3698 expert_add_info(pinfo
, hdr_item
, &ei_http_te_unknown
);
3703 stat_info
->http_host
= wmem_strndup(pinfo
->pool
, value
, value_len
);
3704 if (!PINFO_FD_VISITED(pinfo
) && curr_req_res
) {
3705 curr_req_res
->http_host
= wmem_strndup(wmem_file_scope(), value
, value_len
);
3710 if (scope
== NULL
) { /* identical to (PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */
3713 eh_ptr
->upgrade
= wmem_ascii_strdown(scope
, value
, value_len
);
3718 proto_tree
*cookie_tree
;
3719 char *part
, *part_end
;
3722 cookie_tree
= proto_item_add_subtree(hdr_item
, ett_http_header_item
);
3723 for (f
= 0; f
< value_len
; ) {
3724 /* skip whitespace and ';' (terminates at '\0' or earlier) */
3726 while (c
== ';' || g_ascii_isspace(c
))
3732 /* find "cookie=foo " in "cookie=foo ; bar" */
3734 part_end
= (char *)memchr(part
, ';', value_len
- f
);
3736 part_len
=(int)(part_end
- part
);
3738 part_len
= value_len
- f
;
3740 /* finally add cookie to tree */
3741 proto_tree_add_item(cookie_tree
, hf_http_cookie_pair
,
3742 tvb
, value_offset
+ f
, part_len
, ENC_NA
|ENC_ASCII
);
3748 case HDR_WEBSOCKET_PROTOCOL
:
3749 if (http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
3750 conv_data
->websocket_protocol
= wmem_strndup(wmem_file_scope(), value
, value_len
);
3754 case HDR_WEBSOCKET_EXTENSIONS
:
3755 if (http_type
== MEDIA_CONTAINER_HTTP_RESPONSE
) {
3756 conv_data
->websocket_extensions
= wmem_strndup(wmem_file_scope(), value
, value_len
);
3761 stat_info
->referer_uri
= wmem_strndup(pinfo
->pool
, value
, value_len
);
3765 if (curr_req_res
&& curr_req_res
->request_uri
){
3766 stat_info
->location_target
= wmem_strndup(pinfo
->pool
, value
, value_len
);
3767 stat_info
->location_base_uri
= wmem_strdup(pinfo
->pool
, curr_req_res
->full_uri
);
3770 case HDR_HTTP2_SETTINGS
:
3772 proto_tree
* settings_tree
= proto_item_add_subtree(hdr_item
, ett_http_http2_settings_item
);
3773 tvbuff_t
* new_tvb
= base64uri_tvb_to_new_tvb(tvb
, value_offset
, value_bytes_len
);
3774 add_new_data_source(pinfo
, new_tvb
, "Base64uri decoded");
3776 dissect_http2_settings_ext(new_tvb
, pinfo
, settings_tree
, 0);
3778 show_exception(tvb
, pinfo
, settings_tree
, EXCEPT_CODE
, GET_MESSAGE
);
3786 /* THIS IS A GET REQUEST
3787 * Note: GET is the only method that employs ranges.
3788 * (Unless the data has errors or is noncompliant.)
3790 if (curr_req_res
&& !pinfo
->fd
->visited
) {
3792 * Unlike protocols such as NFS and SMB, the HTTP protocol (RFC 9110) does not
3793 * provide an identifier with which to match requests and responses. Instead,
3794 * matching is solely based upon the order in which responses are received.
3795 * HTTP I/O is 'asynchronously ordered' such that, for example, the first of four
3796 * GET responses are matched with the first outstanding request, the next
3797 * response with the second oldest outstanding request and so on (FIFO).
3798 * The previous method instead matched responses with the last of several
3799 * async requests rather than the first (LIFO), and did not handle requests
3800 * with no responses such as the case where one or more HTTP packets were
3801 * not captured. Whenever there were multiple outstanding requests, the SRT
3802 * (RTT) stats were incorrect, in some cases massively so.
3804 * While RFC 9110 expressly prohibits matching via byte ranges because, among
3805 * other things, the server may return fewer bytes than requested,
3806 * the first number of the range does not change. Unlike HTTP implementations,
3807 * Wireshark has the problem of requests/responses missing from the capture
3808 * file. In such cases resumption of correct matching was virtually impossible.
3809 * In addition, all matching was incorrect from that point on.
3811 * The method of matching used herein is able to recover from packet loss,
3812 * any number of missing frames, and duplicate range requests. The
3813 * method used is explained within the comments.
3815 /* https://www.rfc-editor.org/rfc/rfc9110.html#name-range-requests
3816 * Note that RFC 9110 16.5 defines a registry for
3817 * range units, but only bytes are registered.
3819 * Range = ranges-specifier
3820 * ranges-specifier = range-unit "=" range-set
3821 * range-set = 1#range-spec
3822 * range-spec = int-range / suffix-range / other-range
3823 * 1# is an ABNF extension defined in RFC 9110 5.6.1
3824 * which covers comma separated list with optional
3826 * 1#element => element *( OWS "," OWS element )
3827 * We don't care about other-range, but will try to
3828 * handle int-range and suffix-range.
3829 * This ignores any entries past the first in a list,
3830 * though responses to such would be multipart.
3831 * As mentioned above, this breaks down if the
3832 * response does not include all requested ranges
3833 * fully in one response.
3835 const char *pos
= strchr(value
, '=');
3840 uint64_t first_range_num
= 0;
3841 /* Get the first range number */
3842 ws_strtou64(pos
, &pos
, &first_range_num
);
3843 /* If the first number of the range is missing or '0',
3844 * use the second number in the range instead if we can.
3845 * XXX - Unlike strtoul, we can check the return value
3846 * of ws_strtou64() to distinguish between "converted
3847 * successfully as 0" and "failed conversion."
3848 * Note that strtoul allows an unsigned integer to
3849 * begin with a negative sign and applies unsigned
3850 * integer wraparound rules.
3851 * ws_strtouXX rejects an initial hyphen-minus, which
3852 * is good, as we want to properly handle:
3853 * suffix-range = "-" suffix-length
3855 if (first_range_num
== 0 && *pos
== '-') {
3857 /* Pass in an end pointer to convert
3858 * a list of ranges, the first of which is
3861 ws_strtou64(pos
, &pos
, &first_range_num
);
3863 /* req_list is used for req/resp matching and the deletion (and freeing) of matching
3864 * requests and any orphans that preceed them. A GSList is used instead of a wmem map
3865 * because there are rarely more than 10 requests in the list."
3867 if (first_range_num
> 0) {
3868 request_trans_t
* req_trans
= wmem_new(wmem_file_scope(), request_trans_t
);
3869 req_trans
->first_range_num
= first_range_num
;
3870 req_trans
->req_frame
= pinfo
->num
;
3871 req_trans
->abs_time
= pinfo
->fd
->abs_ts
;
3872 req_trans
->request_uri
= curr_req_res
->request_uri
;
3874 /* XXX - This leaks if matching responses aren't
3875 * found (the data does not, but the list node
3876 * does.) A wmem_list would prevent that.
3878 conv_data
->req_list
= g_slist_append(conv_data
->req_list
, GUINT_TO_POINTER(req_trans
));
3879 curr_req_res
->req_has_range
= true;
3885 case HDR_CONTENT_RANGE
:
3887 * THIS IS A GET RESPONSE
3888 * GET is the only method that employs ranges.
3889 * XXX - Except that RFC 9110 14.4 & 14.5 note that by
3890 * private agreement it can be included in a request
3891 * to request a partial PUT.
3893 * Content-Range = range-unit SP ( range-resp / unsatisfied-range )
3894 * range-resp = incl-range "/" ( complete-length / "*" )
3895 * We do not attempt to handle unsatisfied-range.
3896 * Note that only one range can be included; multiple
3897 * ranges are transmitted with the media type of
3898 * "multipart/byteranges" and each body part contains
3899 * its own Content-Type and Content-Range fields.
3900 * The multipart dissector does not handle this nor
3901 * access the request list.
3903 if (curr_req_res
&& !pinfo
->fd
->visited
) {
3904 request_trans_t
*req_trans
;
3905 match_trans_t
*match_trans
= NULL
;
3907 GSList
*iter
= NULL
;
3909 /* Note SP instead of '=' in ABNF. */
3910 const char *pos
= strchr(value
, ' ');
3915 uint64_t first_crange_num
= 0;
3916 /* Get the first content range number */
3917 ws_strtou64(pos
, &pos
, &first_crange_num
);
3919 if (first_crange_num
== 0 && *pos
== '-') {
3921 ws_strtou64(pos
, &pos
, &first_crange_num
);
3924 /* Get the position of the matching request if any in the reqs_table.
3925 * This is used to remove and free the matching request, and the unmatched
3926 * requests (orphans) that preceed it.
3927 * XXX - There is *NO* guarantee that there is
3928 * a perfectly matching request, see 15.3.7:
3929 * "However, a server might want to send only a
3930 * subset of the data requested for reasons of
3931 * its own... A client MUST inspect a 206
3932 * response's Content-Type and Content-Range
3933 * field(s) to determine what parts are enclosed
3934 * and whether additional requests are needed."
3935 * Also 15.3.7.2 Multiple Parts, noting that
3936 * the response may be sent in a Content-Type
3937 * multipart/byteranges, also "When multiple
3938 * ranges are requested, a server MAY coalesce
3939 * any of the ranges that overlap, or that are
3940 * separated by a gap that is smaller than the
3941 * overhead of sending multiple parts, regardless
3942 * of the order in which the corresponding range-
3943 * spec appeared in the received Range header
3944 * field." and 15.3.7.3 Combining Parts.
3945 * However, as mentioned above, the LIFO method
3946 * had issues with that as well. Truly proper
3947 * handling of such edge cases is more difficult.
3950 if (conv_data
->req_list
&& conv_data
->req_list
->data
) {
3951 for (iter
= conv_data
->req_list
; iter
; iter
= iter
->next
) {
3952 if (((request_trans_t
*)iter
->data
)->first_range_num
== first_crange_num
) {
3953 req_trans
= iter
->data
;
3959 if (first_crange_num
!= 0 && req_trans
) {
3960 match_trans
= wmem_new(wmem_file_scope(), match_trans_t
);
3961 match_trans
->req_frame
= req_trans
->req_frame
;
3962 match_trans
->resp_frame
= pinfo
->num
;
3963 nstime_delta(&ns
, &pinfo
->fd
->abs_ts
, &req_trans
->abs_time
);
3964 match_trans
->delta_time
= ns
;
3965 match_trans
->request_uri
= req_trans
->request_uri
;
3966 match_trans
->http_host
= curr_req_res
->http_host
;
3968 wmem_map_insert(conv_data
->matches_table
,
3969 GUINT_TO_POINTER(match_trans
->req_frame
), (void *)match_trans
);
3970 wmem_map_insert(conv_data
->matches_table
,
3971 GUINT_TO_POINTER(match_trans
->resp_frame
), (void *)match_trans
);
3973 /* Remove and free all of the list entries up to and including the
3974 * matching one from req_list. */
3975 if (conv_data
->req_list
) {
3976 GSList
*top_of_list
= NULL
;
3978 top_of_list
= conv_data
->req_list
;
3979 while (top_of_list
&& top_of_list
->data
!= req_trans
) {
3981 top_of_list
= g_slist_delete_link(top_of_list
, top_of_list
);
3983 if (top_of_list
&& top_of_list
->data
== req_trans
) {
3985 top_of_list
= g_slist_delete_link(top_of_list
, top_of_list
);
3987 conv_data
->req_list
= top_of_list
;
3992 curr_req_res
->resp_has_range
= true;
3999 /* Returns index of header tag in headers */
4001 find_header_hf_value(tvbuff_t
*tvb
, int offset
, unsigned header_len
)
4005 for (i
= 0; i
< array_length(headers
); i
++) {
4006 if (header_len
== strlen(headers
[i
].name
) &&
4007 tvb_strncaseeql(tvb
, offset
,
4008 headers
[i
].name
, header_len
) == 0)
4016 * Dissect Microsoft's abomination called NTLMSSP over HTTP.
4019 check_auth_ntlmssp(proto_item
*hdr_item
, tvbuff_t
*tvb
, packet_info
*pinfo
, char *value
)
4021 static const char *ntlm_headers
[] = {
4026 const char **header
;
4028 proto_tree
*hdr_tree
;
4031 * Check for NTLM credentials and challenge; those can
4032 * occur with WWW-Authenticate.
4034 for (header
= &ntlm_headers
[0]; *header
!= NULL
; header
++) {
4035 hdrlen
= strlen(*header
);
4036 if (strncmp(value
, *header
, hdrlen
) == 0) {
4037 if (hdr_item
!= NULL
) {
4038 hdr_tree
= proto_item_add_subtree(hdr_item
,
4043 dissect_http_ntlmssp(tvb
, pinfo
, hdr_tree
, value
);
4050 static tap_credential_t
*
4051 basic_auth_credentials(wmem_allocator_t
*scope
, const char* str
)
4053 char **tokens
= g_strsplit(str
, ":", -1);
4055 if (!tokens
|| !tokens
[0] || !tokens
[1]) {
4060 tap_credential_t
* auth
= wmem_new0(scope
, tap_credential_t
);
4062 auth
->username
= wmem_strdup(scope
, tokens
[0]);
4063 auth
->proto
= "HTTP basic auth";
4071 * Dissect HTTP Basic authorization.
4074 check_auth_basic(proto_item
*hdr_item
, tvbuff_t
*tvb
, packet_info
*pinfo
, char *value
)
4076 static const char *basic_headers
[] = {
4080 const char **header
;
4082 const uint8_t *decoded_value
;
4083 proto_tree
*hdr_tree
;
4086 for (header
= &basic_headers
[0]; *header
!= NULL
; header
++) {
4087 hdrlen
= strlen(*header
);
4088 if (strncmp(value
, *header
, hdrlen
) == 0) {
4089 if (hdr_item
!= NULL
) {
4090 hdr_tree
= proto_item_add_subtree(hdr_item
,
4096 auth_tvb
= base64_to_tvb(tvb
, value
);
4097 add_new_data_source(pinfo
, auth_tvb
, "Basic Credentials");
4098 /* RFC 7617 says that the character encoding is only
4099 * known to be UTF-8 if the 'charset' parameter was
4100 * used. Otherwise, after Base64 decoding it could be
4101 * any character encoding.
4102 * XXX: Perhaps the field should be a FT_BYTES with
4103 * BASE_SHOW_UTF_8_PRINTABLE?
4105 proto_tree_add_item_ret_string(hdr_tree
, hf_http_basic
, auth_tvb
, 0, tvb_reported_length(auth_tvb
), ENC_UTF_8
, pinfo
->pool
, &decoded_value
);
4106 tap_credential_t
* auth
= basic_auth_credentials(pinfo
->pool
, decoded_value
);
4108 auth
->num
= auth
->username_num
= pinfo
->num
;
4109 auth
->password_hf_id
= hf_http_basic
;
4110 tap_queue_packet(credentials_tap
, pinfo
, auth
);
4120 * Dissect HTTP Digest authorization.
4123 check_auth_digest(proto_item
* hdr_item
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
, char* value
, int offset
, int len
)
4125 proto_tree
* hdr_tree
;
4128 if (strncmp(value
, "Digest", 6) == 0) {
4129 if (hdr_item
!= NULL
) {
4130 hdr_tree
= proto_item_add_subtree(hdr_item
, ett_http_ntlmssp
);
4137 /* Find comma/end of line */
4138 queried_offset
= tvb_find_uint8(tvb
, offset
, len
, ',');
4139 if (queried_offset
> 0) {
4140 proto_tree_add_format_text(hdr_tree
, tvb
, offset
, queried_offset
- offset
);
4141 len
-= (queried_offset
- offset
);
4142 offset
= queried_offset
+ 1;
4153 * Dissect HTTP CitrixAGBasic authorization.
4156 check_auth_citrixbasic(proto_item
*hdr_item
, tvbuff_t
*tvb
, packet_info
*pinfo
, char *value
, int offset
)
4158 static const char *basic_headers
[] = {
4162 const char **header
;
4164 proto_tree
*hdr_tree
;
4168 proto_item
*hidden_item
;
4170 const uint8_t *user
= NULL
, *passwd
= NULL
;
4172 for (header
= &basic_headers
[0]; *header
!= NULL
; header
++) {
4173 hdrlen
= strlen(*header
);
4174 if (strncmp(value
, *header
, hdrlen
) == 0) {
4175 if (hdr_item
!= NULL
) {
4176 hdr_tree
= proto_item_add_subtree(hdr_item
,
4181 offset
+= (int)hdrlen
+ 15;
4182 hidden_item
= proto_tree_add_boolean(hdr_tree
,
4183 hf_http_citrix
, tvb
, 0, 0, 1);
4184 proto_item_set_hidden(hidden_item
);
4186 if(strncmp(value
, "username=\"", 10) == 0) {
4189 ch_ptr
= strchr(value
, '"');
4190 if ( ch_ptr
!= NULL
) {
4191 data_len
= (int)(ch_ptr
- value
);
4193 data_tvb
= base64_tvb_to_new_tvb(tvb
, offset
, data_len
);
4194 add_new_data_source(pinfo
, data_tvb
, "Username");
4195 /* XXX: We don't know for certain the string encoding here. */
4196 pi
= proto_tree_add_item_ret_string(hdr_tree
, hf_http_citrix_user
, data_tvb
, 0, tvb_reported_length(data_tvb
), ENC_UTF_8
, pinfo
->pool
, &user
);
4198 pi
= proto_tree_add_string(hdr_tree
, hf_http_citrix_user
, tvb
, offset
, 0, "");
4200 proto_item_set_generated(pi
);
4201 value
+= data_len
+ 1;
4202 offset
+= data_len
+ 1;
4205 if(strncmp(value
, "; domain=\"", 10) == 0) {
4208 ch_ptr
= strchr(value
, '"');
4209 if ( ch_ptr
!= NULL
) {
4210 data_len
= (int)(ch_ptr
- value
);
4212 data_tvb
= base64_tvb_to_new_tvb(tvb
, offset
, data_len
);
4213 add_new_data_source(pinfo
, data_tvb
, "Domain");
4214 pi
= proto_tree_add_item(hdr_tree
, hf_http_citrix_domain
, data_tvb
, 0, tvb_reported_length(data_tvb
), ENC_UTF_8
);
4216 pi
= proto_tree_add_string(hdr_tree
, hf_http_citrix_domain
, tvb
, offset
, 0, "");
4218 proto_item_set_generated(pi
);
4219 value
+= data_len
+ 1;
4220 offset
+= data_len
+ 1;
4223 if(strncmp(value
, "; password=\"", 12) == 0) {
4226 ch_ptr
= strchr(value
, '"');
4227 if ( ch_ptr
!= NULL
) {
4228 data_len
= (int)(ch_ptr
- value
);
4230 data_tvb
= base64_tvb_to_new_tvb(tvb
, offset
, data_len
);
4231 add_new_data_source(pinfo
, data_tvb
, "Password");
4232 pi
= proto_tree_add_item_ret_string(hdr_tree
, hf_http_citrix_passwd
, data_tvb
, 0, tvb_reported_length(data_tvb
), ENC_UTF_8
, pinfo
->pool
, &passwd
);
4234 pi
= proto_tree_add_string(hdr_tree
, hf_http_citrix_passwd
, tvb
, offset
, 0, "");
4236 proto_item_set_generated(pi
);
4237 value
+= data_len
+ 1;
4238 offset
+= data_len
+ 1;
4241 if(strncmp(value
, "; AGESessionId=\"", 16) == 0) {
4244 ch_ptr
= strchr(value
, '"');
4245 if ( ch_ptr
!= NULL
) {
4246 data_len
= (int)(ch_ptr
- value
);
4248 data_tvb
= base64_tvb_to_new_tvb(tvb
, offset
, data_len
);
4249 add_new_data_source(pinfo
, data_tvb
, "Session ID");
4250 pi
= proto_tree_add_item(hdr_tree
, hf_http_citrix_session
, data_tvb
, 0, tvb_reported_length(data_tvb
), ENC_UTF_8
);
4252 pi
= proto_tree_add_string(hdr_tree
, hf_http_citrix_session
, tvb
,
4255 proto_item_set_generated(pi
);
4258 if (user
!= NULL
&& passwd
!= NULL
) {
4260 tap_credential_t
* auth
= wmem_new0(pinfo
->pool
, tap_credential_t
);
4262 auth
->username
= wmem_strdup(pinfo
->pool
, user
);
4263 auth
->proto
= "HTTP CitrixAGBasic auth";
4264 auth
->num
= auth
->username_num
= pinfo
->num
;
4265 auth
->password_hf_id
= hf_http_citrix_passwd
;
4266 tap_queue_packet(credentials_tap
, pinfo
, auth
);
4275 check_auth_kerberos(proto_item
*hdr_item
, tvbuff_t
*tvb
, packet_info
*pinfo
, const char *value
)
4277 proto_tree
*hdr_tree
;
4279 if (strncmp(value
, "Kerberos ", 9) == 0) {
4280 if (hdr_item
!= NULL
) {
4281 hdr_tree
= proto_item_add_subtree(hdr_item
, ett_http_kerberos
);
4285 dissect_http_kerberos(tvb
, pinfo
, hdr_tree
, value
);
4292 dissect_http_on_stream(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
4293 http_conv_t
*conv_data
, bool end_of_stream
, const uint32_t *seq
)
4298 while (tvb_reported_length_remaining(tvb
, offset
) > 0) {
4299 /* Switch protocol if the data starts after response headers. */
4300 if (conv_data
->startframe
&&
4301 (pinfo
->num
> conv_data
->startframe
||
4302 (pinfo
->num
== conv_data
->startframe
&& offset
>= conv_data
->startoffset
))) {
4303 /* Increase pinfo->can_desegment because we are traversing
4304 * http and want to preserve desegmentation functionality for
4305 * the proxied protocol
4307 if (pinfo
->can_desegment
> 0)
4308 pinfo
->can_desegment
++;
4309 if (conv_data
->next_handle
) {
4310 call_dissector_only(conv_data
->next_handle
, tvb_new_subset_remaining(tvb
, offset
), pinfo
, tree
, NULL
);
4312 call_data_dissector(tvb_new_subset_remaining(tvb
, offset
), pinfo
, tree
);
4315 * If a subdissector requests reassembly, be sure not to
4316 * include the preceding HTTP headers.
4318 if (pinfo
->desegment_len
) {
4319 pinfo
->desegment_offset
+= offset
;
4323 len
= dissect_http_message(tvb
, offset
, pinfo
, tree
, conv_data
, "HTTP", proto_http
, end_of_stream
, seq
);
4329 * OK, we've set the Protocol and Info columns for the
4330 * first HTTP message; set a fence so that subsequent
4331 * HTTP messages don't overwrite the Info column.
4333 col_set_fence(pinfo
->cinfo
, COL_INFO
);
4335 /* dissect_http_message() returns -2 if message is not valid HTTP */
4338 : (int)tvb_captured_length(tvb
);
4342 dissect_http_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
4344 struct tcpinfo
*tcpinfo
= (struct tcpinfo
*)data
;
4345 conversation_t
*conversation
;
4346 http_conv_t
*conv_data
;
4349 conv_data
= get_http_conversation_data(pinfo
, &conversation
);
4351 /* Call HTTP2 dissector directly when detected via heuristics, but not
4352 * when it was upgraded (the conversation started with HTTP). */
4353 if (conversation_get_proto_data(conversation
, proto_http2
) &&
4354 !conv_data
->startframe
) {
4355 if (pinfo
->can_desegment
> 0)
4356 pinfo
->can_desegment
++;
4357 return call_dissector_only(http2_handle
, tvb
, pinfo
, tree
, data
);
4361 * Check if this is proxied connection and if so, hand of dissection to the
4362 * payload-dissector.
4363 * Response code 200 means "OK" and strncmp() == 0 means the strings match exactly */
4364 http_req_res_t
*curr_req_res
= conv_data
->req_res_tail
;
4365 if(pinfo
->num
>= conv_data
->startframe
&&
4367 curr_req_res
->response_code
== 200 &&
4368 curr_req_res
->request_method
&&
4369 strncmp(curr_req_res
->request_method
, "CONNECT", 7) == 0 &&
4370 curr_req_res
->request_uri
) {
4371 if (conv_data
->startframe
== 0 && !PINFO_FD_VISITED(pinfo
)) {
4372 conv_data
->startframe
= pinfo
->num
;
4373 conv_data
->startoffset
= 0;
4374 copy_address_wmem(wmem_file_scope(), &conv_data
->server_addr
, &pinfo
->dst
);
4375 conv_data
->server_port
= pinfo
->destport
;
4377 http_payload_subdissector(tvb
, tree
, pinfo
, conv_data
, data
);
4379 return tvb_captured_length(tvb
);
4382 /* XXX - how to detect end-of-stream without tcpinfo */
4383 end_of_stream
= (tcpinfo
&& IS_TH_FIN(tcpinfo
->flags
));
4384 return dissect_http_on_stream(tvb
, pinfo
, tree
, conv_data
, end_of_stream
, tcpinfo
? &tcpinfo
->seq
: NULL
);
4388 dissect_http_heur_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
4390 int offset
= 0, next_offset
, linelen
;
4391 conversation_t
*conversation
;
4394 /* Check if we have a line terminated by CRLF
4395 * Return the length of the line (not counting the line terminator at
4396 * the end), or, if we don't find a line terminator:
4398 * if "deseg" is true, return -1;
4400 linelen
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, true);
4401 if((linelen
== -1)||(linelen
== 8)){
4405 /* Check if the line start or ends with the HTTP token */
4406 if((tvb_strncaseeql(tvb
, linelen
-8, "HTTP/1.", 7) == 0)||(tvb_strncaseeql(tvb
, 0, "HTTP/1.", 7) == 0)){
4407 conversation
= find_or_create_conversation(pinfo
);
4408 conversation_set_dissector_from_frame_number(conversation
, pinfo
->num
, http_tcp_handle
);
4409 dissect_http_tcp(tvb
, pinfo
, tree
, data
);
4417 dissect_http_tls(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
4419 conversation_t
*conversation
;
4420 http_conv_t
*conv_data
;
4423 conv_data
= get_http_conversation_data(pinfo
, &conversation
);
4425 struct tlsinfo
*tlsinfo
= (struct tlsinfo
*)data
;
4426 end_of_stream
= (tlsinfo
&& tlsinfo
->end_of_stream
);
4427 return dissect_http_on_stream(tvb
, pinfo
, tree
, conv_data
, end_of_stream
, tlsinfo
? &tlsinfo
->seq
: NULL
);
4431 dissect_http_heur_tls(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
4433 int offset
= 0, next_offset
, linelen
;
4434 conversation_t
*conversation
;
4435 http_conv_t
*conv_data
;
4437 conversation
= find_or_create_conversation(pinfo
);
4438 conv_data
= (http_conv_t
*)conversation_get_proto_data(conversation
, proto_http
);
4439 /* A http conversation was previously started, assume it is still active */
4441 dissect_http_tls(tvb
, pinfo
, tree
, data
);
4445 /* Check if we have a line terminated by CRLF
4446 * Return the length of the line (not counting the line terminator at
4447 * the end), or, if we don't find a line terminator:
4449 * if "deseg" is true, return -1;
4451 linelen
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, true);
4452 if((linelen
== -1)||(linelen
== 8)){
4456 /* Check if the line start or ends with the HTTP token */
4457 if((tvb_strncaseeql(tvb
, linelen
-8, "HTTP/1.", 7) != 0) && (tvb_strncaseeql(tvb
, 0, "HTTP/1.", 7) != 0)) {
4458 /* we couldn't find the Magic Hello HTTP/1.X. */
4462 dissect_http_tls(tvb
, pinfo
, tree
, data
);
4467 dissect_http_sctp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
4469 conversation_t
*conversation
;
4470 http_conv_t
*conv_data
;
4472 conv_data
= get_http_conversation_data(pinfo
, &conversation
);
4475 * XXX - we need to provide an end-of-stream indication.
4477 return dissect_http_on_stream(tvb
, pinfo
, tree
, conv_data
, false, NULL
);
4481 dissect_http(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
4483 conversation_t
*conversation
;
4484 http_conv_t
*conv_data
;
4486 conv_data
= get_http_conversation_data(pinfo
, &conversation
);
4489 * XXX - what should be done about reassembly, pipelining, etc.
4492 return dissect_http_on_stream(tvb
, pinfo
, tree
, conv_data
, false, NULL
);
4496 dissect_ssdp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
4498 conversation_t
*conversation
;
4499 http_conv_t
*conv_data
;
4501 conv_data
= get_http_conversation_data(pinfo
, &conversation
);
4502 dissect_http_message(tvb
, 0, pinfo
, tree
, conv_data
, "SSDP", proto_ssdp
, false, NULL
);
4503 return tvb_captured_length(tvb
);
4507 range_delete_http_tls_callback(uint32_t port
, void *ptr _U_
) {
4508 ssl_dissector_delete(port
, http_tls_handle
);
4512 range_add_http_tls_callback(uint32_t port
, void *ptr _U_
) {
4513 ssl_dissector_add(port
, http_tls_handle
);
4516 static void reinit_http(void) {
4517 http_tcp_range
= prefs_get_range_value("http", "tcp.port");
4519 http_sctp_range
= prefs_get_range_value("http", "sctp.port");
4521 range_foreach(http_tls_range
, range_delete_http_tls_callback
, NULL
);
4522 wmem_free(wmem_epan_scope(), http_tls_range
);
4523 http_tls_range
= range_copy(wmem_epan_scope(), global_http_tls_range
);
4524 range_foreach(http_tls_range
, range_add_http_tls_callback
, NULL
);
4528 proto_register_http(void)
4530 static hf_register_info hf
[] = {
4531 { &hf_http_notification
,
4532 { "Notification", "http.notification",
4533 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
4534 "true if HTTP notification", HFILL
}},
4535 { &hf_http_response
,
4536 { "Response", "http.response",
4537 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
4538 "true if HTTP response", HFILL
}},
4540 { "Request", "http.request",
4541 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
4542 "true if HTTP request", HFILL
}},
4544 { "Credentials", "http.authbasic",
4545 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
4547 { "Citrix AG Auth", "http.authcitrix",
4548 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
4549 "true if CitrixAGBasic Auth", HFILL
}},
4550 { &hf_http_citrix_user
,
4551 { "Citrix AG Username", "http.authcitrix.user",
4552 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
4553 { &hf_http_citrix_domain
,
4554 { "Citrix AG Domain", "http.authcitrix.domain",
4555 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
4556 { &hf_http_citrix_passwd
,
4557 { "Citrix AG Password", "http.authcitrix.password",
4558 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
4559 { &hf_http_citrix_session
,
4560 { "Citrix AG Session ID", "http.authcitrix.session",
4561 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
4562 { &hf_http_response_line
,
4563 { "Response line", "http.response.line",
4564 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
4565 { &hf_http_request_line
,
4566 { "Request line", "http.request.line",
4567 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
4568 { &hf_http_request_method
,
4569 { "Request Method", "http.request.method",
4570 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4571 "HTTP Request Method", HFILL
}},
4572 { &hf_http_request_uri
,
4573 { "Request URI", "http.request.uri",
4574 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4575 "HTTP Request-URI", HFILL
}},
4576 { &hf_http_request_path
,
4577 { "Request URI Path", "http.request.uri.path",
4578 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4579 "HTTP Request-URI Path", HFILL
}},
4580 { &hf_http_request_path_segment
,
4581 { "Request URI Path Segment", "http.request.uri.path.segment",
4582 FT_STRING
, BASE_NONE
, NULL
, 0,
4584 { &hf_http_request_query
,
4585 { "Request URI Query", "http.request.uri.query",
4586 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4587 "HTTP Request-URI Query", HFILL
}},
4588 { &hf_http_request_query_parameter
,
4589 { "Request URI Query Parameter", "http.request.uri.query.parameter",
4590 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4591 "HTTP Request-URI Query Parameter", HFILL
}},
4592 { &hf_http_request_version
,
4593 { "Request Version", "http.request.version",
4594 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4595 "HTTP Request HTTP-Version", HFILL
}},
4596 { &hf_http_response_version
,
4597 { "Response Version", "http.response.version",
4598 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4599 "HTTP Response HTTP-Version", HFILL
}},
4600 { &hf_http_request_full_uri
,
4601 { "Full request URI", "http.request.full_uri",
4602 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4603 "The full requested URI (including host name)", HFILL
}},
4604 { &hf_http_response_code
,
4605 { "Status Code", "http.response.code",
4606 FT_UINT24
, BASE_DEC
, NULL
, 0x0,
4607 "HTTP Response Status Code", HFILL
}},
4608 { &hf_http_response_code_desc
,
4609 { "Status Code Description", "http.response.code.desc",
4610 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4611 "HTTP Response Status Code Description", HFILL
}},
4612 { &hf_http_response_phrase
,
4613 { "Response Phrase", "http.response.phrase",
4614 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4615 "HTTP Response Reason Phrase", HFILL
}},
4616 { &hf_http_authorization
,
4617 { "Authorization", "http.authorization",
4618 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4619 "HTTP Authorization header", HFILL
}},
4620 { &hf_http_proxy_authenticate
,
4621 { "Proxy-Authenticate", "http.proxy_authenticate",
4622 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4623 "HTTP Proxy-Authenticate header", HFILL
}},
4624 { &hf_http_proxy_authorization
,
4625 { "Proxy-Authorization", "http.proxy_authorization",
4626 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4627 "HTTP Proxy-Authorization header", HFILL
}},
4628 { &hf_http_proxy_connect_host
,
4629 { "Proxy-Connect-Hostname", "http.proxy_connect_host",
4630 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4631 "HTTP Proxy Connect Hostname", HFILL
}},
4632 { &hf_http_proxy_connect_port
,
4633 { "Proxy-Connect-Port", "http.proxy_connect_port",
4634 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
4635 "HTTP Proxy Connect Port", HFILL
}},
4636 { &hf_http_www_authenticate
,
4637 { "WWW-Authenticate", "http.www_authenticate",
4638 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4639 "HTTP WWW-Authenticate header", HFILL
}},
4640 { &hf_http_content_type
,
4641 { "Content-Type", "http.content_type",
4642 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4643 "HTTP Content-Type header", HFILL
}},
4644 { &hf_http_content_length_header
,
4645 { "Content-Length", "http.content_length_header",
4646 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4647 "HTTP Content-Length header", HFILL
}},
4648 { &hf_http_content_length
,
4649 { "Content length", "http.content_length",
4650 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
4652 { &hf_http_content_encoding
,
4653 { "Content-Encoding", "http.content_encoding",
4654 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4655 "HTTP Content-Encoding header", HFILL
}},
4656 { &hf_http_transfer_encoding
,
4657 { "Transfer-Encoding", "http.transfer_encoding",
4658 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4659 "HTTP Transfer-Encoding header", HFILL
}},
4661 { "Upgrade", "http.upgrade",
4662 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4663 "HTTP Upgrade header", HFILL
}},
4664 { &hf_http_user_agent
,
4665 { "User-Agent", "http.user_agent",
4666 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4667 "HTTP User-Agent header", HFILL
}},
4669 { "Host", "http.host",
4670 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4671 "HTTP Host", HFILL
}},
4673 { "Range", "http.range",
4674 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4675 "HTTP Range", HFILL
}},
4676 { &hf_http_content_range
,
4677 { "Content-Range", "http.content_range",
4678 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4679 "HTTP Content-Range", HFILL
}},
4680 { &hf_http_connection
,
4681 { "Connection", "http.connection",
4682 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4683 "HTTP Connection", HFILL
}},
4685 { "Cookie", "http.cookie",
4686 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4687 "HTTP Cookie", HFILL
}},
4688 { &hf_http_cookie_pair
,
4689 { "Cookie pair", "http.cookie_pair",
4690 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4691 "A name/value HTTP cookie pair", HFILL
}},
4693 { "Accept", "http.accept",
4694 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4695 "HTTP Accept", HFILL
}},
4697 { "Referer", "http.referer",
4698 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4699 "HTTP Referer", HFILL
}},
4700 { &hf_http_accept_language
,
4701 { "Accept-Language", "http.accept_language",
4702 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4703 "HTTP Accept Language", HFILL
}},
4704 { &hf_http_accept_encoding
,
4705 { "Accept Encoding", "http.accept_encoding",
4706 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4707 "HTTP Accept Encoding", HFILL
}},
4709 { "Date", "http.date",
4710 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4711 "HTTP Date", HFILL
}},
4712 { &hf_http_cache_control
,
4713 { "Cache-Control", "http.cache_control",
4714 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4715 "HTTP Cache Control", HFILL
}},
4717 { "Server", "http.server",
4718 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4719 "HTTP Server", HFILL
}},
4720 { &hf_http_location
,
4721 { "Location", "http.location",
4722 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4723 "HTTP Location", HFILL
}},
4724 { &hf_http_sec_websocket_accept
,
4725 { "Sec-WebSocket-Accept", "http.sec_websocket_accept",
4726 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4728 { &hf_http_sec_websocket_extensions
,
4729 { "Sec-WebSocket-Extensions", "http.sec_websocket_extensions",
4730 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4732 { &hf_http_sec_websocket_key
,
4733 { "Sec-WebSocket-Key", "http.sec_websocket_key",
4734 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4736 { &hf_http_sec_websocket_protocol
,
4737 { "Sec-WebSocket-Protocol", "http.sec_websocket_protocol",
4738 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4740 { &hf_http_sec_websocket_version
,
4741 { "Sec-WebSocket-Version", "http.sec_websocket_version",
4742 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4744 { &hf_http_set_cookie
,
4745 { "Set-Cookie", "http.set_cookie",
4746 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4747 "HTTP Set Cookie", HFILL
}},
4748 { &hf_http_last_modified
,
4749 { "Last-Modified", "http.last_modified",
4750 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4751 "HTTP Last Modified", HFILL
}},
4752 { &hf_http_x_forwarded_for
,
4753 { "X-Forwarded-For", "http.x_forwarded_for",
4754 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4755 "HTTP X-Forwarded-For", HFILL
}},
4756 { &hf_http_http2_settings
,
4757 { "HTTP2-Settings", "http.http2_settings",
4758 FT_STRING
, BASE_NONE
, NULL
, 0x0,
4760 { &hf_http_request_in
,
4761 { "Request in frame", "http.request_in",
4762 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0,
4763 "This packet is a response to the packet with this number", HFILL
}},
4764 { &hf_http_response_in
,
4765 { "Response in frame", "http.response_in",
4766 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE
), 0,
4767 "This packet will be responded in the packet with this number", HFILL
}},
4769 { "Time since request", "http.time",
4770 FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0,
4771 "Time since the request was sent", HFILL
}},
4772 { &hf_http_chunked_trailer_part
,
4773 { "trailer-part", "http.chunked_trailer_part",
4774 FT_STRING
, BASE_NONE
, NULL
, 0,
4775 "Optional trailer in a chunked body", HFILL
}},
4776 { &hf_http_chunk_boundary
,
4777 { "Chunk boundary", "http.chunk_boundary",
4778 FT_BYTES
, BASE_NONE
, NULL
, 0,
4780 { &hf_http_chunk_size
,
4781 { "Chunk size", "http.chunk_size",
4782 FT_UINT32
, BASE_DEC
|BASE_UNIT_STRING
, UNS(&units_octet_octets
), 0,
4784 { &hf_http_chunk_data
,
4785 { "Chunk data", "http.chunk_data",
4786 FT_BYTES
, BASE_NONE
, NULL
, 0,
4788 { &hf_http_file_data
,
4789 { "File Data", "http.file_data",
4790 FT_BYTES
, BASE_NONE
, NULL
, 0,
4792 { &hf_http_unknown_header
,
4793 { "Unknown header", "http.unknown_header",
4794 FT_STRING
, BASE_NONE
, NULL
, 0,
4796 { &hf_http_http2_settings_uri
,
4797 { "HTTP2 Settings URI", "http.http2_settings_uri",
4798 FT_BYTES
, BASE_NONE
, NULL
, 0,
4801 /* Body fragments */
4802 REASSEMBLE_INIT_HF_ITEMS(http_body
, "HTTP Chunked Body", "http.body"),
4804 static int *ett
[] = {
4809 &ett_http_request_uri
,
4810 &ett_http_request_path
,
4811 &ett_http_request_query
,
4812 &ett_http_chunked_response
,
4813 &ett_http_chunk_data
,
4814 &ett_http_encoded_entity
,
4815 &ett_http_header_item
,
4816 &ett_http_http2_settings_item
,
4817 REASSEMBLE_INIT_ETT_ITEMS(http_body
),
4820 static ei_register_info ei
[] = {
4821 { &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
}},
4822 { &ei_http_te_unknown
, { "http.te_unknown", PI_UNDECODED
, PI_WARN
, "Unknown transfer coding name in Transfer-Encoding header", EXPFILL
}},
4823 { &ei_http_subdissector_failed
, { "http.subdissector_failed", PI_MALFORMED
, PI_NOTE
, "HTTP body subdissector failed, trying heuristic subdissector", EXPFILL
}},
4824 { &ei_http_tls_port
, { "http.tls_port", PI_SECURITY
, PI_WARN
, "Unencrypted HTTP protocol detected over encrypted port, could indicate a dangerous misconfiguration.", EXPFILL
}},
4825 { &ei_http_excess_data
, { "http.excess_data", PI_PROTOCOL
, PI_WARN
, "Excess data after a body (not a new request/response), previous Content-Length bogus?", EXPFILL
}},
4826 { &ei_http_leading_crlf
, { "http.leading_crlf", PI_MALFORMED
, PI_ERROR
, "Leading CRLF previous message in the stream may have extra CRLF", EXPFILL
}},
4827 { &ei_http_bad_header_name
, { "http.bad_header_name", PI_PROTOCOL
, PI_WARN
, "Illegal characters found in header name", EXPFILL
}},
4828 { &ei_http_decompression_failed
, { "http.decompression_failed", PI_UNDECODED
, PI_WARN
, "Decompression failed", EXPFILL
}},
4829 { &ei_http_decompression_disabled
, { "http.decompression_disabled", PI_UNDECODED
, PI_CHAT
, "Decompression disabled", EXPFILL
}}
4833 /* UAT for header fields */
4834 static uat_field_t custom_header_uat_fields
[] = {
4835 UAT_FLD_CSTRING(header_fields
, header_name
, "Header name", "HTTP header name"),
4836 UAT_FLD_CSTRING(header_fields
, header_desc
, "Field desc", "Description of the value contained in the header"),
4840 module_t
*http_module
;
4841 expert_module_t
* expert_http
;
4844 proto_http
= proto_register_protocol("Hypertext Transfer Protocol", "HTTP", "http");
4845 proto_ssdp
= proto_register_protocol("Simple Service Discovery Protocol", "SSDP", "ssdp");
4847 proto_register_field_array(proto_http
, hf
, array_length(hf
));
4848 proto_register_subtree_array(ett
, array_length(ett
));
4849 expert_http
= expert_register_protocol(proto_http
);
4850 expert_register_field_array(expert_http
, ei
, array_length(ei
));
4852 http_handle
= register_dissector("http", dissect_http
, proto_http
);
4853 http_tcp_handle
= register_dissector("http-over-tcp", dissect_http_tcp
, proto_http
);
4854 http_tls_handle
= register_dissector("http-over-tls", dissect_http_tls
, proto_http
); /* RFC 2818 */
4855 http_sctp_handle
= register_dissector("http-over-sctp", dissect_http_sctp
, proto_http
);
4857 reassembly_table_register(&http_streaming_reassembly_table
, &addresses_ports_reassembly_table_functions
);
4859 http_module
= prefs_register_protocol(proto_http
, reinit_http
);
4860 prefs_register_bool_preference(http_module
, "desegment_headers",
4861 "Reassemble HTTP headers spanning multiple TCP segments",
4862 "Whether the HTTP dissector should reassemble headers "
4863 "of a request spanning multiple TCP segments. "
4864 "To use this option, you must also enable "
4865 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
4866 &http_desegment_headers
);
4867 prefs_register_bool_preference(http_module
, "desegment_body",
4868 "Reassemble HTTP bodies spanning multiple TCP segments",
4869 "Whether the HTTP dissector should use the "
4870 "\"Content-length:\" value, if present, to reassemble "
4871 "the body of a request spanning multiple TCP segments, "
4872 "and reassemble chunked data spanning multiple TCP segments. "
4873 "To use this option, you must also enable "
4874 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
4875 &http_desegment_body
);
4876 prefs_register_bool_preference(http_module
, "dechunk_body",
4877 "Reassemble chunked transfer-coded bodies",
4878 "Whether to reassemble bodies of entities that are transferred "
4879 "using the \"Transfer-Encoding: chunked\" method",
4880 &http_dechunk_body
);
4881 prefs_register_bool_preference(http_module
, "decompress_body",
4882 "Uncompress entity bodies",
4883 "Whether to uncompress entity bodies that are compressed "
4884 "using \"Content-Encoding: \"",
4885 &http_decompress_body
);
4886 prefs_register_bool_preference(http_module
, "check_ascii_headers",
4887 "Reject non-ASCII headers as invalid HTTP",
4888 "Whether to treat non-ASCII in headers as non-HTTP data "
4889 "and allow other dissectors to process it",
4890 &http_check_ascii_headers
);
4891 prefs_register_obsolete_preference(http_module
, "tcp_alternate_port");
4893 range_convert_str(wmem_epan_scope(), &global_http_tls_range
, TLS_DEFAULT_RANGE
, 65535);
4894 prefs_register_range_preference(http_module
, "tls.port", "SSL/TLS Ports",
4895 "SSL/TLS Ports range",
4896 &global_http_tls_range
, 65535);
4897 prefs_register_obsolete_preference(http_module
, "ssl.port");
4899 headers_uat
= uat_new("Custom HTTP Header Fields",
4900 sizeof(header_field_t
),
4901 "custom_http_header_fields",
4905 /* specifies named fields, so affects dissection
4906 and the set of named fields */
4907 UAT_AFFECTS_DISSECTION
|UAT_AFFECTS_FIELDS
,
4909 header_fields_copy_cb
,
4910 header_fields_update_cb
,
4911 header_fields_free_cb
,
4912 header_fields_post_update_cb
,
4913 header_fields_reset_cb
,
4914 custom_header_uat_fields
4917 prefs_register_uat_preference(http_module
, "custom_http_header_fields", "Custom HTTP header fields",
4918 "A table to define custom HTTP header for which fields can be setup and used for filtering/data extraction etc.",
4922 * Dissectors shouldn't register themselves in this table;
4923 * instead, they should call "http_tcp_dissector_add()", and
4924 * we'll register the port number they specify as a port
4925 * for HTTP, and register them in our subdissector table.
4927 * This only works for protocols such as IPP that run over
4928 * HTTP on a specific non-HTTP port.
4930 port_subdissector_table
= register_dissector_table("http.port",
4931 "TCP port for protocols using HTTP", proto_http
, FT_UINT16
, BASE_DEC
);
4934 * Maps the lowercase Upgrade header value.
4935 * https://tools.ietf.org/html/rfc7230#section-8.6
4937 upgrade_subdissector_table
= register_dissector_table("http.upgrade", "HTTP Upgrade", proto_http
, FT_STRING
, STRING_CASE_SENSITIVE
);
4940 * Heuristic dissectors SHOULD register themselves in
4941 * this table using the standard heur_dissector_add()
4944 heur_subdissector_list
= register_heur_dissector_list_with_description("http", "HTTP payload fallback", proto_http
);
4947 * Register for tapping
4949 http_tap
= register_tap("http"); /* HTTP statistics tap */
4950 http_follow_tap
= register_tap("http_follow"); /* HTTP Follow tap */
4951 credentials_tap
= register_tap("credentials"); /* credentials tap */
4953 register_follow_stream(proto_http
, "http_follow", tcp_follow_conv_filter
, tcp_follow_index_filter
, tcp_follow_address_filter
,
4954 tcp_port_to_display
, follow_tvb_tap_listener
,
4955 get_tcp_stream_count
, NULL
);
4956 http_eo_tap
= register_export_object(proto_http
, http_eo_packet
, NULL
);
4958 /* compile patterns, excluding "/" */
4959 ws_mempbrk_compile(&pbrk_gen_delims
, ":?#[]@");
4960 /* exclude "=", separating key and value should be done separately */
4961 ws_mempbrk_compile(&pbrk_sub_delims
, "!$&'()*+,;");
4966 * Called by dissectors for protocols that run atop HTTP/TCP.
4969 http_tcp_dissector_add(uint32_t port
, dissector_handle_t handle
)
4972 * Register ourselves as the handler for that port number
4973 * over TCP. "Auto-preference" not needed
4975 dissector_add_uint("tcp.port", port
, http_tcp_handle
);
4978 * And register them in *our* table for that port.
4980 dissector_add_uint("http.port", port
, handle
);
4984 void http_tcp_dissector_delete(uint32_t port
)
4987 * Unregister ourselves as the handler for that port number
4988 * over TCP. "Auto-preference" not needed
4990 dissector_delete_uint("tcp.port", port
, NULL
);
4993 * And unregister them in *our* table for that port.
4995 dissector_delete_uint("http.port", port
, NULL
);
4999 http_tcp_port_add(uint32_t port
)
5002 * Register ourselves as the handler for that port number
5003 * over TCP. We rely on our caller having registered
5004 * themselves for the appropriate media type.
5005 * No "auto-preference" used.
5007 dissector_add_uint("tcp.port", port
, http_tcp_handle
);
5011 proto_reg_handoff_http(void)
5013 dissector_handle_t ssdp_handle
;
5015 media_handle
= find_dissector_add_dependency("media", proto_http
);
5016 http2_handle
= find_dissector("http2");
5018 * XXX - is there anything to dissect in the body of an SSDP
5019 * request or reply? I.e., should there be an SSDP dissector?
5021 ssdp_handle
= create_dissector_handle(dissect_ssdp
, proto_ssdp
);
5022 dissector_add_uint_with_preference("udp.port", UDP_PORT_SSDP
, ssdp_handle
);
5025 * TLS Application-Layer Protocol Negotiation (ALPN) protocol ID.
5027 dissector_add_string("tls.alpn", "http/1.1", http_tls_handle
);
5029 ntlmssp_handle
= find_dissector_add_dependency("ntlmssp", proto_http
);
5030 gssapi_handle
= find_dissector_add_dependency("gssapi", proto_http
);
5031 sstp_handle
= find_dissector_add_dependency("sstp", proto_http
);
5033 stats_tree_cfg
*st_config
;
5034 st_config
= stats_tree_register("http", "http", "HTTP" STATS_TREE_MENU_SEPARATOR
"Packet Counter", 0, http_stats_tree_packet
, http_stats_tree_init
, NULL
);
5035 stats_tree_set_first_column_name(st_config
, "Packet Type");
5036 st_config
= stats_tree_register("http", "http_req", "HTTP" STATS_TREE_MENU_SEPARATOR
"Requests", 0, http_req_stats_tree_packet
, http_req_stats_tree_init
, NULL
);
5037 stats_tree_set_first_column_name(st_config
, "Request Type");
5038 st_config
= stats_tree_register("http", "http_srv", "HTTP" STATS_TREE_MENU_SEPARATOR
"Load Distribution",0, http_reqs_stats_tree_packet
, http_reqs_stats_tree_init
, NULL
);
5039 stats_tree_set_first_column_name(st_config
, "Packet Type");
5040 st_config
= stats_tree_register("http", "http_seq", "HTTP" STATS_TREE_MENU_SEPARATOR
"Request Sequences",0, http_seq_stats_tree_packet
, http_seq_stats_tree_init
, NULL
);
5041 stats_tree_set_first_column_name(st_config
, "Sequence Type");
5043 dissector_add_uint("acdr.tls_application_port", 443, http_handle
);
5044 dissector_add_uint("acdr.tls_application", TLS_APP_HTTP
, http_handle
);
5045 dissector_add_uint("acdr.tls_application", TLS_APP_TR069
, http_handle
);
5046 dissector_add_uint("ippusb", 0, http_tcp_handle
);
5050 * Content-Type: message/http
5053 static int proto_message_http
;
5054 static int ett_message_http
;
5057 dissect_message_http(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
5059 proto_tree
*subtree
;
5061 int offset
= 0, next_offset
;
5064 col_append_str(pinfo
->cinfo
, COL_INFO
, " (message/http)");
5066 ti
= proto_tree_add_item(tree
, proto_message_http
,
5067 tvb
, 0, -1, ENC_NA
);
5068 subtree
= proto_item_add_subtree(ti
, ett_message_http
);
5069 while (tvb_offset_exists(tvb
, offset
)) {
5070 len
= tvb_find_line_end(tvb
, offset
,
5071 tvb_ensure_captured_length_remaining(tvb
, offset
),
5072 &next_offset
, false);
5075 proto_tree_add_format_text(subtree
, tvb
, offset
, len
);
5076 offset
= next_offset
;
5079 return tvb_captured_length(tvb
);
5083 proto_register_message_http(void)
5085 static int *ett
[] = {
5089 proto_message_http
= proto_register_protocol("Media Type: message/http", "message/http", "message-http");
5090 proto_register_subtree_array(ett
, array_length(ett
));
5094 proto_reg_handoff_message_http(void)
5096 dissector_handle_t message_http_handle
;
5098 message_http_handle
= create_dissector_handle(dissect_message_http
,
5099 proto_message_http
);
5101 dissector_add_string("media_type", "message/http", message_http_handle
);
5103 heur_dissector_add("tcp", dissect_http_heur_tcp
, "HTTP over TCP", "http_tcp", proto_http
, HEURISTIC_ENABLE
);
5104 heur_dissector_add("tls", dissect_http_heur_tls
, "HTTP over TLS", "http_tls", proto_http
, HEURISTIC_ENABLE
);
5106 proto_http2
= proto_get_id_by_filter_name("http2");
5108 dissector_add_uint_range_with_preference("tcp.port", TCP_DEFAULT_RANGE
, http_tcp_handle
);
5109 dissector_add_uint_range_with_preference("sctp.port", SCTP_DEFAULT_RANGE
, http_sctp_handle
);
5112 * Get the content type and Internet media type table
5114 media_type_subdissector_table
= find_dissector_table("media_type");
5116 streaming_content_type_dissector_table
= find_dissector_table("streaming_content_type");
5122 * Editor modelines - https://www.wireshark.org/tools/modelines.html
5127 * indent-tabs-mode: t
5130 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
5131 * :indentSize=8:tabSize=8:noTabs=false: