clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name packet-http.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-18/lib/clang/18 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /usr/include/mit-krb5 -isystem /usr/include/libxml2 -isystem /builds/wireshark/wireshark/build/epan/dissectors -isystem /builds/wireshark/wireshark/epan/dissectors -isystem /builds/wireshark/wireshark/epan -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-error=stringop-overflow= -Wno-error=deprecated-declarations -Wno-format-truncation -Wno-pointer-sign -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -analyzer-output=html -dwarf-debug-flags /usr/lib/llvm-18/bin/clang -### --analyze -x c -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /usr/include/mit-krb5 -isystem /usr/include/libxml2 -isystem /builds/wireshark/wireshark/build/epan/dissectors -isystem /builds/wireshark/wireshark/epan/dissectors -isystem /builds/wireshark/wireshark/epan -fvisibility=hidden -fno-strict-overflow -fexcess-precision=fast -Wno-error=stringop-overflow= -Wno-error=deprecated-declarations -Wno-format-truncation -fdiagnostics-color=always -Wno-pointer-sign -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -std=gnu11 -fPIC /builds/wireshark/wireshark/epan/dissectors/packet-http.c -o /builds/wireshark/wireshark/sbout/2024-07-31-100245-3330-1 -Xclang -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2024-07-31-100245-3330-1 -x c /builds/wireshark/wireshark/epan/dissectors/packet-http.c
1 | /* packet-http.c |
2 | * Routines for HTTP packet disassembly |
3 | * RFC 1945 (HTTP/1.0) |
4 | * RFC 2616 (HTTP/1.1) |
5 | * |
6 | * Guy Harris <[emailprotected]> |
7 | * |
8 | * Copyright 2017, Eugene Adell <[emailprotected]> |
9 | * Copyright 2004, Jerry Talkington <[emailprotected]> |
10 | * Copyright 2002, Tim Potter <[emailprotected]> |
11 | * Copyright 1999, Andrew Tridgell <[emailprotected]> |
12 | * |
13 | * Wireshark - Network traffic analyzer |
14 | * By Gerald Combs <[emailprotected]> |
15 | * Copyright 1998 Gerald Combs |
16 | * |
17 | * SPDX-License-Identifier: GPL-2.0-or-later |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include <errno(*__errno_location ()).h> |
23 | |
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> |
29 | #include <epan/uat.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 <glib.h> |
40 | #include "packet-http.h" |
41 | #include "packet-http2.h" |
42 | #include "packet-tcp.h" |
43 | #include "packet-tls.h" |
44 | #include "packet-acdr.h" |
45 | #include "packet-media-type.h" |
46 | |
47 | #include <ui/tap-credentials.h> |
48 | |
49 | void proto_register_http(void); |
50 | void proto_reg_handoff_http(void); |
51 | void proto_register_message_http(void); |
52 | void proto_reg_handoff_message_http(void); |
53 | |
54 | static int http_tap; |
55 | static int http_eo_tap; |
56 | static int http_follow_tap; |
57 | static int credentials_tap; |
58 | |
59 | static int proto_http; |
60 | static int proto_http2; |
61 | static int proto_ssdp; |
62 | static int hf_http_notification; |
63 | static int hf_http_response; |
64 | static int hf_http_request; |
65 | static int hf_http_response_line; |
66 | static int hf_http_request_line; |
67 | static int hf_http_basic; |
68 | static int hf_http_citrix; |
69 | static int hf_http_citrix_user; |
70 | static int hf_http_citrix_domain; |
71 | static int hf_http_citrix_passwd; |
72 | static int hf_http_citrix_session; |
73 | static int hf_http_request_method; |
74 | static int hf_http_request_uri; |
75 | static int hf_http_request_full_uri; |
76 | static int hf_http_request_path; |
77 | static int hf_http_request_path_segment; |
78 | static int hf_http_request_query; |
79 | static int hf_http_request_query_parameter; |
80 | static int hf_http_request_version; |
81 | static int hf_http_response_version; |
82 | static int hf_http_response_code; |
83 | static int hf_http_response_code_desc; |
84 | static int hf_http_response_phrase; |
85 | static int hf_http_authorization; |
86 | static int hf_http_proxy_authenticate; |
87 | static int hf_http_proxy_authorization; |
88 | static int hf_http_proxy_connect_host; |
89 | static int hf_http_proxy_connect_port; |
90 | static int hf_http_www_authenticate; |
91 | static int hf_http_content_type; |
92 | static int hf_http_content_length_header; |
93 | static int hf_http_content_length; |
94 | static int hf_http_content_encoding; |
95 | static int hf_http_transfer_encoding; |
96 | static int hf_http_upgrade; |
97 | static int hf_http_user_agent; |
98 | static int hf_http_host; |
99 | static int hf_http_range; |
100 | static int hf_http_content_range; |
101 | static int hf_http_connection; |
102 | static int hf_http_cookie; |
103 | static int hf_http_cookie_pair; |
104 | static int hf_http_accept; |
105 | static int hf_http_referer; |
106 | static int hf_http_accept_language; |
107 | static int hf_http_accept_encoding; |
108 | static int hf_http_date; |
109 | static int hf_http_cache_control; |
110 | static int hf_http_server; |
111 | static int hf_http_location; |
112 | static int hf_http_sec_websocket_accept; |
113 | static int hf_http_sec_websocket_extensions; |
114 | static int hf_http_sec_websocket_key; |
115 | static int hf_http_sec_websocket_protocol; |
116 | static int hf_http_sec_websocket_version; |
117 | static int hf_http_set_cookie; |
118 | static int hf_http_last_modified; |
119 | static int hf_http_x_forwarded_for; |
120 | static int hf_http_http2_settings; |
121 | static int hf_http_request_in; |
122 | static int hf_http_response_in; |
123 | /*static int hf_http_next_request_in; |
124 | static int hf_http_next_response_in; |
125 | static int hf_http_prev_request_in; |
126 | static int hf_http_prev_response_in; */ |
127 | static int hf_http_time; |
128 | static int hf_http_chunk_size; |
129 | static int hf_http_chunk_data; |
130 | static int hf_http_chunk_boundary; |
131 | static int hf_http_chunked_trailer_part; |
132 | static int hf_http_file_data; |
133 | static int hf_http_unknown_header; |
134 | static int hf_http_http2_settings_uri; |
135 | |
136 | static int ett_http; |
137 | static int ett_http_ntlmssp; |
138 | static int ett_http_kerberos; |
139 | static int ett_http_request; |
140 | static int ett_http_request_uri; |
141 | static int ett_http_request_path; |
142 | static int ett_http_request_query; |
143 | static int ett_http_chunked_response; |
144 | static int ett_http_chunk_data; |
145 | static int ett_http_encoded_entity; |
146 | static int ett_http_header_item; |
147 | static int ett_http_http2_settings_item; |
148 | |
149 | static expert_field ei_http_te_and_length; |
150 | static expert_field ei_http_te_unknown; |
151 | static expert_field ei_http_subdissector_failed; |
152 | static expert_field ei_http_tls_port; |
153 | static expert_field ei_http_leading_crlf; |
154 | static expert_field ei_http_excess_data; |
155 | static expert_field ei_http_bad_header_name; |
156 | static expert_field ei_http_decompression_failed; |
157 | static expert_field ei_http_decompression_disabled; |
158 | |
159 | static dissector_handle_t http_handle; |
160 | static dissector_handle_t http_tcp_handle; |
161 | static dissector_handle_t http_tls_handle; |
162 | static dissector_handle_t http_sctp_handle; |
163 | |
164 | static dissector_handle_t media_handle; |
165 | static dissector_handle_t http2_handle; |
166 | static dissector_handle_t sstp_handle; |
167 | static dissector_handle_t ntlmssp_handle; |
168 | static dissector_handle_t gssapi_handle; |
169 | |
170 | /* RFC 3986 Ch 2.2 Reserved characters*/ |
171 | /* patterns used for tvb_ws_mempbrk_pattern_uint8 */ |
172 | static ws_mempbrk_pattern pbrk_gen_delims; |
173 | static ws_mempbrk_pattern pbrk_sub_delims; |
174 | |
175 | /* reassembly table for streaming chunk mode */ |
176 | static reassembly_table http_streaming_reassembly_table; |
177 | |
178 | REASSEMBLE_ITEMS_DEFINE(http_body, "HTTP Chunked Body")static int ett_http_body_fragment; static int ett_http_body_fragments ; static int hf_http_body_fragments; static int hf_http_body_fragment ; static int hf_http_body_fragment_overlap; static int hf_http_body_fragment_overlap_conflicts ; static int hf_http_body_fragment_multiple_tails; static int hf_http_body_fragment_too_long_fragment; static int hf_http_body_fragment_error ; static int hf_http_body_fragment_count; static int hf_http_body_reassembled_in ; static int hf_http_body_reassembled_length; static int hf_http_body_reassembled_data ; static int hf_http_body_segment; static const fragment_items http_body_fragment_items = { &ett_http_body_fragment, & ett_http_body_fragments, &hf_http_body_fragments, &hf_http_body_fragment , &hf_http_body_fragment_overlap, &hf_http_body_fragment_overlap_conflicts , &hf_http_body_fragment_multiple_tails, &hf_http_body_fragment_too_long_fragment , &hf_http_body_fragment_error, &hf_http_body_fragment_count , &hf_http_body_reassembled_in, &hf_http_body_reassembled_length , &hf_http_body_reassembled_data, "HTTP Chunked Body" " fragments" }; |
179 | |
180 | /* HTTP chunk virtual frame number (similar to HTTP2 frame num) */ |
181 | #define get_http_chunk_frame_numget_virtual_frame_num64 get_virtual_frame_num64 |
182 | |
183 | /* Stuff for generation/handling of fields for custom HTTP headers */ |
184 | typedef struct _header_field_t { |
185 | char* header_name; |
186 | char* header_desc; |
187 | } header_field_t; |
188 | |
189 | static header_field_t* header_fields; |
190 | static unsigned num_header_fields; |
191 | |
192 | static GHashTable* header_fields_hash; |
193 | static hf_register_info* dynamic_hf; |
194 | static unsigned dynamic_hf_size; |
195 | |
196 | static bool_Bool |
197 | header_fields_update_cb(void *r, char **err) |
198 | { |
199 | header_field_t *rec = (header_field_t *)r; |
200 | char c; |
201 | |
202 | if (rec->header_name == NULL((void*)0)) { |
203 | *err = g_strdup("Header name can't be empty")g_strdup_inline ("Header name can't be empty"); |
204 | return false0; |
205 | } |
206 | |
207 | g_strstrip(rec->header_name)g_strchomp (g_strchug (rec->header_name)); |
208 | if (rec->header_name[0] == 0) { |
209 | *err = g_strdup("Header name can't be empty")g_strdup_inline ("Header name can't be empty"); |
210 | return false0; |
211 | } |
212 | |
213 | /* Check for invalid characters (to avoid asserting out when |
214 | * registering the field). |
215 | */ |
216 | c = proto_check_field_name(rec->header_name); |
217 | if (c) { |
218 | *err = ws_strdup_printf("Header name can't contain '%c'", c)wmem_strdup_printf(((void*)0), "Header name can't contain '%c'" , c); |
219 | return false0; |
220 | } |
221 | |
222 | *err = NULL((void*)0); |
223 | return true1; |
224 | } |
225 | |
226 | static void * |
227 | header_fields_copy_cb(void* n, const void* o, size_t siz _U___attribute__((unused))) |
228 | { |
229 | header_field_t* new_rec = (header_field_t*)n; |
230 | const header_field_t* old_rec = (const header_field_t*)o; |
231 | |
232 | new_rec->header_name = g_strdup(old_rec->header_name)g_strdup_inline (old_rec->header_name); |
233 | new_rec->header_desc = g_strdup(old_rec->header_desc)g_strdup_inline (old_rec->header_desc); |
234 | |
235 | return new_rec; |
236 | } |
237 | |
238 | static void |
239 | header_fields_free_cb(void*r) |
240 | { |
241 | header_field_t* rec = (header_field_t*)r; |
242 | |
243 | g_free(rec->header_name); |
244 | g_free(rec->header_desc); |
245 | } |
246 | |
247 | UAT_CSTRING_CB_DEF(header_fields, header_name, header_field_t)static void header_fields_header_name_set_cb(void* rec, const char* buf, unsigned len, const void* u1 __attribute__((unused )), const void* u2 __attribute__((unused))) { char* new_buf = g_strndup(buf,len); g_free((((header_field_t*)rec)->header_name )); (((header_field_t*)rec)->header_name) = new_buf; } static void header_fields_header_name_tostr_cb(void* rec, char** out_ptr , unsigned* out_len, const void* u1 __attribute__((unused)), const void* u2 __attribute__((unused))) { if (((header_field_t*)rec )->header_name ) { *out_ptr = g_strdup_inline ((((header_field_t *)rec)->header_name)); *out_len = (unsigned)strlen((((header_field_t *)rec)->header_name)); } else { *out_ptr = g_strdup_inline (""); *out_len = 0; } } |
248 | UAT_CSTRING_CB_DEF(header_fields, header_desc, header_field_t)static void header_fields_header_desc_set_cb(void* rec, const char* buf, unsigned len, const void* u1 __attribute__((unused )), const void* u2 __attribute__((unused))) { char* new_buf = g_strndup(buf,len); g_free((((header_field_t*)rec)->header_desc )); (((header_field_t*)rec)->header_desc) = new_buf; } static void header_fields_header_desc_tostr_cb(void* rec, char** out_ptr , unsigned* out_len, const void* u1 __attribute__((unused)), const void* u2 __attribute__((unused))) { if (((header_field_t*)rec )->header_desc ) { *out_ptr = g_strdup_inline ((((header_field_t *)rec)->header_desc)); *out_len = (unsigned)strlen((((header_field_t *)rec)->header_desc)); } else { *out_ptr = g_strdup_inline (""); *out_len = 0; } } |
249 | |
250 | /* |
251 | * desegmentation of HTTP headers |
252 | * (when we are over TCP or another protocol providing the desegmentation API) |
253 | */ |
254 | static bool_Bool http_desegment_headers = true1; |
255 | |
256 | /* |
257 | * desegmentation of HTTP bodies |
258 | * (when we are over TCP or another protocol providing the desegmentation API) |
259 | * TODO let the user filter on content-type the bodies he wants desegmented |
260 | */ |
261 | static bool_Bool http_desegment_body = true1; |
262 | |
263 | /* |
264 | * De-chunking of content-encoding: chunk entity bodies. |
265 | */ |
266 | static bool_Bool http_dechunk_body = true1; |
267 | |
268 | /* |
269 | * Decompression of zlib or brotli encoded entities. |
270 | */ |
271 | #if defined(HAVE_ZLIB1)|| defined(HAVE_ZLIBNG) || defined(HAVE_BROTLI1) |
272 | static bool_Bool http_decompress_body = true1; |
273 | #endif |
274 | |
275 | /* |
276 | * Extra checks for valid ASCII data in HTTP headers. |
277 | */ |
278 | static bool_Bool http_check_ascii_headers = false0; |
279 | |
280 | /* Simple Service Discovery Protocol |
281 | * SSDP is implemented atop HTTP (yes, it really *does* run over UDP). |
282 | * SSDP is the discovery protocol of Universal Plug and Play |
283 | * UPnP http://www.upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf |
284 | */ |
285 | #define TCP_PORT_SSDP19001900 |
286 | #define UDP_PORT_SSDP19001900 |
287 | |
288 | /* |
289 | * TCP and TLS ports |
290 | * |
291 | * 2710 is the XBT BitTorrent tracker |
292 | */ |
293 | |
294 | #define TCP_DEFAULT_RANGE"80,3128,3132,5985,8080,8088,11371,1900,2869,2710" "80,3128,3132,5985,8080,8088,11371,1900,2869,2710" |
295 | #define SCTP_DEFAULT_RANGE"80" "80" |
296 | #define TLS_DEFAULT_RANGE"443" "443" |
297 | |
298 | static range_t *global_http_tls_range; |
299 | |
300 | static range_t *http_tcp_range; |
301 | static range_t *http_sctp_range; |
302 | static range_t *http_tls_range; |
303 | |
304 | typedef void (*ReqRespDissector)(packet_info*, tvbuff_t*, proto_tree*, int, const unsigned char*, |
305 | const unsigned char*, http_conv_t *, http_req_res_t *); |
306 | |
307 | /** |
308 | * Transfer codings from |
309 | * https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#transfer-coding |
310 | * Note: chunked encoding is handled separately. |
311 | */ |
312 | typedef enum _http_transfer_coding { |
313 | HTTP_TE_NONE, /* Dummy value for header which is not set */ |
314 | /* HTTP_TE_CHUNKED, */ |
315 | HTTP_TE_COMPRESS, |
316 | HTTP_TE_DEFLATE, |
317 | HTTP_TE_GZIP, |
318 | HTTP_TE_IDENTITY, |
319 | HTTP_TE_UNKNOWN, /* Header was set, but no valid name was found */ |
320 | } http_transfer_coding; |
321 | |
322 | /* |
323 | * Structure holding information from headers needed by main |
324 | * HTTP dissector code. |
325 | */ |
326 | typedef struct { |
327 | char*content_type; |
328 | char*content_type_parameters; |
329 | bool_Bool have_content_length; |
330 | int64_t content_length; |
331 | char *content_encoding; |
332 | bool_Bool transfer_encoding_chunked; |
333 | http_transfer_coding transfer_encoding; |
334 | char *upgrade; |
335 | } headers_t; |
336 | |
337 | /* request or response streaming reassembly data */ |
338 | typedef struct { |
339 | /* reassembly information only for request or response with chunked and streaming data */ |
340 | streaming_reassembly_info_t* streaming_reassembly_info; |
341 | /* subdissector handler for request or response with chunked and streaming data */ |
342 | dissector_handle_t streaming_handle; |
343 | /* message being passed to subdissector if the request or response has chunked and streaming data */ |
344 | media_content_info_t* content_info; |
345 | headers_t* main_headers; |
346 | } http_streaming_reassembly_data_t; |
347 | |
348 | /* http request or response private data */ |
349 | typedef struct { |
350 | /* direction of request message */ |
351 | int req_fwd_flow; |
352 | /* request or response streaming reassembly data */ |
353 | http_streaming_reassembly_data_t* req_streaming_reassembly_data; |
354 | http_streaming_reassembly_data_t* res_streaming_reassembly_data; |
355 | } http_req_res_private_data_t; |
356 | |
357 | typedef struct _request_trans_t { |
358 | uint64_t first_range_num; |
359 | uint32_t req_frame; |
360 | nstime_t abs_time; |
361 | char *request_uri; |
362 | } request_trans_t; |
363 | |
364 | typedef struct _match_trans_t { |
365 | uint32_t req_frame; |
366 | uint32_t resp_frame; |
367 | nstime_t delta_time; |
368 | char *request_uri; |
369 | char *http_host; |
370 | } match_trans_t; |
371 | |
372 | static int parse_http_status_code(const unsigned char *line, const unsigned char *lineend); |
373 | static int is_http_request_or_reply(packet_info *pinfo, const char *data, int linelen, |
374 | media_container_type_t *type, ReqRespDissector |
375 | *reqresp_dissector, http_conv_t *conv_data); |
376 | static unsigned chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo, |
377 | proto_tree *tree, int offset); |
378 | static bool_Bool valid_header_name(const unsigned char *line, int header_len); |
379 | static bool_Bool process_header(tvbuff_t *tvb, int offset, int next_offset, |
380 | const unsigned char *line, int linelen, int colon_offset, |
381 | packet_info *pinfo, proto_tree *tree, |
382 | headers_t *eh_ptr, http_conv_t *conv_data, |
383 | media_container_type_t http_type, wmem_map_t *header_value_map, bool_Bool streaming_chunk_mode); |
384 | static int find_header_hf_value(tvbuff_t *tvb, int offset, unsigned header_len); |
385 | static bool_Bool check_auth_ntlmssp(proto_item *hdr_item, tvbuff_t *tvb, |
386 | packet_info *pinfo, char *value); |
387 | static bool_Bool check_auth_basic(proto_item *hdr_item, tvbuff_t *tvb, |
388 | packet_info *pinfo, char *value); |
389 | static bool_Bool check_auth_digest(proto_item* hdr_item, tvbuff_t* tvb, packet_info* pinfo _U___attribute__((unused)), char* value, int offset, int len); |
390 | static bool_Bool check_auth_citrixbasic(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo, |
391 | char *value, int offset); |
392 | static bool_Bool check_auth_kerberos(proto_item *hdr_item, tvbuff_t *tvb, |
393 | packet_info *pinfo, const char *value); |
394 | |
395 | static dissector_table_t port_subdissector_table; |
396 | static dissector_table_t media_type_subdissector_table; |
397 | static dissector_table_t streaming_content_type_dissector_table; |
398 | static dissector_table_t upgrade_subdissector_table; |
399 | static heur_dissector_list_t heur_subdissector_list; |
400 | |
401 | static tap_packet_status |
402 | http_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U___attribute__((unused)), const void *data, tap_flags_t flags _U___attribute__((unused))) |
403 | { |
404 | export_object_list_t *object_list = (export_object_list_t *)tapdata; |
405 | const http_eo_t *eo_info = (const http_eo_t *)data; |
406 | export_object_entry_t *entry; |
407 | |
408 | if(eo_info) { /* We have data waiting for us */ |
409 | /* These values will be freed when the Export Object window |
410 | * is closed. */ |
411 | entry = g_new(export_object_entry_t, 1)((export_object_entry_t *) g_malloc_n ((1), sizeof (export_object_entry_t ))); |
412 | |
413 | entry->pkt_num = pinfo->num; |
414 | /* XXX: Should this remove the port, if any? It's only |
415 | * for display, so probably not. */ |
416 | entry->hostname = g_strdup(eo_info->hostname)g_strdup_inline (eo_info->hostname); |
417 | entry->content_type = g_strdup(eo_info->content_type)g_strdup_inline (eo_info->content_type); |
418 | /* XXX: Should this remove the query portion, if any, from |
419 | * the path? (Or should that be done in the dissector?) */ |
420 | entry->filename = eo_info->filename ? g_path_get_basename(eo_info->filename) : NULL((void*)0); |
421 | entry->payload_len = tvb_captured_length(eo_info->payload); |
422 | entry->payload_data = (uint8_t *)tvb_memdup(NULL((void*)0), eo_info->payload, 0, entry->payload_len); |
423 | |
424 | object_list->add_entry(object_list->gui_data, entry); |
425 | |
426 | return TAP_PACKET_REDRAW; /* State changed - window should be redrawn */ |
427 | } else { |
428 | return TAP_PACKET_DONT_REDRAW; /* State unchanged - no window updates needed */ |
429 | } |
430 | } |
431 | |
432 | /* --- HTTP Status Codes */ |
433 | /* Note: The reference for uncommented entries is RFC 2616 */ |
434 | const value_string vals_http_status_code[] = { |
435 | { 100, "Continue" }, |
436 | { 101, "Switching Protocols" }, |
437 | { 102, "Processing" }, /* RFC 2518 */ |
438 | { 103, "Early Hints" }, /* RFC-ietf-httpbis-early-hints-05 */ |
439 | { 199, "Informational - Others" }, |
440 | |
441 | { 200, "OK"}, |
442 | { 201, "Created"}, |
443 | { 202, "Accepted"}, |
444 | { 203, "Non-authoritative Information"}, |
445 | { 204, "No Content"}, |
446 | { 205, "Reset Content"}, |
447 | { 206, "Partial Content"}, |
448 | { 207, "Multi-Status"}, /* RFC 4918 */ |
449 | { 208, "Already Reported"}, /* RFC 5842 */ |
450 | { 226, "IM Used"}, /* RFC 3229 */ |
451 | { 299, "Success - Others"}, |
452 | |
453 | { 300, "Multiple Choices"}, |
454 | { 301, "Moved Permanently"}, |
455 | { 302, "Found"}, |
456 | { 303, "See Other"}, |
457 | { 304, "Not Modified"}, |
458 | { 305, "Use Proxy"}, |
459 | { 307, "Temporary Redirect"}, |
460 | { 308, "Permanent Redirect"}, /* RFC 7538 */ |
461 | { 399, "Redirection - Others"}, |
462 | |
463 | { 400, "Bad Request"}, |
464 | { 401, "Unauthorized"}, |
465 | { 402, "Payment Required"}, |
466 | { 403, "Forbidden"}, |
467 | { 404, "Not Found"}, |
468 | { 405, "Method Not Allowed"}, |
469 | { 406, "Not Acceptable"}, |
470 | { 407, "Proxy Authentication Required"}, |
471 | { 408, "Request Time-out"}, |
472 | { 409, "Conflict"}, |
473 | { 410, "Gone"}, |
474 | { 411, "Length Required"}, |
475 | { 412, "Precondition Failed"}, |
476 | { 413, "Request Entity Too Large"}, |
477 | { 414, "Request-URI Too Long"}, |
478 | { 415, "Unsupported Media Type"}, |
479 | { 416, "Requested Range Not Satisfiable"}, |
480 | { 417, "Expectation Failed"}, |
481 | { 418, "I'm a teapot"}, /* RFC 2324 */ |
482 | { 421, "Misdirected Request"}, /* RFC 7540 */ |
483 | { 422, "Unprocessable Entity"}, /* RFC 4918 */ |
484 | { 423, "Locked"}, /* RFC 4918 */ |
485 | { 424, "Failed Dependency"}, /* RFC 4918 */ |
486 | { 425, "Too Early"}, /* RFC 8470 */ |
487 | { 426, "Upgrade Required"}, /* RFC 2817 */ |
488 | { 428, "Precondition Required"}, /* RFC 6585 */ |
489 | { 429, "Too Many Requests"}, /* RFC 6585 */ |
490 | { 431, "Request Header Fields Too Large"}, /* RFC 6585 */ |
491 | { 451, "Unavailable For Legal Reasons"}, /* RFC 7725 */ |
492 | { 499, "Client Error - Others"}, |
493 | |
494 | { 500, "Internal Server Error"}, |
495 | { 501, "Not Implemented"}, |
496 | { 502, "Bad Gateway"}, |
497 | { 503, "Service Unavailable"}, |
498 | { 504, "Gateway Time-out"}, |
499 | { 505, "HTTP Version not supported"}, |
500 | { 506, "Variant Also Negotiates"}, /* RFC 2295 */ |
501 | { 507, "Insufficient Storage"}, /* RFC 4918 */ |
502 | { 508, "Loop Detected"}, /* RFC 5842 */ |
503 | { 510, "Not Extended"}, /* RFC 2774 */ |
504 | { 511, "Network Authentication Required"}, /* RFC 6585 */ |
505 | { 599, "Server Error - Others"}, |
506 | |
507 | { 0, NULL((void*)0)} |
508 | }; |
509 | |
510 | static const char* st_str_reqs = "HTTP Requests by Server"; |
511 | static const char* st_str_reqs_by_srv_addr = "HTTP Requests by Server Address"; |
512 | static const char* st_str_reqs_by_http_host = "HTTP Requests by HTTP Host"; |
513 | static const char* st_str_resps_by_srv_addr = "HTTP Responses by Server Address"; |
514 | |
515 | static int st_node_reqs = -1; |
516 | static int st_node_reqs_by_srv_addr = -1; |
517 | static int st_node_reqs_by_http_host = -1; |
518 | static int st_node_resps_by_srv_addr = -1; |
519 | |
520 | /* Parse HTTP path sub components RFC3986 Ch 3.3, 3.4 */ |
521 | void |
522 | http_add_path_components_to_tree(tvbuff_t* tvb, packet_info* pinfo _U___attribute__((unused)), proto_item* item, int offset, int length) |
523 | { |
524 | proto_item* ti; |
525 | proto_tree* uri_tree; |
526 | int end_offset, end_path_offset, query_offset, path_len, query_len, parameter_offset; |
527 | end_offset = offset + length; |
528 | /* The Content-Location (and Referer) headers in HTTP 1.1, and the |
529 | * :path header in HTTP/2 can be an absolute-URI or a partial-URI; |
530 | * i.e. that they can include a path and a query, but not a fragment. |
531 | * RFC 7230 2.7 Uniform Request Identifiers, RFC 7231 Appendices C and D, |
532 | * RFC 7540 8.1.2.3. Request Pseudo-Header Fields |
533 | * Look for a ? to mark a query. |
534 | */ |
535 | query_offset = tvb_find_uint8(tvb, offset, length, '?'); |
536 | end_path_offset = (query_offset == -1) ? end_offset : query_offset; |
537 | parameter_offset = tvb_ws_mempbrk_pattern_uint8(tvb, offset + 1, end_path_offset - offset - 1, &pbrk_sub_delims, NULL((void*)0)); |
538 | if (query_offset == -1 && parameter_offset == -1) { |
539 | /* Nothing interesting, no need to split. */ |
540 | return; |
541 | } |
542 | uri_tree = proto_item_add_subtree(item, ett_http_request_uri); |
543 | path_len = end_path_offset - offset; |
544 | proto_tree_add_item(uri_tree, hf_http_request_path, tvb, offset, path_len, ENC_ASCII0x00000000); |
545 | parameter_offset = tvb_ws_mempbrk_pattern_uint8(tvb, offset + 1, end_path_offset - offset - 1, &pbrk_sub_delims, NULL((void*)0)); |
546 | if (parameter_offset != -1) { |
547 | proto_tree* path_tree = proto_item_add_subtree(item, ett_http_request_path); |
548 | while (offset < end_path_offset) { |
549 | parameter_offset = tvb_ws_mempbrk_pattern_uint8(tvb, offset + 1, end_path_offset - offset - 1, &pbrk_sub_delims, NULL((void*)0)); |
550 | if (parameter_offset == -1) { |
551 | parameter_offset = end_path_offset; |
552 | } |
553 | proto_tree_add_item(path_tree, hf_http_request_path_segment, tvb, offset, parameter_offset - offset, ENC_ASCII0x00000000); |
554 | offset = parameter_offset + 1; |
555 | } |
556 | } |
557 | if (query_offset == -1) { |
558 | return; |
559 | } |
560 | /* Skip past the delimiter. */ |
561 | query_offset++; |
562 | query_len = end_offset - query_offset; |
563 | offset = query_offset; |
564 | ti = proto_tree_add_item(uri_tree, hf_http_request_query, tvb, query_offset, query_len, ENC_ASCII0x00000000); |
565 | proto_tree *query_tree = proto_item_add_subtree(ti, ett_http_request_query); |
566 | while (offset < end_offset) { |
567 | parameter_offset = tvb_ws_mempbrk_pattern_uint8(tvb, offset + 1, end_offset - offset - 1, &pbrk_sub_delims, NULL((void*)0)); |
568 | if (parameter_offset == -1) { |
569 | parameter_offset = end_offset; |
570 | } |
571 | proto_tree_add_item(query_tree, hf_http_request_query_parameter, tvb, offset, parameter_offset - offset, ENC_ASCII0x00000000); |
572 | offset = parameter_offset + 1; |
573 | } |
574 | } |
575 | |
576 | /* HTTP/Load Distribution stats init function */ |
577 | static void |
578 | http_reqs_stats_tree_init(stats_tree* st) |
579 | { |
580 | st_node_reqs = stats_tree_create_node(st, st_str_reqs, 0, STAT_DT_INT, true1); |
581 | st_node_reqs_by_srv_addr = stats_tree_create_node(st, st_str_reqs_by_srv_addr, st_node_reqs, STAT_DT_INT, true1); |
582 | st_node_reqs_by_http_host = stats_tree_create_node(st, st_str_reqs_by_http_host, st_node_reqs, STAT_DT_INT, true1); |
583 | st_node_resps_by_srv_addr = stats_tree_create_node(st, st_str_resps_by_srv_addr, 0, STAT_DT_INT, true1); |
584 | } |
585 | |
586 | /* HTTP/Load Distribution stats packet function */ |
587 | static tap_packet_status |
588 | http_reqs_stats_tree_packet(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt _U___attribute__((unused)), const void* p, tap_flags_t flags _U___attribute__((unused))) |
589 | { |
590 | const http_info_value_t* v = (const http_info_value_t*)p; |
591 | int reqs_by_this_host; |
592 | int reqs_by_this_addr; |
593 | int resps_by_this_addr; |
594 | int i = v->response_code; |
595 | char *ip_str; |
596 | |
597 | |
598 | if (v->request_method) { |
599 | ip_str = address_to_str(NULL((void*)0), &pinfo->dst); |
600 | |
601 | tick_stat_node(st, st_str_reqs, 0, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_reqs),(0) ,(0),1)); |
602 | tick_stat_node(st, st_str_reqs_by_srv_addr, st_node_reqs, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_reqs_by_srv_addr ),(st_node_reqs),(1),1)); |
603 | tick_stat_node(st, st_str_reqs_by_http_host, st_node_reqs, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_reqs_by_http_host ),(st_node_reqs),(1),1)); |
604 | reqs_by_this_addr = tick_stat_node(st, ip_str, st_node_reqs_by_srv_addr, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(ip_str),(st_node_reqs_by_srv_addr ),(1),1)); |
605 | |
606 | if (v->http_host) { |
607 | reqs_by_this_host = tick_stat_node(st, v->http_host, st_node_reqs_by_http_host, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(v->http_host) ,(st_node_reqs_by_http_host),(1),1)); |
608 | tick_stat_node(st, ip_str, reqs_by_this_host, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(ip_str),(reqs_by_this_host ),(0),1)); |
609 | |
610 | tick_stat_node(st, v->http_host, reqs_by_this_addr, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(v->http_host) ,(reqs_by_this_addr),(0),1)); |
611 | } |
612 | |
613 | wmem_free(NULL((void*)0), ip_str); |
614 | |
615 | return TAP_PACKET_REDRAW; |
616 | |
617 | } else if (i != 0) { |
618 | ip_str = address_to_str(NULL((void*)0), &pinfo->src); |
619 | |
620 | tick_stat_node(st, st_str_resps_by_srv_addr, 0, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_resps_by_srv_addr ),(0),(0),1)); |
621 | resps_by_this_addr = tick_stat_node(st, ip_str, st_node_resps_by_srv_addr, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(ip_str),(st_node_resps_by_srv_addr ),(1),1)); |
622 | |
623 | if ( (i>=100)&&(i<400) ) { |
624 | tick_stat_node(st, "OK", resps_by_this_addr, false)(stats_tree_manip_node_int(MN_INCREASE,(st),("OK"),(resps_by_this_addr ),(0),1)); |
625 | } else { |
626 | tick_stat_node(st, "Error", resps_by_this_addr, false)(stats_tree_manip_node_int(MN_INCREASE,(st),("Error"),(resps_by_this_addr ),(0),1)); |
627 | } |
628 | |
629 | wmem_free(NULL((void*)0), ip_str); |
630 | |
631 | return TAP_PACKET_REDRAW; |
632 | } |
633 | |
634 | return TAP_PACKET_DONT_REDRAW; |
635 | } |
636 | |
637 | |
638 | static int st_node_requests_by_host = -1; |
639 | static const char *st_str_requests_by_host = "HTTP Requests by HTTP Host"; |
640 | |
641 | /* HTTP/Requests stats init function */ |
642 | static void |
643 | http_req_stats_tree_init(stats_tree* st) |
644 | { |
645 | st_node_requests_by_host = stats_tree_create_node(st, st_str_requests_by_host, 0, STAT_DT_INT, true1); |
646 | } |
647 | |
648 | /* HTTP/Requests stats packet function */ |
649 | static tap_packet_status |
650 | http_req_stats_tree_packet(stats_tree* st, packet_info* pinfo _U___attribute__((unused)), epan_dissect_t* edt _U___attribute__((unused)), const void* p, tap_flags_t flags _U___attribute__((unused))) |
651 | { |
652 | const http_info_value_t* v = (const http_info_value_t*)p; |
653 | int reqs_by_this_host; |
654 | |
655 | if (v->request_method) { |
656 | tick_stat_node(st, st_str_requests_by_host, 0, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_requests_by_host ),(0),(0),1)); |
657 | |
658 | if (v->http_host) { |
659 | reqs_by_this_host = tick_stat_node(st, v->http_host, st_node_requests_by_host, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(v->http_host) ,(st_node_requests_by_host),(1),1)); |
660 | |
661 | if (v->request_uri) { |
662 | tick_stat_node(st, v->request_uri, reqs_by_this_host, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(v->request_uri ),(reqs_by_this_host),(1),1)); |
663 | } |
664 | } |
665 | |
666 | return TAP_PACKET_REDRAW; |
667 | } |
668 | |
669 | return TAP_PACKET_DONT_REDRAW; |
670 | } |
671 | |
672 | static const char *st_str_packets = "Total HTTP Packets"; |
673 | static const char *st_str_requests = "HTTP Request Packets"; |
674 | static const char *st_str_responses = "HTTP Response Packets"; |
675 | static const char *st_str_resp_broken = "???: broken"; |
676 | static const char *st_str_resp_100 = "1xx: Informational"; |
677 | static const char *st_str_resp_200 = "2xx: Success"; |
678 | static const char *st_str_resp_300 = "3xx: Redirection"; |
679 | static const char *st_str_resp_400 = "4xx: Client Error"; |
680 | static const char *st_str_resp_500 = "5xx: Server Error"; |
681 | static const char *st_str_other = "Other HTTP Packets"; |
682 | |
683 | static int st_node_packets = -1; |
684 | static int st_node_requests = -1; |
685 | static int st_node_responses = -1; |
686 | static int st_node_resp_broken = -1; |
687 | static int st_node_resp_100 = -1; |
688 | static int st_node_resp_200 = -1; |
689 | static int st_node_resp_300 = -1; |
690 | static int st_node_resp_400 = -1; |
691 | static int st_node_resp_500 = -1; |
692 | static int st_node_other = -1; |
693 | |
694 | |
695 | /* HTTP/Packet Counter stats init function */ |
696 | static void |
697 | http_stats_tree_init(stats_tree* st) |
698 | { |
699 | st_node_packets = stats_tree_create_node(st, st_str_packets, 0, STAT_DT_INT, true1); |
700 | st_node_requests = stats_tree_create_pivot(st, st_str_requests, st_node_packets); |
701 | st_node_responses = stats_tree_create_node(st, st_str_responses, st_node_packets, STAT_DT_INT, true1); |
702 | st_node_resp_broken = stats_tree_create_node(st, st_str_resp_broken, st_node_responses, STAT_DT_INT, true1); |
703 | st_node_resp_100 = stats_tree_create_node(st, st_str_resp_100, st_node_responses, STAT_DT_INT, true1); |
704 | st_node_resp_200 = stats_tree_create_node(st, st_str_resp_200, st_node_responses, STAT_DT_INT, true1); |
705 | st_node_resp_300 = stats_tree_create_node(st, st_str_resp_300, st_node_responses, STAT_DT_INT, true1); |
706 | st_node_resp_400 = stats_tree_create_node(st, st_str_resp_400, st_node_responses, STAT_DT_INT, true1); |
707 | st_node_resp_500 = stats_tree_create_node(st, st_str_resp_500, st_node_responses, STAT_DT_INT, true1); |
708 | st_node_other = stats_tree_create_node(st, st_str_other, st_node_packets, STAT_DT_INT, false0); |
709 | } |
710 | |
711 | /* HTTP/Packet Counter stats packet function */ |
712 | static tap_packet_status |
713 | http_stats_tree_packet(stats_tree* st, packet_info* pinfo _U___attribute__((unused)), epan_dissect_t* edt _U___attribute__((unused)), const void* p, tap_flags_t flags _U___attribute__((unused))) |
714 | { |
715 | const http_info_value_t* v = (const http_info_value_t*)p; |
716 | unsigned i = v->response_code; |
717 | int resp_grp; |
718 | const char *resp_str; |
719 | char str[64]; |
720 | |
721 | tick_stat_node(st, st_str_packets, 0, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_packets), (0),(0),1)); |
722 | |
723 | if (i) { |
724 | tick_stat_node(st, st_str_responses, st_node_packets, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_responses ),(st_node_packets),(0),1)); |
725 | |
726 | if ( (i<100)||(i>=600) ) { |
727 | resp_grp = st_node_resp_broken; |
728 | resp_str = st_str_resp_broken; |
729 | } else if (i<200) { |
730 | resp_grp = st_node_resp_100; |
731 | resp_str = st_str_resp_100; |
732 | } else if (i<300) { |
733 | resp_grp = st_node_resp_200; |
734 | resp_str = st_str_resp_200; |
735 | } else if (i<400) { |
736 | resp_grp = st_node_resp_300; |
737 | resp_str = st_str_resp_300; |
738 | } else if (i<500) { |
739 | resp_grp = st_node_resp_400; |
740 | resp_str = st_str_resp_400; |
741 | } else { |
742 | resp_grp = st_node_resp_500; |
743 | resp_str = st_str_resp_500; |
744 | } |
745 | |
746 | tick_stat_node(st, resp_str, st_node_responses, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(resp_str),(st_node_responses ),(0),1)); |
747 | |
748 | snprintf(str, sizeof(str), "%u %s", i, |
749 | val_to_str(i, vals_http_status_code, "Unknown (%d)")); |
750 | tick_stat_node(st, str, resp_grp, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(str),(resp_grp), (0),1)); |
751 | } else if (v->request_method) { |
752 | stats_tree_tick_pivot(st,st_node_requests,v->request_method); |
753 | } else { |
754 | tick_stat_node(st, st_str_other, st_node_packets, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_other),(st_node_packets ),(0),1)); |
755 | } |
756 | |
757 | return TAP_PACKET_REDRAW; |
758 | } |
759 | |
760 | /* |
761 | Generates a referer tree - a best-effort representation of which web request led to which. |
762 | |
763 | Some challenges: |
764 | A user can be forwarded to a single sites from multiple sources. For example, |
765 | google.com -> foo.com and bing.com -> foo.com. A URI alone is not unique. |
766 | |
767 | Additionally, if a user has a subsequent request to foo.com -> bar.com, the |
768 | full chain could either be: |
769 | google.com -> foo.com -> bar.com, or |
770 | bing.com -> foo.com -> bar.com, |
771 | |
772 | This indicates that a URI and its referer are not unique. Only a URI and its |
773 | full referer chain are unique. However, HTTP requests only contain the URI |
774 | and the immediate referer. This means that any attempt at generating a |
775 | referer tree is inherently going to be a best-effort approach. |
776 | |
777 | This code assumes that the referer in a request is from the most-recent request |
778 | to that referer. |
779 | |
780 | * To maintain readability of the statistics, whenever a site is visited, all |
781 | prior referers are 'ticked' as well, so that one can easily see the breakdown. |
782 | */ |
783 | |
784 | /* Root node for all referer statistics */ |
785 | static int st_node_requests_by_referer = -1; |
786 | /* Referer statistics root node's text */ |
787 | static const char *st_str_request_sequences = "HTTP Request Sequences"; |
788 | |
789 | /* Mapping of URIs to the most-recently seen node id */ |
790 | static wmem_map_t* refstats_uri_to_node_id_hash; |
791 | /* Mapping of node ids to the node's URI ('name' value) */ |
792 | static wmem_map_t* refstats_node_id_to_uri_hash; |
793 | /* Mapping of node ids to the parent node id */ |
794 | static wmem_map_t* refstats_node_id_to_parent_node_id_hash; |
795 | |
796 | |
797 | /* HTTP/Request Sequences stats init function */ |
798 | static void |
799 | http_seq_stats_tree_init(stats_tree* st) |
800 | { |
801 | int root_node_id = 0; |
802 | void *root_node_id_p = GINT_TO_POINTER(root_node_id)((gpointer) (glong) (root_node_id)); |
803 | void *node_id_p = NULL((void*)0); |
804 | char *uri = NULL((void*)0); |
805 | |
806 | refstats_node_id_to_parent_node_id_hash = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); |
807 | refstats_node_id_to_uri_hash = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); |
808 | refstats_uri_to_node_id_hash = wmem_map_new(wmem_file_scope(), wmem_str_hash, g_str_equal); |
809 | |
810 | /* Add the root node and its mappings */ |
811 | st_node_requests_by_referer = stats_tree_create_node(st, st_str_request_sequences, root_node_id, STAT_DT_INT, true1); |
812 | node_id_p = GINT_TO_POINTER(st_node_requests_by_referer)((gpointer) (glong) (st_node_requests_by_referer)); |
813 | uri = wmem_strdup(wmem_file_scope(), st_str_request_sequences); |
814 | |
815 | wmem_map_insert(refstats_uri_to_node_id_hash, uri, node_id_p); |
816 | wmem_map_insert(refstats_node_id_to_uri_hash, node_id_p, uri); |
817 | wmem_map_insert(refstats_node_id_to_parent_node_id_hash, node_id_p, root_node_id_p); |
818 | } |
819 | |
820 | static int |
821 | http_seq_stats_tick_referer(stats_tree* st, const char* arg_referer_uri) |
822 | { |
823 | int root_node_id = st_node_requests_by_referer; |
824 | void *root_node_id_p = GINT_TO_POINTER(st_node_requests_by_referer)((gpointer) (glong) (st_node_requests_by_referer)); |
825 | int referer_node_id; |
826 | void *referer_node_id_p; |
827 | int referer_parent_node_id; |
828 | void *referer_parent_node_id_p; |
829 | char *referer_uri; |
830 | |
831 | /* Tick the referer's URI */ |
832 | /* Does the node exist? */ |
833 | if (!wmem_map_lookup_extended(refstats_uri_to_node_id_hash, arg_referer_uri, NULL((void*)0), &referer_node_id_p)) { |
834 | /* The node for the referer didn't already exist, create the mappings */ |
835 | referer_node_id = tick_stat_node(st, arg_referer_uri, root_node_id, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(arg_referer_uri) ,(root_node_id),(1),1)); |
836 | referer_node_id_p = GINT_TO_POINTER(referer_node_id)((gpointer) (glong) (referer_node_id)); |
837 | referer_parent_node_id_p = root_node_id_p; |
838 | |
839 | referer_uri = wmem_strdup(wmem_file_scope(), arg_referer_uri); |
840 | wmem_map_insert(refstats_uri_to_node_id_hash, referer_uri, referer_node_id_p); |
841 | wmem_map_insert(refstats_node_id_to_uri_hash, referer_node_id_p, referer_uri); |
842 | wmem_map_insert(refstats_node_id_to_parent_node_id_hash, referer_node_id_p, referer_parent_node_id_p); |
843 | } else { |
844 | /* The node for the referer already exists, tick it */ |
845 | referer_parent_node_id_p = wmem_map_lookup(refstats_node_id_to_parent_node_id_hash, referer_node_id_p); |
846 | referer_parent_node_id = GPOINTER_TO_INT(referer_parent_node_id_p)((gint) (glong) (referer_parent_node_id_p)); |
847 | referer_node_id = tick_stat_node(st, arg_referer_uri, referer_parent_node_id, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(arg_referer_uri) ,(referer_parent_node_id),(1),1)); |
848 | } |
849 | return referer_node_id; |
850 | } |
851 | |
852 | static void |
853 | http_seq_stats_tick_request(stats_tree* st, const char* arg_full_uri, int referer_node_id) |
854 | { |
855 | void *referer_node_id_p = GINT_TO_POINTER(referer_node_id)((gpointer) (glong) (referer_node_id)); |
856 | int node_id; |
857 | void *node_id_p; |
858 | char *uri; |
859 | |
860 | node_id = tick_stat_node(st, arg_full_uri, referer_node_id, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(arg_full_uri),(referer_node_id ),(1),1)); |
861 | node_id_p = GINT_TO_POINTER(node_id)((gpointer) (glong) (node_id)); |
862 | |
863 | /* Update the mappings. Even if the URI was already seen, the URI->node mapping may need to be updated */ |
864 | |
865 | /* Is this a new node? */ |
866 | uri = (char *) wmem_map_lookup(refstats_node_id_to_uri_hash, node_id_p); |
867 | if (!uri) { |
868 | /* node not found, add mappings for the node and uri */ |
869 | uri = wmem_strdup(wmem_file_scope(), arg_full_uri); |
870 | |
871 | wmem_map_insert(refstats_uri_to_node_id_hash, uri, node_id_p); |
872 | wmem_map_insert(refstats_node_id_to_uri_hash, node_id_p, uri); |
873 | wmem_map_insert(refstats_node_id_to_parent_node_id_hash, node_id_p, referer_node_id_p); |
874 | } else { |
875 | /* We've seen the node id before. Update the URI mapping refer to this node id*/ |
876 | wmem_map_insert(refstats_uri_to_node_id_hash, uri, node_id_p); |
877 | } |
878 | } |
879 | |
880 | static char* |
881 | determine_http_location_target(wmem_allocator_t *scope, const char *base_url, const char * location_url) |
882 | { |
883 | /* Resolving a base URI + relative URI to an absolute URI ("Relative Resolution") |
884 | is complicated. Because of that, we take shortcuts that may result in |
885 | inaccurate results, but is also significantly simpler. |
886 | It would be best to use an external library to do this for us. |
887 | For reference, the RFC is located at https://tools.ietf.org/html/rfc3986#section-5.4 |
888 | |
889 | Returns NULL if the resolution fails |
890 | */ |
891 | char *final_target; |
892 | |
893 | /* base_url must be an absolute URL.*/ |
894 | if (strstr(base_url, "://") == NULL((void*)0)){ |
895 | return NULL((void*)0); |
896 | } |
897 | |
898 | /* Empty Location */ |
899 | if (location_url[0] == '\0') { |
900 | final_target = wmem_strdup(scope, base_url); |
901 | return final_target; |
902 | } |
903 | /* Protocol Relative */ |
904 | else if (g_str_has_prefix(location_url, "//")(__builtin_constant_p ("//")? __extension__ ({ const char * const __str = (location_url); const char * const __prefix = ("//") ; gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (location_url, "//") ) ) { |
905 | char *base_scheme = g_uri_parse_scheme(base_url); |
906 | if (base_scheme == NULL((void*)0)) { |
907 | return NULL((void*)0); |
908 | } |
909 | final_target = wmem_strdup_printf(scope, "%s:%s", base_scheme, location_url); |
910 | g_free(base_scheme); |
911 | return final_target; |
912 | } |
913 | /* Absolute URL*/ |
914 | else if (strstr(location_url, "://") != NULL((void*)0)) { |
915 | final_target = wmem_strdup(scope, location_url); |
916 | return final_target; |
917 | } |
918 | /* Relative */ |
919 | else { |
920 | char *start_fragment = strstr(base_url, "#"); |
921 | char *start_query = NULL((void*)0); |
922 | char *base_url_no_fragment = NULL((void*)0); |
923 | char *base_url_no_query = NULL((void*)0); |
924 | |
925 | /* Strip off the fragment (which should never be present)*/ |
926 | if (start_fragment == NULL((void*)0)) { |
927 | base_url_no_fragment = wmem_strdup(scope, base_url); |
928 | } |
929 | else { |
930 | base_url_no_fragment = wmem_strndup(scope, base_url, start_fragment - base_url); |
931 | } |
932 | |
933 | /* Strip off the query (Queries are stripped from all relative URIs) */ |
934 | start_query = strstr(base_url_no_fragment, "?"); |
935 | if (start_query == NULL((void*)0)) { |
936 | base_url_no_query = wmem_strdup(scope, base_url_no_fragment); |
937 | } |
938 | else { |
939 | base_url_no_query = wmem_strndup(scope, base_url_no_fragment, start_query - base_url_no_fragment); |
940 | } |
941 | |
942 | /* A leading question mark (?) means to replace the old query with the new*/ |
943 | if (g_str_has_prefix(location_url, "?")(__builtin_constant_p ("?")? __extension__ ({ const char * const __str = (location_url); const char * const __prefix = ("?"); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (location_url, "?") )) { |
944 | final_target = wmem_strdup_printf(scope, "%s%s", base_url_no_query, location_url); |
945 | return final_target; |
946 | } |
947 | /* A leading slash means to put the location after the netloc */ |
948 | else if (g_str_has_prefix(location_url, "/")(__builtin_constant_p ("/")? __extension__ ({ const char * const __str = (location_url); const char * const __prefix = ("/"); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (location_url, "/") )) { |
949 | /* We have already tested strstr(base_url) above */ |
950 | char *scheme_end; |
951 | char *netloc_end; |
952 | int netloc_length; |
953 | |
954 | scheme_end = strstr(base_url_no_query, "://") + 3; |
955 | /* The following code was the only way to stop VS dereferencing errors. */ |
956 | if (!(*scheme_end)) { |
957 | return NULL((void*)0); |
958 | } |
959 | netloc_end = strstr(scheme_end, "/"); |
960 | if (!(*netloc_end)) { |
961 | return NULL((void*)0); |
962 | } |
963 | netloc_length = (int) (netloc_end - base_url_no_query); |
964 | final_target = wmem_strdup_printf(scope, "%.*s%s", netloc_length, base_url_no_query, location_url); |
965 | return final_target; |
966 | } |
967 | /* Otherwise, it replaces the last element in the URI */ |
968 | else { |
969 | char *scheme_end = strstr(base_url_no_query, "://") + 3; |
970 | char *end_of_path = g_strrstr(scheme_end, "/"); |
971 | |
972 | if (end_of_path != NULL((void*)0)) { |
973 | int base_through_path = (int) (end_of_path - base_url_no_query); |
974 | final_target = wmem_strdup_printf(scope, "%.*s/%s", base_through_path, base_url_no_query, location_url); |
975 | } |
976 | else { |
977 | final_target = wmem_strdup_printf(scope, "%s/%s", base_url_no_query, location_url); |
978 | } |
979 | |
980 | return final_target; |
981 | } |
982 | } |
983 | return NULL((void*)0); |
984 | } |
985 | |
986 | /* HTTP/Request Sequences stats packet function */ |
987 | static tap_packet_status |
988 | http_seq_stats_tree_packet(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt _U___attribute__((unused)), const void* p, tap_flags_t flags _U___attribute__((unused))) |
989 | { |
990 | const http_info_value_t* v = (const http_info_value_t*)p; |
991 | |
992 | /* Track HTTP Redirects */ |
993 | if (v->location_target && v->location_base_uri) { |
994 | int referer_node_id; |
995 | int parent_node_id; |
996 | void *parent_node_id_p; |
997 | void *current_node_id_p; |
998 | char *uri = NULL((void*)0); |
999 | |
1000 | char *absolute_target = determine_http_location_target(pinfo->pool, v->location_base_uri, v->location_target); |
1001 | /* absolute_target is NULL if the resolution fails */ |
1002 | if (absolute_target != NULL((void*)0)) { |
1003 | /* We assume the user makes the request to the absolute_target */ |
1004 | /* Tick the base URI */ |
1005 | referer_node_id = http_seq_stats_tick_referer(st, v->location_base_uri); |
1006 | |
1007 | /* Tick the location header's resolved URI */ |
1008 | http_seq_stats_tick_request(st, absolute_target, referer_node_id); |
1009 | |
1010 | /* Tick all stats nodes above the location */ |
1011 | current_node_id_p = GINT_TO_POINTER(referer_node_id)((gpointer) (glong) (referer_node_id)); |
1012 | while (wmem_map_lookup_extended(refstats_node_id_to_parent_node_id_hash, current_node_id_p, NULL((void*)0), &parent_node_id_p)) { |
1013 | parent_node_id = GPOINTER_TO_INT(parent_node_id_p)((gint) (glong) (parent_node_id_p)); |
1014 | uri = (char *) wmem_map_lookup(refstats_node_id_to_uri_hash, current_node_id_p); |
1015 | tick_stat_node(st, uri, parent_node_id, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(uri),(parent_node_id ),(1),1)); |
1016 | current_node_id_p = parent_node_id_p; |
1017 | } |
1018 | } |
1019 | } |
1020 | |
1021 | /* Track HTTP Requests/Referers */ |
1022 | if (v->request_method && v->referer_uri && v->full_uri) { |
1023 | int referer_node_id; |
1024 | int parent_node_id; |
1025 | void *parent_node_id_p; |
1026 | void *current_node_id_p; |
1027 | char *uri = NULL((void*)0); |
1028 | /* Tick the referer's URI */ |
1029 | referer_node_id = http_seq_stats_tick_referer(st, v->referer_uri); |
1030 | |
1031 | /* Tick the request's URI */ |
1032 | http_seq_stats_tick_request(st, v->full_uri, referer_node_id); |
1033 | |
1034 | /* Tick all stats nodes above the referer */ |
1035 | current_node_id_p = GINT_TO_POINTER(referer_node_id)((gpointer) (glong) (referer_node_id)); |
1036 | while (wmem_map_lookup_extended(refstats_node_id_to_parent_node_id_hash, current_node_id_p, NULL((void*)0), &parent_node_id_p)) { |
1037 | parent_node_id = GPOINTER_TO_INT(parent_node_id_p)((gint) (glong) (parent_node_id_p)); |
1038 | uri = (char *) wmem_map_lookup(refstats_node_id_to_uri_hash, current_node_id_p); |
1039 | tick_stat_node(st, uri, parent_node_id, true)(stats_tree_manip_node_int(MN_INCREASE,(st),(uri),(parent_node_id ),(1),1)); |
1040 | current_node_id_p = parent_node_id_p; |
1041 | } |
1042 | } |
1043 | return TAP_PACKET_DONT_REDRAW; |
1044 | } |
1045 | |
1046 | |
1047 | static void |
1048 | dissect_http_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
1049 | const char *line) |
1050 | { |
1051 | tvbuff_t *ntlmssp_tvb; |
1052 | |
1053 | ntlmssp_tvb = base64_to_tvb(tvb, line); |
1054 | add_new_data_source(pinfo, ntlmssp_tvb, "NTLMSSP / GSSAPI Data"); |
1055 | if (tvb_strneql(ntlmssp_tvb, 0, "NTLMSSP", 7) == 0) |
1056 | call_dissector(ntlmssp_handle, ntlmssp_tvb, pinfo, tree); |
1057 | else |
1058 | call_dissector(gssapi_handle, ntlmssp_tvb, pinfo, tree); |
1059 | } |
1060 | |
1061 | static void |
1062 | dissect_http_kerberos(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
1063 | const char *line) |
1064 | { |
1065 | tvbuff_t *kerberos_tvb; |
1066 | |
1067 | kerberos_tvb = base64_to_tvb(tvb, line + 9); /* skip 'Kerberos ' which is 9 chars */ |
1068 | add_new_data_source(pinfo, kerberos_tvb, "Kerberos Data"); |
1069 | call_dissector(gssapi_handle, kerberos_tvb, pinfo, tree); |
1070 | |
1071 | } |
1072 | |
1073 | |
1074 | static http_conv_t * |
1075 | get_http_conversation_data(packet_info *pinfo, conversation_t **conversation) |
1076 | { |
1077 | http_conv_t*conv_data; |
1078 | |
1079 | *conversation = find_or_create_conversation(pinfo); |
1080 | |
1081 | /* Retrieve information from conversation |
1082 | * or add it if it isn't there yet |
1083 | */ |
1084 | conv_data = (http_conv_t *)conversation_get_proto_data(*conversation, proto_http); |
1085 | if(!conv_data) { |
1086 | /* Setup the conversation structure itself */ |
1087 | conv_data = wmem_new0(wmem_file_scope(), http_conv_t)((http_conv_t*)wmem_alloc0((wmem_file_scope()), sizeof(http_conv_t ))); |
1088 | conv_data->chunk_offsets_fwd = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); |
1089 | conv_data->chunk_offsets_rev = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); |
1090 | conv_data->req_list = NULL((void*)0); |
1091 | conv_data->matches_table = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); |
1092 | |
1093 | conversation_add_proto_data(*conversation, proto_http, |
1094 | conv_data); |
1095 | } |
1096 | |
1097 | return conv_data; |
1098 | } |
1099 | |
1100 | /** |
1101 | * create a new http_req_res_t and add it to the conversation. |
1102 | * @return the new allocated object which is already added to the linked list |
1103 | */ |
1104 | static http_req_res_t* |
1105 | push_req_res(http_conv_t *conv_data) |
1106 | { |
1107 | http_req_res_t *req_res = wmem_new0(wmem_file_scope(), http_req_res_t)((http_req_res_t*)wmem_alloc0((wmem_file_scope()), sizeof(http_req_res_t ))); |
1108 | |
1109 | nstime_set_unset(&(req_res->req_ts)); |
1110 | conv_data->req_res_tail = req_res; |
1111 | req_res->private_data = wmem_new0(wmem_file_scope(), http_req_res_private_data_t)((http_req_res_private_data_t*)wmem_alloc0((wmem_file_scope() ), sizeof(http_req_res_private_data_t))); |
1112 | |
1113 | return req_res; |
1114 | } |
1115 | |
1116 | /** |
1117 | * push a request frame number and its time stamp to the conversation data. |
1118 | */ |
1119 | static http_req_res_t* |
1120 | push_req(http_conv_t *conv_data, packet_info *pinfo) |
1121 | { |
1122 | /* a request will always create a new http_req_res_t object */ |
1123 | http_req_res_t *req_res = push_req_res(conv_data); |
1124 | |
1125 | req_res->req_framenum = pinfo->num; |
1126 | req_res->req_ts = pinfo->abs_ts; |
1127 | |
1128 | /* XXX: Using the same proto key for the frame doesn't work well |
1129 | * with HTTP 1.1 pipelining, or other situations where more |
1130 | * than one request can appear in a frame. |
1131 | */ |
1132 | p_add_proto_data(wmem_file_scope(), pinfo, proto_http, HTTP_PROTO_DATA_REQRES0, req_res); |
1133 | |
1134 | return req_res; |
1135 | } |
1136 | |
1137 | /** |
1138 | * push a response frame number to the conversation data. |
1139 | */ |
1140 | static http_req_res_t* |
1141 | push_res(http_conv_t *conv_data, packet_info *pinfo) |
1142 | { |
1143 | /* a response will create a new http_req_res_t object: if no |
1144 | object exists, or if the most recent one is already for |
1145 | a different response. (Exception: If the previous response |
1146 | code was in the Informational 1xx category, then it was |
1147 | an interim response, and this response could be for the same |
1148 | request.) In both cases the corresponding request was not |
1149 | detected/included in the conversation. In all other cases |
1150 | the http_req_res_t object created by the request is |
1151 | used. */ |
1152 | /* XXX: This finds the only most recent request and doesn't support |
1153 | * HTTP 1.1 pipelining. This limitation has been addressed for |
1154 | * HTTP GETS if Range Requests are supported. |
1155 | */ |
1156 | http_req_res_t *req_res = conv_data->req_res_tail; |
1157 | if (!req_res || (req_res->res_framenum > 0 && req_res->response_code >= 200)) { |
1158 | req_res = push_req_res(conv_data); |
1159 | } |
1160 | req_res->res_framenum = pinfo->num; |
1161 | /* XXX: Using the same proto key for the frame doesn't work well |
1162 | * with HTTP 1.1 pipelining, or other situations where more |
1163 | * than one request can appear in a frame and multiple outstanding |
1164 | * GET requests. The latter has been addressed with matches_table." |
1165 | */ |
1166 | p_add_proto_data(wmem_file_scope(), pinfo, proto_http, HTTP_PROTO_DATA_REQRES0, req_res); |
1167 | |
1168 | return req_res; |
1169 | } |
1170 | |
1171 | static int |
1172 | dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo, |
1173 | proto_tree *tree, http_conv_t *conv_data, |
1174 | const char* proto_tag, int proto, bool_Bool end_of_stream, |
1175 | const uint32_t* const seq) |
1176 | { |
1177 | proto_tree*http_tree = NULL((void*)0); |
1178 | proto_item*ti = NULL((void*)0); |
1179 | proto_item*hidden_item; |
1180 | const unsigned char*line, *firstline; |
1181 | intnext_offset; |
1182 | const unsigned char*linep, *lineend; |
1183 | intorig_offset = offset; |
1184 | intfirst_linelen, linelen; |
1185 | bool_Boolis_request_or_reply, is_tls = false0; |
1186 | bool_Boolsaw_req_resp_or_header; |
1187 | media_container_type_t http_type; |
1188 | proto_item*hdr_item = NULL((void*)0); |
1189 | ReqRespDissector reqresp_dissector; |
1190 | proto_tree*req_tree; |
1191 | intcolon_offset; |
1192 | headers_t*headers = NULL((void*)0); |
| 1 | 'headers' initialized to a null pointer value | |
|
1193 | intdatalen; |
1194 | intreported_datalen = -1; |
1195 | dissector_handle_t handle = NULL((void*)0); |
1196 | bool_Booldissected = false0; |
1197 | bool_Boolfirst_loop = true1; |
1198 | bool_Boolhave_seen_http = false0; |
1199 | /*unsignedi;*/ |
1200 | /*http_info_value_t *si;*/ |
1201 | http_eo_t *eo_info; |
1202 | heur_dtbl_entry_t *hdtbl_entry; |
1203 | int reported_length; |
1204 | uint16_t word; |
1205 | bool_Boolleading_crlf = false0; |
1206 | bool_Boolexcess_data = false0; |
1207 | media_content_info_t* content_info = NULL((void*)0); |
1208 | wmem_map_t* header_value_map = NULL((void*)0); |
1209 | int chunk_offset = 0; |
1210 | wmem_map_t*chunk_map = NULL((void*)0); |
1211 | /* |
1212 | * For supporting dissecting chunked data in streaming reassembly mode. |
1213 | * |
1214 | * If a HTTP request or response is chunked encoding (the transfer-encoding |
1215 | * header is 'chunked') and its content-type matching a subdissector in |
1216 | * "streaming_content_type" dissector table, then we switch to dissect in |
1217 | * streaming chunk mode. In streaming chunk mode, we dissect the data as soon |
1218 | * as possible, unlike normal mode, we don't start reassembling until the end |
1219 | * of the request or response message or at the end of the TCP stream. In |
1220 | * streaming chunk mode, the first reassembled PDU contains HTTP headers |
1221 | * and at least one completed chunk of this request or response message. And |
1222 | * subsequent PDUs consist of one or more chunks: |
1223 | * |
1224 | * ----- +-- Reassembled Streaming Content PDU(s) --+-- Reassembled Streaming Content PDU(s) --+--- Reassembled ... |
1225 | * HLProtos | 1*high-proto-pdu | 1*high-proto-pdu | 1*high-proto-pdu |
1226 | * ----- +-------------------------------------+----+--------------------------------+---------+------------------- |
1227 | * | de-chunked-data | de-chunked-data | de-chunked-data |
1228 | * HTTP +-------- First Reassembled HTTP PDU -----------+--- Second Reassembled HTTP PDU -----+- Third PDU -+ +- Fourth --- |
1229 | * | headers and 1*chunk | 1*chunk | 1*chunk | | 1*chunk ... |
1230 | * ----- +--------- TCP segment ---------+ +-----------TCP segment -----------+ +---- TCP segment ---------+ +------------ |
1231 | * TCP | headers | *chunk | part-chunk | | part-chunk | *chunk | part-chunk | | part-chunk | 1*chunk | | 1*chunk ... |
1232 | * ----- +---------+--------+------------+ +------------+--------+------------+ +------------+-------------+ +------------ |
1233 | * |
1234 | * Notation: |
1235 | * - headers HTTP headers of a request or response message. |
1236 | * - part-chunk The front or rear part of a HTTP chunk. |
1237 | * - *chunk Zero or more completed HTTP chunks of a HTTP message. |
1238 | * - 1*chunk One or more completed HTTP chunks of a HTTP message. |
1239 | * - de-chunked-data De-chunked HTTP body data based on one or more completed chunks. |
1240 | * - 1*high-proto-pdu One or more high level protocol (on top of HTTP) PDUs. |
1241 | * - HLProtos High Level Protocols like GRPC-Web. |
1242 | * |
1243 | * The headers and content_info of the req_res are allocated in file scope that |
1244 | * helps to provide information for dissecting subsequent PDUs which only |
1245 | * contains chunks without headers. |
1246 | */ |
1247 | bool_Bool streaming_chunk_mode = false0; |
1248 | bool_Bool begin_with_chunk = false0; |
1249 | http_streaming_reassembly_data_t* streaming_reassembly_data = NULL((void*)0); |
1250 | |
1251 | http_req_res_t *curr = (http_req_res_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_http, HTTP_PROTO_DATA_REQRES0); |
1252 | http_info_value_t *stat_info = NULL((void*)0); |
1253 | http_req_res_private_data_t* prv_data = curr ? (http_req_res_private_data_t*)curr->private_data : NULL((void*)0); |
| |
| |
1254 | http_req_res_private_data_t* tail_prv_data = NULL((void*)0); |
1255 | |
1256 | /* Determine the direction as in the TCP dissector, but don't call |
1257 | * get_tcp_conversation_data because we don't want to create a new |
1258 | * TCP stream if it doesn't exist (e.g., SSDP over UDP.) |
1259 | */ |
1260 | int direction = cmp_address(&pinfo->src, &pinfo->dst); |
1261 | /* if the addresses are equal, match the ports instead */ |
1262 | if (direction == 0) { |
| 4 | | Assuming 'direction' is not equal to 0 | |
|
| |
1263 | direction = (pinfo->srcport > pinfo->destport) ? 1 : -1; |
1264 | } |
1265 | if (direction >= 0) { |
| 6 | | Assuming 'direction' is < 0 | |
|
| |
1266 | chunk_map = conv_data->chunk_offsets_fwd; |
1267 | } else { |
1268 | chunk_map = conv_data->chunk_offsets_rev; |
1269 | } |
1270 | |
1271 | if (seq && chunk_map) { |
| |
1272 | chunk_offset = GPOINTER_TO_INT(wmem_map_lookup(chunk_map, GUINT_TO_POINTER(*seq)))((gint) (glong) (wmem_map_lookup(chunk_map, ((gpointer) (gulong ) (*seq))))); |
1273 | /* Returns 0 when there is no entry in the map, as we want. */ |
1274 | } |
1275 | |
1276 | reported_length = tvb_reported_length_remaining(tvb, offset); |
1277 | if (reported_length < 1) { |
| 9 | | Assuming 'reported_length' is >= 1 | |
|
| |
1278 | return -1; |
1279 | } |
1280 | |
1281 | /* RFC 2616 |
1282 | * In the interest of robustness, servers SHOULD ignore any empty |
1283 | * line(s) received where a Request-Line is expected. In other words, if |
1284 | * the server is reading the protocol stream at the beginning of a |
1285 | * message and receives a CRLF first, it should ignore the CRLF. |
1286 | */ |
1287 | if (reported_length > 3) { |
| 11 | | Assuming 'reported_length' is <= 3 | |
|
| |
1288 | word = tvb_get_ntohs(tvb,offset); |
1289 | if (word == 0x0d0a) { |
1290 | leading_crlf = true1; |
1291 | offset += 2; |
1292 | } |
1293 | } |
1294 | |
1295 | /* |
1296 | * If we previously dissected an HTTP request in this conversation then |
1297 | * we should be pretty sure that whatever we got in this TVB is |
1298 | * actually HTTP (even if what we have here is part of a file being |
1299 | * transferred over HTTP). |
1300 | */ |
1301 | if (conv_data->req_res_tail) |
| 13 | | Assuming field 'req_res_tail' is null | |
|
| |
1302 | have_seen_http = true1; |
1303 | |
1304 | /* |
1305 | * If this is binary data then there's no point in doing all the string |
1306 | * operations below: they'll just be slow on this data. |
1307 | */ |
1308 | if (!g_ascii_isprint(tvb_get_uint8(tvb, offset))((g_ascii_table[(guchar) (tvb_get_uint8(tvb, offset))] & G_ASCII_PRINT ) != 0)) { |
| 15 | | Assuming the condition is false | |
|
| |
1309 | /* |
1310 | * But, if we've seen some real HTTP then we're sure this is |
1311 | * an HTTP conversation, and this is binary file data. |
1312 | * Mark it as such. |
1313 | */ |
1314 | if (have_seen_http) { |
1315 | tvbuff_t *next_tvb; |
1316 | int data_len; |
1317 | |
1318 | col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag); |
1319 | col_set_str(pinfo->cinfo, COL_INFO, "Continuation"); |
1320 | ti = proto_tree_add_item(tree, proto, tvb, offset, -1, ENC_NA0x00000000); |
1321 | http_tree = proto_item_add_subtree(ti, ett_http); |
1322 | |
1323 | next_tvb = tvb_new_subset_remaining(tvb, orig_offset); |
1324 | /* If orig_offset > 0, this isn't the first message |
1325 | * dissected in this TCP segment, which means we had |
1326 | * a Content-Length, but more data after that body. |
1327 | */ |
1328 | if (orig_offset > 0) { |
1329 | proto_tree_add_expert(http_tree, pinfo, &ei_http_excess_data, next_tvb, 0, tvb_captured_length(next_tvb)); |
1330 | } |
1331 | /* Send it to Follow HTTP Stream and mark as file data */ |
1332 | if(have_tap_listener(http_follow_tap)) { |
1333 | tap_queue_packet(http_follow_tap, pinfo, next_tvb); |
1334 | } |
1335 | data_len = tvb_captured_length(next_tvb); |
1336 | proto_tree_add_bytes_format_value(http_tree, hf_http_file_data, |
1337 | next_tvb, 0, data_len, NULL((void*)0), "%u byte%s", data_len, plurality(data_len, "", "s")((data_len) == 1 ? ("") : ("s"))); |
1338 | call_data_dissector(next_tvb, pinfo, http_tree); |
1339 | } |
1340 | return -1; |
1341 | } |
1342 | |
1343 | /* |
1344 | * Is this a request or response? |
1345 | * |
1346 | * Note that "tvb_find_line_end()" will return a value that |
1347 | * is not longer than what's in the buffer, so the |
1348 | * "tvb_get_ptr()" call won't throw an exception. |
1349 | */ |
1350 | first_linelen = tvb_find_line_end(tvb, offset, |
1351 | tvb_ensure_captured_length_remaining(tvb, offset), &next_offset, |
1352 | true1); |
1353 | |
1354 | if (first_linelen == -1) { |
| 17 | | Assuming the condition is false | |
|
1355 | /* No complete line was found in this segment, do |
1356 | * desegmentation if we're told to. |
1357 | */ |
1358 | if (!req_resp_hdrs_do_reassembly(tvb, offset, pinfo, |
1359 | http_desegment_headers, http_desegment_body, false0, &chunk_offset, |
1360 | streaming_content_type_dissector_table, &handle)) { |
1361 | /* |
1362 | * More data needed for desegmentation. |
1363 | */ |
1364 | return -1; |
1365 | } |
1366 | } |
1367 | |
1368 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && conv_data->req_res_tail && conv_data->req_res_tail->private_data) { |
| 18 | | Assuming field 'visited' is not equal to 0 | |
|
1369 | tail_prv_data = (http_req_res_private_data_t*) conv_data->req_res_tail->private_data; |
1370 | } |
1371 | |
1372 | /* Check whether the first line is the beginning of a chunk. If it is the beginning |
1373 | * of a chunk, the headers and at least one chunk of HTTP request or response should |
1374 | * be dissected in the previous packets, and now we are processing subsequent chunks. |
1375 | */ |
1376 | if (http_desegment_body && http_dechunk_body) { |
| 19 | | Assuming 'http_desegment_body' is true | |
|
| 20 | | Assuming 'http_dechunk_body' is true | |
|
| |
1377 | begin_with_chunk = starts_with_chunk_size(tvb, offset, pinfo); |
| 22 | | Calling 'starts_with_chunk_size' | |
|
| 32 | | Returning from 'starts_with_chunk_size' | |
|
1378 | |
1379 | if (begin_with_chunk32.1 | 'begin_with_chunk' is true |
32.1 | 'begin_with_chunk' is true | && |
1380 | ((prv_data && ( /* This packet has been parsed */ |
1381 | /* and now we are in a HTTP request chunk stream */ |
1382 | (prv_data->req_fwd_flow == direction && prv_data->req_streaming_reassembly_data) || |
1383 | /* and now we are in a HTTP response chunk stream */ |
1384 | (prv_data->req_fwd_flow != direction && prv_data->res_streaming_reassembly_data))) |
1385 | || |
1386 | (tail_prv_data32.3 | 'tail_prv_data' is null |
32.3 | 'tail_prv_data' is null | && ( /* This packet has not been parsed and headers info in conv_data->req_res_tail */ |
1387 | /* and now we are in a HTTP request chunk stream */ |
1388 | (tail_prv_data->req_fwd_flow == direction && tail_prv_data->req_streaming_reassembly_data) || |
1389 | /* and now we are in a HTTP response chunk stream */ |
1390 | (tail_prv_data->req_fwd_flow != direction && tail_prv_data->res_streaming_reassembly_data))))) |
1391 | { |
1392 | streaming_chunk_mode = true1; |
1393 | } |
1394 | } |
1395 | |
1396 | /* |
1397 | * Is the first line a request or response? |
1398 | * |
1399 | * Note that "tvb_find_line_end()" will return a value that |
1400 | * is not longer than what's in the buffer, so the |
1401 | * "tvb_get_ptr()" call won't throw an exception. |
1402 | */ |
1403 | firstline = tvb_get_ptr(tvb, offset, first_linelen); |
1404 | http_type = MEDIA_CONTAINER_HTTP_OTHERS;/* type not known yet */ |
1405 | is_request_or_reply = is_http_request_or_reply(pinfo, (const char *)firstline, |
| 33 | | Calling 'is_http_request_or_reply' | |
|
| 39 | | Returning from 'is_http_request_or_reply' | |
|
1406 | first_linelen, &http_type, NULL((void*)0), conv_data); |
1407 | if (is_request_or_reply39.1 | 'is_request_or_reply' is true |
39.1 | 'is_request_or_reply' is true | || streaming_chunk_mode) { |
1408 | bool_Bool try_desegment_body; |
1409 | |
1410 | if (streaming_chunk_mode39.2 | 'streaming_chunk_mode' is false |
39.2 | 'streaming_chunk_mode' is false | && begin_with_chunk) { |
1411 | col_set_str(pinfo->cinfo, COL_INFO, "Chunk Stream "); |
1412 | } else { |
1413 | /* |
1414 | * Yes, it's a request or response. |
1415 | * Put the first line from the buffer into the summary |
1416 | * (but leave out the line terminator). |
1417 | */ |
1418 | col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", format_text(pinfo->pool, firstline, first_linelen)); |
1419 | } |
1420 | |
1421 | /* |
1422 | * Do header desegmentation if we've been told to, |
1423 | * and do body desegmentation if we've been told to and |
1424 | * we find a Content-Length header in requests. |
1425 | * |
1426 | * The following cases (from RFC 7230, Section 3.3) never have a |
1427 | * response body, so do not attempt to desegment the body for: |
1428 | * * Responses to HEAD requests. |
1429 | * * 2xx responses to CONNECT requests. |
1430 | * * 1xx, 204 No Content, 304 Not Modified responses. |
1431 | * |
1432 | * Additionally if we are at the end of stream, no more segments |
1433 | * will be added so disable body segmentation too in that case. |
1434 | */ |
1435 | try_desegment_body = (http_desegment_body && !end_of_stream); |
| 40 | | Assuming 'http_desegment_body' is false | |
|
1436 | if (try_desegment_body40.1 | 'try_desegment_body' is false |
40.1 | 'try_desegment_body' is false | && http_type == MEDIA_CONTAINER_HTTP_RESPONSE && !streaming_chunk_mode) { |
1437 | /* |
1438 | * The response_code is not yet set, so extract |
1439 | * the response code from the current line. |
1440 | */ |
1441 | int response_code = parse_http_status_code(firstline, firstline + first_linelen); |
1442 | /* |
1443 | * On a second pass, we should have already associated |
1444 | * the response with the request. On a first sequential |
1445 | * pass, we haven't done so yet (as we don't know if we |
1446 | * need more data), so get the request method from the |
1447 | * most recent request, if it exists. |
1448 | */ |
1449 | char* request_method = NULL((void*)0); |
1450 | if (curr) { |
1451 | request_method = curr->request_method; |
1452 | } else if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && conv_data->req_res_tail) { |
1453 | request_method = conv_data->req_res_tail->request_method; |
1454 | } |
1455 | if ((g_strcmp0(request_method, "HEAD") == 0 || |
1456 | (response_code / 100 == 2 && |
1457 | (g_strcmp0(request_method, "CONNECT") == 0 || |
1458 | g_strcmp0(request_method, "SSTP_DUPLEX_POST") == 0)) || |
1459 | response_code / 100 == 1 || |
1460 | response_code == 204 || |
1461 | response_code == 304)) { |
1462 | /* No response body is present. */ |
1463 | try_desegment_body = false0; |
1464 | } |
1465 | } |
1466 | if (!req_resp_hdrs_do_reassembly(tvb, offset, pinfo, |
| 41 | | Assuming the condition is false | |
|
1467 | http_desegment_headers, try_desegment_body, http_type == MEDIA_CONTAINER_HTTP_RESPONSE, &chunk_offset, |
1468 | streaming_content_type_dissector_table, &handle)) { |
1469 | /* |
1470 | * More data needed for desegmentation. |
1471 | */ |
1472 | if (seq && chunk_map && chunk_offset) { |
1473 | wmem_map_insert(chunk_map, GUINT_TO_POINTER(*seq)((gpointer) (gulong) (*seq)), GINT_TO_POINTER(chunk_offset)((gpointer) (glong) (chunk_offset))); |
1474 | } |
1475 | return -1; |
1476 | } |
1477 | |
1478 | if (handle && http_desegment_body && http_dechunk_body) { |
| 42 | | Assuming 'handle' is non-null | |
|
| 43 | | Assuming 'http_desegment_body' is true | |
|
| 44 | | Assuming 'http_dechunk_body' is true | |
|
| |
1479 | /* This handle is set because there is a header 'Transfer-Encoding: chunked', and |
1480 | * a streaming mode reassembly supported subdissector is found according to the |
1481 | * header of Content-Type. |
1482 | */ |
1483 | streaming_chunk_mode = true1; |
1484 | } |
1485 | } else if (have_seen_http) { |
1486 | /* |
1487 | * If we know this is HTTP then call it continuation. |
1488 | */ |
1489 | /* If orig_offset > 0, this isn't the first message dissected |
1490 | * in this segment, which means we had a Content-Length, but |
1491 | * more data after the body. If this isn't a request or reply, |
1492 | * that's bogus, and probably means the Content-Length was |
1493 | * wrong. |
1494 | */ |
1495 | if (orig_offset > 0) { |
1496 | excess_data = true1; |
1497 | } |
1498 | col_set_str(pinfo->cinfo, COL_INFO, "Continuation"); |
1499 | } |
1500 | |
1501 | if (is_request_or_reply45.1 | 'is_request_or_reply' is true |
45.1 | 'is_request_or_reply' is true | || have_seen_http || streaming_chunk_mode) { |
1502 | /* |
1503 | * Now set COL_PROTOCOL and create the http tree for the |
1504 | * cases where we set COL_INFO above. |
1505 | */ |
1506 | col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag); |
1507 | ti = proto_tree_add_item(tree, proto, tvb, offset, -1, ENC_NA0x00000000); |
1508 | http_tree = proto_item_add_subtree(ti, ett_http); |
1509 | |
1510 | if (leading_crlf45.2 | 'leading_crlf' is false |
45.2 | 'leading_crlf' is false | ) { |
| |
1511 | proto_tree_add_expert(http_tree, pinfo, &ei_http_leading_crlf, tvb, offset-2, 2); |
1512 | } |
1513 | if (excess_data46.1 | 'excess_data' is false |
46.1 | 'excess_data' is false | ) { |
| |
1514 | proto_tree_add_expert(http_tree, pinfo, &ei_http_excess_data, tvb, offset, tvb_captured_length_remaining(tvb, offset)); |
1515 | } |
1516 | } |
1517 | |
1518 | is_tls = proto_is_frame_protocol(pinfo->layers, "tls"); |
1519 | |
1520 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && begin_with_chunk |
| 48 | | Assuming field 'visited' is not equal to 0 | |
|
1521 | && streaming_chunk_mode && conv_data->req_res_tail) { |
1522 | /* point this packet beginning with a chunk to req_res info created in previous packet. */ |
1523 | curr = conv_data->req_res_tail; |
1524 | prv_data = (http_req_res_private_data_t*)curr->private_data; |
1525 | p_set_proto_data(wmem_file_scope(), pinfo, proto_http, HTTP_PROTO_DATA_REQRES0, curr); |
1526 | } |
1527 | |
1528 | if (prv_data) { |
1529 | if (prv_data->req_fwd_flow == direction && prv_data->req_streaming_reassembly_data) { |
1530 | /* in request flow */ |
1531 | streaming_reassembly_data = prv_data->req_streaming_reassembly_data; |
1532 | } else if (prv_data->req_fwd_flow != direction && prv_data->res_streaming_reassembly_data) { |
1533 | /* in response flow */ |
1534 | streaming_reassembly_data = prv_data->res_streaming_reassembly_data; |
1535 | } |
1536 | |
1537 | if (streaming_reassembly_data) { |
1538 | streaming_chunk_mode = true1; |
1539 | headers = streaming_reassembly_data->main_headers; |
1540 | handle = streaming_reassembly_data->streaming_handle; |
1541 | content_info = streaming_reassembly_data->content_info; |
1542 | header_value_map = (wmem_map_t*) content_info->data; |
1543 | } |
1544 | } |
1545 | |
1546 | if (streaming_chunk_mode48.2 | 'streaming_chunk_mode' is true |
48.2 | 'streaming_chunk_mode' is true | && begin_with_chunk48.3 | 'begin_with_chunk' is true |
48.3 | 'begin_with_chunk' is true | ) { |
| |
1547 | datalen = reported_length; |
1548 | goto dissecting_body; |
| 50 | | Control jumps to line 2044 | |
|
1549 | } |
1550 | |
1551 | stat_info = wmem_new(pinfo->pool, http_info_value_t)((http_info_value_t*)wmem_alloc((pinfo->pool), sizeof(http_info_value_t ))); |
1552 | stat_info->framenum = pinfo->num; |
1553 | stat_info->response_code = 0; |
1554 | stat_info->request_method = NULL((void*)0); |
1555 | stat_info->request_uri = NULL((void*)0); |
1556 | stat_info->referer_uri = NULL((void*)0); |
1557 | stat_info->http_host = NULL((void*)0); |
1558 | stat_info->full_uri = NULL((void*)0); |
1559 | stat_info->location_target = NULL((void*)0); |
1560 | stat_info->location_base_uri = NULL((void*)0); |
1561 | p_set_proto_data(pinfo->pool, pinfo, proto_http, HTTP_PROTO_DATA_INFO1, (void *)stat_info); |
1562 | |
1563 | /* |
1564 | * Process the packet data, a line at a time. |
1565 | */ |
1566 | http_type = MEDIA_CONTAINER_HTTP_OTHERS;/* type not known yet */ |
1567 | if (headers == NULL((void*)0)) { |
1568 | DISSECTOR_ASSERT_HINT(!PINFO_FD_VISITED(pinfo) || (PINFO_FD_VISITED(pinfo) && !streaming_chunk_mode),((void) ((!((pinfo)->fd->visited) || (((pinfo)->fd-> visited) && !streaming_chunk_mode)) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\" (%s)", "epan/dissectors/packet-http.c" , 1569, "!((pinfo)->fd->visited) || (((pinfo)->fd->visited) && !streaming_chunk_mode)" , "The headers variable should not be NULL if it is in streaming mode during a non first scan." )))) |
1569 | "The headers variable should not be NULL if it is in streaming mode during a non first scan.")((void) ((!((pinfo)->fd->visited) || (((pinfo)->fd-> visited) && !streaming_chunk_mode)) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\" (%s)", "epan/dissectors/packet-http.c" , 1569, "!((pinfo)->fd->visited) || (((pinfo)->fd->visited) && !streaming_chunk_mode)" , "The headers variable should not be NULL if it is in streaming mode during a non first scan." )))); |
1570 | DISSECTOR_ASSERT_HINT(header_value_map == NULL, "The header_value_map variable should be NULL while headers is NULL.")((void) ((header_value_map == ((void*)0)) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\" (%s)", "epan/dissectors/packet-http.c" , 1570, "header_value_map == ((void*)0)", "The header_value_map variable should be NULL while headers is NULL." )))); |
1571 | |
1572 | headers = wmem_new0((streaming_chunk_mode ? wmem_file_scope() : pinfo->pool), headers_t)((headers_t*)wmem_alloc0(((streaming_chunk_mode ? wmem_file_scope () : pinfo->pool)), sizeof(headers_t))); |
1573 | header_value_map = wmem_map_new((streaming_chunk_mode ? wmem_file_scope() : pinfo->pool), g_str_hash, g_str_equal); |
1574 | } |
1575 | |
1576 | saw_req_resp_or_header = false0;/* haven't seen anything yet */ |
1577 | while (tvb_offset_exists(tvb, offset)) { |
1578 | /* |
1579 | * Find the end of the line. |
1580 | * XXX - what if we don't find it because the packet |
1581 | * is cut short by a snapshot length or the header is |
1582 | * split across TCP segments? How much dissection should |
1583 | * we do on it? |
1584 | */ |
1585 | linelen = tvb_find_line_end(tvb, offset, |
1586 | tvb_ensure_captured_length_remaining(tvb, offset), &next_offset, |
1587 | false0); |
1588 | if (linelen < 0) |
1589 | return -1; |
1590 | |
1591 | /* |
1592 | * Get a buffer that refers to the line. |
1593 | * |
1594 | * Note that "tvb_find_line_end()" will return a value that |
1595 | * is not longer than what's in the buffer, so the |
1596 | * "tvb_get_ptr()" call won't throw an exception. |
1597 | */ |
1598 | line = tvb_get_ptr(tvb, offset, linelen); |
1599 | lineend = line + linelen; |
1600 | colon_offset = -1; |
1601 | |
1602 | /* |
1603 | * OK, does it look like an HTTP request or response? |
1604 | */ |
1605 | reqresp_dissector = NULL((void*)0); |
1606 | is_request_or_reply = |
1607 | is_http_request_or_reply(pinfo, (const char *)line, |
1608 | linelen, &http_type, &reqresp_dissector, conv_data); |
1609 | if (is_request_or_reply) |
1610 | goto is_http; |
1611 | |
1612 | /* |
1613 | * No. Does it look like a blank line (as would appear |
1614 | * at the end of an HTTP request)? |
1615 | */ |
1616 | if (linelen == 0) |
1617 | goto is_http;/* Yes. */ |
1618 | |
1619 | /* |
1620 | * No. Does it look like a header? |
1621 | */ |
1622 | colon_offset = offset; |
1623 | |
1624 | linep = (const unsigned char *)memchr(line, ':', linelen); |
1625 | if (linep) { |
1626 | /* |
1627 | * Colon found, assume it is a header if we've seen a |
1628 | * valid line before. Check a little more if not. |
1629 | */ |
1630 | if (saw_req_resp_or_header || valid_header_name(line, (int)(linep - line))) { |
1631 | colon_offset += (int)(linep - line); |
1632 | if (http_check_ascii_headers) { |
1633 | int i; |
1634 | for (i = 0; i < linelen; i++) { |
1635 | if (line[i] & 0x80) { |
1636 | /* |
1637 | * Non-ASCII! Return -2 for invalid |
1638 | * HTTP, distinct from -1 for possible |
1639 | * reassembly required. |
1640 | */ |
1641 | return -2; |
1642 | } |
1643 | } |
1644 | } |
1645 | goto is_http; |
1646 | } |
1647 | } |
1648 | |
1649 | /* |
1650 | * We haven't seen the colon yet. |
1651 | * |
1652 | * If we've already seen an HTTP request or response |
1653 | * line, or a header line, and we're at the end of |
1654 | * the tvbuff, we assume this is an incomplete header |
1655 | * line. (We quit this loop after seeing a blank line, |
1656 | * so if we've seen a request or response line, or a |
1657 | * header line, this is probably more of the request |
1658 | * or response we're presumably seeing. There is some |
1659 | * risk of false positives, but the same applies for |
1660 | * full request or response lines or header lines, |
1661 | * although that's less likely.) |
1662 | * |
1663 | * We throw an exception in that case, by checking for |
1664 | * the existence of the next byte after the last one |
1665 | * in the line. If it exists, "tvb_ensure_bytes_exist()" |
1666 | * throws no exception, and we fall through to the |
1667 | * "not HTTP" case. If it doesn't exist, |
1668 | * "tvb_ensure_bytes_exist()" will throw the appropriate |
1669 | * exception. |
1670 | */ |
1671 | if (saw_req_resp_or_header) |
1672 | tvb_ensure_bytes_exist(tvb, offset, linelen + 1); |
1673 | |
1674 | /* |
1675 | * We don't consider this part of an HTTP request or |
1676 | * reply, so we don't display it. |
1677 | * (Yeah, that means we don't display, say, a text/http |
1678 | * page, but you can get that from the data pane.) |
1679 | */ |
1680 | break; |
1681 | |
1682 | is_http: |
1683 | if ((tree) && (http_tree == NULL((void*)0))) { |
1684 | ti = proto_tree_add_item(tree, proto, tvb, orig_offset, -1, ENC_NA0x00000000); |
1685 | http_tree = proto_item_add_subtree(ti, ett_http); |
1686 | if (leading_crlf) { |
1687 | proto_tree_add_expert(http_tree, pinfo, &ei_http_leading_crlf, tvb, orig_offset-2, 2); |
1688 | } |
1689 | } |
1690 | |
1691 | if (first_loop && !is_tls && pinfo->ptype == PT_TCP && |
1692 | (pinfo->srcport == 443 || pinfo->destport == 443)) { |
1693 | expert_add_info(pinfo, ti, &ei_http_tls_port); |
1694 | } |
1695 | |
1696 | first_loop = false0; |
1697 | |
1698 | /* |
1699 | * Process this line. |
1700 | */ |
1701 | |
1702 | if (linelen == 0) { |
1703 | /* |
1704 | * This is a blank line, which means that |
1705 | * whatever follows it isn't part of this |
1706 | * request or reply. |
1707 | */ |
1708 | proto_tree_add_format_text(http_tree, tvb, offset, next_offset - offset); |
1709 | offset = next_offset; |
1710 | break; |
1711 | } |
1712 | |
1713 | /* |
1714 | * Not a blank line - either a request, a reply, or a header |
1715 | * line. |
1716 | */ |
1717 | saw_req_resp_or_header = true1; |
1718 | if (is_request_or_reply) { |
1719 | char *text = tvb_format_text(pinfo->pool, tvb, offset, next_offset - offset); |
1720 | |
1721 | req_tree = proto_tree_add_subtree(http_tree, tvb, |
1722 | offset, next_offset - offset, ett_http_request, &hdr_item, text); |
1723 | |
1724 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) { |
1725 | if (http_type == MEDIA_CONTAINER_HTTP_REQUEST) { |
1726 | curr = push_req(conv_data, pinfo); |
1727 | curr->request_method = wmem_strdup(wmem_file_scope(), stat_info->request_method); |
1728 | prv_data = curr->private_data; |
1729 | prv_data->req_fwd_flow = direction; |
1730 | } else if (http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
1731 | curr = push_res(conv_data, pinfo); |
1732 | prv_data = curr->private_data; |
1733 | prv_data->req_fwd_flow = -direction; |
1734 | } |
1735 | } |
1736 | if (reqresp_dissector) { |
1737 | reqresp_dissector(pinfo, tvb, req_tree, offset, line, |
1738 | lineend, conv_data, curr); |
1739 | } |
1740 | } else { |
1741 | /* |
1742 | * Header. |
1743 | */ |
1744 | bool_Bool good_header = process_header(tvb, offset, next_offset, line, linelen, |
1745 | colon_offset, pinfo, http_tree, headers, conv_data, |
1746 | http_type, header_value_map, streaming_chunk_mode); |
1747 | if (http_check_ascii_headers && !good_header) { |
1748 | /* |
1749 | * Line is not a good HTTP header. |
1750 | * Return -2 to mark as invalid HTTP; |
1751 | * this is distinct from returning -1 when |
1752 | * it may be HTTP but in need of reassembly. |
1753 | */ |
1754 | return -2; |
1755 | } |
1756 | } |
1757 | offset = next_offset; |
1758 | } |
1759 | if (stat_info->http_host && stat_info->request_uri) { |
1760 | char *uri; |
1761 | |
1762 | if ((g_ascii_strncasecmp(stat_info->request_uri, "http://", 7) == 0) || |
1763 | (g_ascii_strncasecmp(stat_info->request_uri, "https://", 8) == 0) || |
1764 | (g_ascii_strncasecmp(stat_info->request_method, "CONNECT", 7) == 0)) { |
1765 | uri = wmem_strdup(pinfo->pool, stat_info->request_uri); |
1766 | } |
1767 | else { |
1768 | uri = wmem_strdup_printf(pinfo->pool, "%s://%s%s", |
1769 | is_tls ? "https" : "http", |
1770 | g_strstrip(wmem_strdup(pinfo->pool, stat_info->http_host))g_strchomp (g_strchug (wmem_strdup(pinfo->pool, stat_info-> http_host))), stat_info->request_uri); |
1771 | } |
1772 | stat_info->full_uri = wmem_strdup(pinfo->pool, uri); |
1773 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && curr) { |
1774 | curr->full_uri = wmem_strdup(wmem_file_scope(), uri); |
1775 | } |
1776 | } |
1777 | else { |
1778 | /* If the request has a range, this is, or potentially is, asynchronous I/O thus |
1779 | * full_uri must be reinitialized because it is set to that of the last request. */ |
1780 | if (curr && curr->req_has_range) |
1781 | curr->full_uri = NULL((void*)0); |
1782 | } |
1783 | if (tree) { |
1784 | proto_item *pi; |
1785 | |
1786 | switch (http_type) { |
1787 | |
1788 | case MEDIA_CONTAINER_HTTP_NOTIFICATION: |
1789 | hidden_item = proto_tree_add_boolean(http_tree, |
1790 | hf_http_notification, tvb, 0, 0, 1); |
1791 | proto_item_set_hidden(hidden_item); |
1792 | break; |
1793 | |
1794 | case MEDIA_CONTAINER_HTTP_RESPONSE: |
1795 | hidden_item = proto_tree_add_boolean(http_tree, |
1796 | hf_http_response, tvb, 0, 0, 1); |
1797 | proto_item_set_hidden(hidden_item); |
1798 | |
1799 | match_trans_t *match_trans = NULL((void*)0); |
1800 | |
1801 | if (curr && curr->response_code == 206 && curr->resp_has_range) { |
1802 | /* The conv_data->matches_table is only used for GET requests with ranges and |
1803 | * response_codes of 206 (Partial Content). (Note: only GETs use ranges.) |
1804 | */ |
1805 | match_trans = (match_trans_t *)wmem_map_lookup(conv_data->matches_table, |
1806 | GUINT_TO_POINTER(pinfo->num)((gpointer) (gulong) (pinfo->num))); |
1807 | if (match_trans) { |
1808 | pi = proto_tree_add_uint(http_tree, hf_http_request_in, tvb, 0, 0, |
1809 | match_trans->req_frame); |
1810 | proto_item_set_generated(pi); |
1811 | |
1812 | pi = proto_tree_add_time(http_tree, hf_http_time, tvb, 0, 0, |
1813 | &match_trans->delta_time); |
1814 | proto_item_set_generated(pi); |
1815 | |
1816 | pi = proto_tree_add_string(http_tree, hf_http_request_uri, tvb, 0, 0, |
1817 | match_trans->request_uri); |
1818 | proto_item_set_generated(pi); |
1819 | { |
1820 | char *uri; |
1821 | uri = wmem_strdup_printf(pinfo->pool, "%s://%s%s", |
1822 | is_tls ? "https" : "http", |
1823 | g_strstrip(wmem_strdup(pinfo->pool, match_trans->http_host))g_strchomp (g_strchug (wmem_strdup(pinfo->pool, match_trans ->http_host))), match_trans->request_uri); |
1824 | |
1825 | pi = proto_tree_add_string(http_tree, hf_http_request_full_uri, tvb, 0, 0, |
1826 | uri); |
1827 | proto_item_set_url(pi); |
1828 | proto_item_set_generated(pi); |
1829 | } |
1830 | } |
1831 | } |
1832 | |
1833 | /* If responses don't have a range, the I/O is synchronous in which case a request is |
1834 | * matched with the following response. If a request or response is missing from the |
1835 | * capture file, correct matching resumes at the next request. */ |
1836 | if(!match_trans |
1837 | && curr |
1838 | && !curr->resp_has_range |
1839 | && curr->req_framenum) { |
1840 | pi = proto_tree_add_uint(http_tree, hf_http_request_in, tvb, 0, 0, curr->req_framenum); |
1841 | proto_item_set_generated(pi); |
1842 | |
1843 | if (! nstime_is_unset(&(curr->req_ts))) { |
1844 | nstime_t delta; |
1845 | |
1846 | nstime_delta(&delta, &pinfo->abs_ts, &(curr->req_ts)); |
1847 | pi = proto_tree_add_time(http_tree, hf_http_time, tvb, 0, 0, &delta); |
1848 | proto_item_set_generated(pi); |
1849 | } |
1850 | if (curr->request_uri) { |
1851 | pi = proto_tree_add_string(http_tree, hf_http_request_uri, tvb, 0, 0, |
1852 | curr->request_uri); |
1853 | proto_item_set_generated(pi); |
1854 | } |
1855 | if (curr->full_uri) { |
1856 | pi = proto_tree_add_string(http_tree, hf_http_request_full_uri, tvb, 0, 0, |
1857 | curr->full_uri); |
1858 | proto_item_set_url(pi); |
1859 | proto_item_set_generated(pi); |
1860 | } |
1861 | } |
1862 | break; |
1863 | case MEDIA_CONTAINER_HTTP_REQUEST: |
1864 | { |
1865 | int size = wmem_map_size(conv_data->matches_table); |
1866 | |
1867 | hidden_item = proto_tree_add_boolean(http_tree,hf_http_request, tvb, 0, 0, 1); |
1868 | proto_item_set_hidden(hidden_item); |
1869 | |
1870 | match_trans = NULL((void*)0); |
1871 | if (curr) { |
1872 | if (size > 0 && curr->req_has_range) { |
1873 | match_trans = (match_trans_t *)wmem_map_lookup(conv_data->matches_table, |
1874 | GUINT_TO_POINTER(pinfo->num)((gpointer) (gulong) (pinfo->num))); |
1875 | if (match_trans) { |
1876 | pi = proto_tree_add_uint(http_tree, hf_http_response_in, |
1877 | tvb, 0, 0, match_trans->resp_frame); |
1878 | proto_item_set_generated(pi); |
1879 | } |
1880 | } |
1881 | else { |
1882 | if(!match_trans |
1883 | && !curr->resp_has_range |
1884 | && curr->res_framenum) { |
1885 | pi = proto_tree_add_uint(http_tree, hf_http_response_in, tvb, 0, 0, curr->res_framenum); |
1886 | proto_item_set_generated(pi); |
1887 | |
1888 | } |
1889 | } |
1890 | |
1891 | if (curr->full_uri) { |
1892 | pi = proto_tree_add_string(http_tree, hf_http_request_full_uri, tvb, 0, 0, |
1893 | curr->full_uri); |
1894 | proto_item_set_url(pi); |
1895 | proto_item_set_generated(pi); |
1896 | } |
1897 | else if (stat_info->full_uri){ |
1898 | pi = proto_tree_add_string(http_tree, hf_http_request_full_uri, tvb, 0, 0, |
1899 | stat_info->full_uri); |
1900 | proto_item_set_url(pi); |
1901 | proto_item_set_generated(pi); |
1902 | } |
1903 | } |
1904 | } |
1905 | break; |
1906 | |
1907 | case MEDIA_CONTAINER_HTTP_OTHERS: |
1908 | default: |
1909 | break; |
1910 | } |
1911 | } |
1912 | |
1913 | /* Give the follow tap what we've currently dissected */ |
1914 | if(have_tap_listener(http_follow_tap)) { |
1915 | tap_queue_packet(http_follow_tap, pinfo, tvb_new_subset_length(tvb, orig_offset, offset-orig_offset)); |
1916 | } |
1917 | |
1918 | reported_datalen = tvb_reported_length_remaining(tvb, offset); |
1919 | datalen = tvb_captured_length_remaining(tvb, offset); |
1920 | |
1921 | /* |
1922 | * If a content length was supplied, the amount of data to be |
1923 | * processed as HTTP payload is the minimum of the content |
1924 | * length and the amount of data remaining in the frame. |
1925 | * |
1926 | * If a message is received with both a Transfer-Encoding |
1927 | * header field and a Content-Length header field, the latter |
1928 | * MUST be ignored. |
1929 | * |
1930 | * If no content length was supplied (or if a bad content length |
1931 | * was supplied), the amount of data to be processed is the amount |
1932 | * of data remaining in the frame. |
1933 | * |
1934 | * If there was no Content-Length entity header, we should |
1935 | * accumulate all data until the end of the connection. |
1936 | * That'd require that the TCP dissector call subdissectors |
1937 | * for all frames with FIN, even if they contain no data, |
1938 | * which would require subdissectors to deal intelligently |
1939 | * with empty segments. |
1940 | * |
1941 | * According to RFC 2616, however, 1xx responses, 204 responses, |
1942 | * and 304 responses MUST NOT include a message body; if no |
1943 | * content length is specified for them, we don't attempt to |
1944 | * dissect the body. |
1945 | * |
1946 | * XXX - it says the same about responses to HEAD requests; |
1947 | * unless there's a way to determine from the response |
1948 | * whether it's a response to a HEAD request, we have to |
1949 | * keep information about the request and associate that with |
1950 | * the response in order to handle that. |
1951 | */ |
1952 | if (headers->have_content_length && |
1953 | headers->transfer_encoding == HTTP_TE_NONE) { |
1954 | if (datalen > headers->content_length) |
1955 | datalen = (int)headers->content_length; |
1956 | |
1957 | /* |
1958 | * XXX - limit the reported length in the tvbuff we'll |
1959 | * hand to a subdissector to be no greater than the |
1960 | * content length. |
1961 | * |
1962 | * We really need both unreassembled and "how long it'd |
1963 | * be if it were reassembled" lengths for tvbuffs, so |
1964 | * that we throw the appropriate exceptions for |
1965 | * "not enough data captured" (running past the length), |
1966 | * "packet needed reassembly" (within the length but |
1967 | * running past the unreassembled length), and |
1968 | * "packet is malformed" (running past the reassembled |
1969 | * length). |
1970 | */ |
1971 | if (reported_datalen > headers->content_length) |
1972 | reported_datalen = (int)headers->content_length; |
1973 | } else { |
1974 | switch (http_type) { |
1975 | |
1976 | case MEDIA_CONTAINER_HTTP_REQUEST: |
1977 | /* |
1978 | * Requests have no content if there's no |
1979 | * Content-Length header and no Transfer-Encoding |
1980 | * header. |
1981 | */ |
1982 | if (headers->transfer_encoding == HTTP_TE_NONE) |
1983 | datalen = 0; |
1984 | else |
1985 | reported_datalen = -1; |
1986 | break; |
1987 | |
1988 | case MEDIA_CONTAINER_HTTP_RESPONSE: |
1989 | if ((stat_info->response_code/100) == 1 || |
1990 | stat_info->response_code == 204 || |
1991 | stat_info->response_code == 304) |
1992 | datalen = 0;/* no content! */ |
1993 | else { |
1994 | /* |
1995 | * XXX - responses to HEAD requests, |
1996 | * and possibly other responses, |
1997 | * "MUST NOT" include a |
1998 | * message-body. |
1999 | */ |
2000 | reported_datalen = -1; |
2001 | } |
2002 | break; |
2003 | |
2004 | default: |
2005 | /* |
2006 | * XXX - what about MEDIA_CONTAINER_HTTP_NOTIFICATION? |
2007 | */ |
2008 | reported_datalen = -1; |
2009 | break; |
2010 | } |
2011 | } |
2012 | |
2013 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && streaming_chunk_mode && streaming_reassembly_data == NULL((void*)0)) { |
2014 | DISSECTOR_ASSERT(!begin_with_chunk && handle && http_dechunk_body && http_desegment_body((void) ((!begin_with_chunk && handle && http_dechunk_body && http_desegment_body && headers && headers->content_type && header_value_map) ? (void )0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http.c", 2015, "!begin_with_chunk && handle && http_dechunk_body && http_desegment_body && headers && headers->content_type && header_value_map" )))) |
2015 | && headers && headers->content_type && header_value_map)((void) ((!begin_with_chunk && handle && http_dechunk_body && http_desegment_body && headers && headers->content_type && header_value_map) ? (void )0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http.c", 2015, "!begin_with_chunk && handle && http_dechunk_body && http_desegment_body && headers && headers->content_type && header_value_map" )))); |
2016 | |
2017 | content_info = wmem_new0(wmem_file_scope(), media_content_info_t)((media_content_info_t*)wmem_alloc0((wmem_file_scope()), sizeof (media_content_info_t))); |
2018 | content_info->media_str = headers->content_type_parameters; |
2019 | content_info->type = http_type; |
2020 | content_info->data = header_value_map; |
2021 | |
2022 | streaming_reassembly_data = wmem_new0(wmem_file_scope(), http_streaming_reassembly_data_t)((http_streaming_reassembly_data_t*)wmem_alloc0((wmem_file_scope ()), sizeof(http_streaming_reassembly_data_t))); |
2023 | streaming_reassembly_data->streaming_handle = handle; |
2024 | streaming_reassembly_data->streaming_reassembly_info = streaming_reassembly_info_new(); |
2025 | streaming_reassembly_data->content_info = content_info; |
2026 | streaming_reassembly_data->main_headers = headers; |
2027 | |
2028 | if (prv_data->req_fwd_flow == direction) { |
2029 | prv_data->req_streaming_reassembly_data = streaming_reassembly_data; |
2030 | } else { |
2031 | prv_data->res_streaming_reassembly_data = streaming_reassembly_data; |
2032 | } |
2033 | } |
2034 | |
2035 | if (content_info == NULL((void*)0)) { |
2036 | content_info = wmem_new0(pinfo->pool, media_content_info_t)((media_content_info_t*)wmem_alloc0((pinfo->pool), sizeof( media_content_info_t))); |
2037 | content_info->media_str = headers->content_type_parameters; |
2038 | content_info->type = http_type; |
2039 | content_info->data = header_value_map; |
2040 | } |
2041 | |
2042 | dissecting_body: |
2043 | |
2044 | if (datalen > 0) { |
| |
2045 | /* |
2046 | * There's stuff left over; process it. |
2047 | */ |
2048 | tvbuff_t *next_tvb; |
2049 | unsigned chunked_datalen = 0; |
2050 | int data_len; |
2051 | |
2052 | /* |
2053 | * Create a tvbuff for the payload. |
2054 | * |
2055 | * The amount of data to be processed that's |
2056 | * available in the tvbuff is "datalen", which |
2057 | * is the minimum of the amount of data left in |
2058 | * the tvbuff and any specified content length. |
2059 | * |
2060 | * The amount of data to be processed that's in |
2061 | * this frame, regardless of whether it was |
2062 | * captured or not, is "reported_datalen", |
2063 | * which, if no content length was specified, |
2064 | * is -1, i.e. "to the end of the frame. |
2065 | */ |
2066 | next_tvb = tvb_new_subset_length_caplen(tvb, offset, datalen, |
2067 | reported_datalen); |
2068 | |
2069 | /* |
2070 | * Handle *transfer* encodings. |
2071 | */ |
2072 | if (headers && headers->transfer_encoding_chunked) { |
2073 | if (!http_dechunk_body) { |
2074 | /* Chunking disabled, cannot dissect further. */ |
2075 | /* XXX: Should this be sent to the follow tap? */ |
2076 | call_data_dissector(next_tvb, pinfo, http_tree); |
2077 | goto body_dissected; |
2078 | } |
2079 | |
2080 | chunked_datalen = chunked_encoding_dissector( |
2081 | &next_tvb, pinfo, http_tree, 0); |
2082 | |
2083 | if (chunked_datalen == 0) { |
2084 | /* |
2085 | * The chunks weren't reassembled, |
2086 | * or there was a single zero |
2087 | * length chunk. |
2088 | */ |
2089 | goto body_dissected; |
2090 | } else { |
2091 | /* |
2092 | * Add a new data source for the |
2093 | * de-chunked data. |
2094 | */ |
2095 | #if 0 /* Handled in chunked_encoding_dissector() */ |
2096 | tvb_set_child_real_data_tvbuff(tvb, |
2097 | next_tvb); |
2098 | #endif |
2099 | add_new_data_source(pinfo, next_tvb, |
2100 | "De-chunked entity body"); |
2101 | /* chunked-body might be smaller than |
2102 | * datalen. */ |
2103 | datalen = chunked_datalen; |
2104 | } |
2105 | } |
2106 | /* Handle other transfer codings after de-chunking. */ |
2107 | switch (headers->transfer_encoding) { |
| 52 | | Access to field 'transfer_encoding' results in a dereference of a null pointer (loaded from variable 'headers') |
|
2108 | case HTTP_TE_COMPRESS: |
2109 | case HTTP_TE_DEFLATE: |
2110 | case HTTP_TE_GZIP: |
2111 | /* |
2112 | * We currently can't handle, for example, "gzip", |
2113 | * "compress", or "deflate" as *transfer* encodings; |
2114 | * just handle them as data for now. |
2115 | * XXX: Should this be sent to the follow tap? |
2116 | */ |
2117 | call_data_dissector(next_tvb, pinfo, http_tree); |
2118 | goto body_dissected; |
2119 | default: |
2120 | /* Nothing to do for "identity" or when header is |
2121 | * missing or invalid. */ |
2122 | break; |
2123 | } |
2124 | /* |
2125 | * At this point, any chunked *transfer* coding has been removed |
2126 | * (the entity body has been dechunked) so it can be presented |
2127 | * for the following operation (*content* encoding), or it has |
2128 | * been handed off to the data dissector. |
2129 | * |
2130 | * Handle *content* encodings other than "identity" (which |
2131 | * shouldn't appear in a Content-Encoding header, but |
2132 | * we handle it in any case). |
2133 | */ |
2134 | if (headers->content_encoding != NULL((void*)0) && |
2135 | g_ascii_strcasecmp(headers->content_encoding, "identity") != 0) { |
2136 | /* |
2137 | * We currently don't handle, for example, "compress"; |
2138 | * just handle them as data for now. |
2139 | * |
2140 | * After July 7, 2004 the LZW patent expired, so |
2141 | * support could be added. However, I don't think |
2142 | * that anybody ever really implemented "compress", |
2143 | * due to the aforementioned patent. |
2144 | */ |
2145 | tvbuff_t *uncomp_tvb = NULL((void*)0); |
2146 | proto_item *e_ti = NULL((void*)0); |
2147 | proto_tree *e_tree = NULL((void*)0); |
2148 | |
2149 | #if defined(HAVE_ZLIB1) || defined(HAVE_ZLIBNG) |
2150 | if (http_decompress_body && |
2151 | (g_ascii_strcasecmp(headers->content_encoding, "gzip") == 0 || |
2152 | g_ascii_strcasecmp(headers->content_encoding, "deflate") == 0 || |
2153 | g_ascii_strcasecmp(headers->content_encoding, "x-gzip") == 0 || |
2154 | g_ascii_strcasecmp(headers->content_encoding, "x-deflate") == 0)) |
2155 | { |
2156 | uncomp_tvb = tvb_child_uncompress_zlib(tvb, next_tvb, 0, |
2157 | tvb_captured_length(next_tvb)); |
2158 | } |
2159 | #endif |
2160 | |
2161 | #ifdef HAVE_BROTLI1 |
2162 | if (http_decompress_body && |
2163 | g_ascii_strcasecmp(headers->content_encoding, "br") == 0) |
2164 | { |
2165 | uncomp_tvb = tvb_child_uncompress_brotli(tvb, next_tvb, 0, |
2166 | tvb_captured_length(next_tvb)); |
2167 | } |
2168 | #endif |
2169 | |
2170 | #ifdef HAVE_SNAPPY1 |
2171 | if (http_decompress_body && |
2172 | g_ascii_strcasecmp(headers->content_encoding, "snappy") == 0) |
2173 | { |
2174 | uncomp_tvb = tvb_child_uncompress_snappy(tvb, next_tvb, 0, |
2175 | tvb_captured_length(next_tvb)); |
2176 | } |
2177 | #endif |
2178 | |
2179 | /* |
2180 | * Add the encoded entity to the protocol tree |
2181 | */ |
2182 | e_tree = proto_tree_add_subtree_format(http_tree, next_tvb, |
2183 | 0, tvb_captured_length(next_tvb), ett_http_encoded_entity, &e_ti, |
2184 | "Content-encoded entity body (%s): %u bytes", |
2185 | headers->content_encoding, |
2186 | tvb_captured_length(next_tvb)); |
2187 | |
2188 | if (uncomp_tvb != NULL((void*)0)) { |
2189 | /* |
2190 | * Decompression worked |
2191 | */ |
2192 | |
2193 | /* XXX - Don't free this, since it's possible |
2194 | * that the data was only partially |
2195 | * decompressed, such as when desegmentation |
2196 | * isn't enabled. |
2197 | * |
2198 | tvb_free(next_tvb); |
2199 | */ |
2200 | proto_item_append_text(e_ti, " -> %u bytes", tvb_captured_length(uncomp_tvb)); |
2201 | next_tvb = uncomp_tvb; |
2202 | add_new_data_source(pinfo, next_tvb, |
2203 | "Uncompressed entity body"); |
2204 | } else { |
2205 | #if defined(HAVE_ZLIB1) || defined(HAVE_ZLIBNG) || defined(HAVE_BROTLI1) |
2206 | if (http_decompress_body) { |
2207 | expert_add_info(pinfo, e_ti, &ei_http_decompression_failed); |
2208 | } |
2209 | else { |
2210 | expert_add_info(pinfo, e_ti, &ei_http_decompression_disabled); |
2211 | } |
2212 | #endif |
2213 | /* XXX: Should this be sent to the follow tap? */ |
2214 | call_data_dissector(next_tvb, pinfo, e_tree); |
2215 | |
2216 | goto body_dissected; |
2217 | } |
2218 | } |
2219 | /* |
2220 | * Note that a new data source is added for the entity body |
2221 | * only if it was content-encoded and/or transfer-encoded. |
2222 | */ |
2223 | |
2224 | /* Save values for the Export Object GUI feature if we have |
2225 | * an active listener to process it (which happens when |
2226 | * the export object window is open). */ |
2227 | /* XXX: Do we really want to send it to Export Object if we didn't |
2228 | * get the headers, so that this is just a fragment of Continuation |
2229 | * Data and not a complete object? |
2230 | */ |
2231 | if(have_tap_listener(http_eo_tap)) { |
2232 | eo_info = wmem_new0(pinfo->pool, http_eo_t)((http_eo_t*)wmem_alloc0((pinfo->pool), sizeof(http_eo_t)) ); |
2233 | |
2234 | if (curr) { |
2235 | eo_info->hostname = curr->http_host; |
2236 | eo_info->filename = curr->request_uri; |
2237 | } |
2238 | eo_info->content_type = headers->content_type; |
2239 | eo_info->payload = next_tvb; |
2240 | |
2241 | tap_queue_packet(http_eo_tap, pinfo, eo_info); |
2242 | } |
2243 | |
2244 | /* Send it to Follow HTTP Stream and mark as file data */ |
2245 | if(have_tap_listener(http_follow_tap)) { |
2246 | tap_queue_packet(http_follow_tap, pinfo, next_tvb); |
2247 | } |
2248 | data_len = tvb_captured_length(next_tvb); |
2249 | proto_tree_add_bytes_format_value(http_tree, hf_http_file_data, |
2250 | next_tvb, 0, data_len, NULL((void*)0), "%u byte%s", data_len, plurality(data_len, "", "s")((data_len) == 1 ? ("") : ("s"))); |
2251 | |
2252 | if (tvb_captured_length(next_tvb) == 0) |
2253 | goto body_dissected; |
2254 | |
2255 | /* |
2256 | * Do subdissector checks. |
2257 | * |
2258 | * First, if we have a Content-Type value, check whether |
2259 | * there's a subdissector for that media type. |
2260 | */ |
2261 | if (headers->content_type != NULL((void*)0) && handle == NULL((void*)0)) { |
2262 | /* |
2263 | * We didn't find any subdissector that |
2264 | * registered for the port, and we have a |
2265 | * Content-Type value. Is there any subdissector |
2266 | * for that content type? |
2267 | */ |
2268 | |
2269 | /* |
2270 | * Calling the string handle for the media type |
2271 | * dissector table will set pinfo->match_string |
2272 | * to headers->content_type for us. |
2273 | */ |
2274 | pinfo->match_string = headers->content_type; |
2275 | handle = dissector_get_string_handle( |
2276 | media_type_subdissector_table, |
2277 | headers->content_type); |
2278 | if (handle == NULL((void*)0) && |
2279 | strncmp(headers->content_type, "multipart/", sizeof("multipart/")-1) == 0) { |
2280 | /* Try to decode the unknown multipart subtype anyway */ |
2281 | handle = dissector_get_string_handle( |
2282 | media_type_subdissector_table, |
2283 | "multipart/"); |
2284 | } |
2285 | } |
2286 | |
2287 | /* |
2288 | * Now, if we didn't find such a subdissector, check |
2289 | * whether some subdissector asked that they be called |
2290 | * if HTTP traffic was on some particular port. This |
2291 | * handles protocols that use HTTP syntax but don't have |
2292 | * a media type and instead use a specified port. |
2293 | */ |
2294 | if (handle == NULL((void*)0)) { |
2295 | /* If the HTTP dissector was called heuristically |
2296 | * (or the HTTP dissector was called from the TLS |
2297 | * dissector, which was called heuristically), then |
2298 | * match_uint doesn't get set (or is likely set to |
2299 | * 6 for IP_PROTO_TCP.) Some protocols (e.g., IPP) |
2300 | * use the same specified port for both HTTP and |
2301 | * HTTP over TLS, and one will be a heuristic match. |
2302 | * In those cases, look at the src or dest port. |
2303 | */ |
2304 | if (pinfo->match_uint == pinfo->srcport || pinfo->match_uint == pinfo->destport) { |
2305 | handle = dissector_get_uint_handle(port_subdissector_table, |
2306 | pinfo->match_uint); |
2307 | } else if (http_type == MEDIA_CONTAINER_HTTP_REQUEST) { |
2308 | handle = dissector_get_uint_handle(port_subdissector_table, |
2309 | pinfo->destport); |
2310 | } else if (http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
2311 | handle = dissector_get_uint_handle(port_subdissector_table, |
2312 | pinfo->srcport); |
2313 | } |
2314 | } |
2315 | |
2316 | if (handle != NULL((void*)0)) { |
2317 | /* |
2318 | * We have a subdissector - call it. |
2319 | */ |
2320 | if (streaming_chunk_mode) { |
2321 | pinfo->match_string = headers->content_type; |
2322 | /* reassemble and call subdissector */ |
2323 | dissected = (bool_Bool)reassemble_streaming_data_and_call_subdissector(next_tvb, pinfo, 0, |
2324 | tvb_reported_length_remaining(next_tvb, 0), http_tree, proto_tree_get_parent_tree(tree), |
2325 | http_streaming_reassembly_table, streaming_reassembly_data->streaming_reassembly_info, |
2326 | get_http_chunk_frame_numget_virtual_frame_num64(tvb, pinfo, offset), handle, |
2327 | proto_tree_get_parent_tree(tree), content_info, |
2328 | "HTTP", &http_body_fragment_items, hf_http_body_segment); |
2329 | } else { |
2330 | dissected = (bool_Bool)call_dissector_only(handle, next_tvb, pinfo, tree, content_info); |
2331 | } |
2332 | if (!dissected) |
2333 | expert_add_info(pinfo, http_tree, &ei_http_subdissector_failed); |
2334 | } |
2335 | |
2336 | if (!dissected) { |
2337 | /* |
2338 | * We don't have a subdissector or we have one and it did not |
2339 | * dissect the payload - try the heuristic subdissectors. |
2340 | */ |
2341 | dissected = dissector_try_heuristic(heur_subdissector_list, |
2342 | next_tvb, pinfo, tree, &hdtbl_entry, content_info); |
2343 | } |
2344 | |
2345 | if (dissected) { |
2346 | /* |
2347 | * The subdissector dissected the body. |
2348 | * Fix up the top-level item so that it doesn't |
2349 | * include the stuff for that protocol. |
2350 | */ |
2351 | if (ti != NULL((void*)0)) |
2352 | proto_item_set_len(ti, offset); |
2353 | } else { |
2354 | if (headers->content_type != NULL((void*)0)) { |
2355 | /* |
2356 | * Calling the default media handle if there is a content-type that |
2357 | * wasn't handled above. |
2358 | */ |
2359 | call_dissector_with_data(media_handle, next_tvb, pinfo, tree, content_info); |
2360 | } else { |
2361 | /* Call the default data dissector */ |
2362 | call_data_dissector(next_tvb, pinfo, http_tree); |
2363 | } |
2364 | } |
2365 | |
2366 | body_dissected: |
2367 | /* |
2368 | * We've processed "datalen" bytes worth of data |
2369 | * (which may be no data at all); advance the |
2370 | * offset past whatever data we've processed. |
2371 | */ |
2372 | offset += datalen; |
2373 | } |
2374 | |
2375 | /* Detect protocol changes after receiving full response headers. */ |
2376 | if (http_type == MEDIA_CONTAINER_HTTP_RESPONSE && curr && pinfo->desegment_offset <= 0 && pinfo->desegment_len <= 0) { |
2377 | dissector_handle_t next_handle = NULL((void*)0); |
2378 | bool_Bool server_acked = false0; |
2379 | |
2380 | /* |
2381 | * SSTP uses a special request method (instead of the Upgrade |
2382 | * header) and expects a 200 response to set up the session. |
2383 | */ |
2384 | if (g_strcmp0(curr->request_method, "SSTP_DUPLEX_POST") == 0 && curr->response_code == 200) { |
2385 | next_handle = sstp_handle; |
2386 | server_acked = true1; |
2387 | } |
2388 | |
2389 | /* |
2390 | * An HTTP/1.1 upgrade only proceeds if the server responds |
2391 | * with 101 Switching Protocols. See RFC 7230 Section 6.7. |
2392 | */ |
2393 | if (headers->upgrade && curr->response_code == 101) { |
2394 | next_handle = dissector_get_string_handle(upgrade_subdissector_table, headers->upgrade); |
2395 | if (!next_handle) { |
2396 | char *slash_pos = strchr(headers->upgrade, '/'); |
2397 | if (slash_pos) { |
2398 | /* Try again without version suffix. */ |
2399 | next_handle = dissector_get_string_handle(upgrade_subdissector_table, |
2400 | wmem_strndup(pinfo->pool, headers->upgrade, slash_pos - headers->upgrade)); |
2401 | } |
2402 | } |
2403 | server_acked = true1; |
2404 | } |
2405 | |
2406 | if (server_acked && !PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) { |
2407 | conv_data->startframe = pinfo->num; |
2408 | conv_data->startoffset = offset; |
2409 | conv_data->next_handle = next_handle; |
2410 | copy_address_wmem(wmem_file_scope(), &conv_data->server_addr, &pinfo->src); |
2411 | conv_data->server_port = pinfo->srcport; |
2412 | } |
2413 | } |
2414 | |
2415 | if (stat_info) |
2416 | tap_queue_packet(http_tap, pinfo, stat_info); |
2417 | |
2418 | return offset - orig_offset; |
2419 | } |
2420 | |
2421 | /* This can be used to dissect an HTTP request until such time |
2422 | * that a more complete dissector is written for that HTTP request. |
2423 | * This simple dissector only puts the request method, URI, and |
2424 | * protocol version into a sub-tree. |
2425 | */ |
2426 | static void |
2427 | basic_request_dissector(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree, |
2428 | int offset, const unsigned char *line, const unsigned char *lineend, |
2429 | http_conv_t *conv_data _U___attribute__((unused)), http_req_res_t *curr) |
2430 | { |
2431 | const unsigned char *next_token; |
2432 | const char *request_uri; |
2433 | int tokenlen; |
2434 | proto_item* ti; |
2435 | http_info_value_t *stat_info = p_get_proto_data(pinfo->pool, pinfo, proto_http, HTTP_PROTO_DATA_INFO1); |
2436 | |
2437 | /* The first token is the method. */ |
2438 | tokenlen = get_token_len(line, lineend, &next_token); |
2439 | if (tokenlen == 0) |
2440 | return; |
2441 | proto_tree_add_item(tree, hf_http_request_method, tvb, offset, tokenlen, |
2442 | ENC_ASCII0x00000000); |
2443 | if ((next_token - line) > 2 && next_token[-1] == ' ' && next_token[-2] == ' ') { |
2444 | /* Two spaces in a now indicates empty URI, so roll back one here */ |
2445 | next_token--; |
2446 | } |
2447 | offset += (int) (next_token - line); |
2448 | line = next_token; |
2449 | |
2450 | /* The next token is the URI. */ |
2451 | tokenlen = get_token_len(line, lineend, &next_token); |
2452 | |
2453 | /* Save the request URI for various later uses */ |
2454 | request_uri = tvb_get_string_enc(pinfo->pool, tvb, offset, tokenlen, ENC_ASCII0x00000000); |
2455 | |
2456 | if (request_uri == NULL((void*)0) && curr) |
2457 | request_uri = curr->request_uri; |
2458 | |
2459 | stat_info->request_uri = wmem_strdup(pinfo->pool, request_uri); |
2460 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && curr) { |
2461 | curr->request_uri = wmem_strdup(wmem_file_scope(), request_uri); |
2462 | } |
2463 | ti = proto_tree_add_string(tree, hf_http_request_uri, tvb, offset, tokenlen, request_uri); |
2464 | http_add_path_components_to_tree(tvb, pinfo, ti, offset, tokenlen); |
2465 | offset += (int) (next_token - line); |
2466 | line = next_token; |
2467 | |
2468 | /* Everything to the end of the line is the version. */ |
2469 | tokenlen = (int) (lineend - line); |
2470 | proto_tree_add_item(tree, hf_http_request_version, tvb, offset, tokenlen, |
2471 | ENC_ASCII0x00000000); |
2472 | } |
2473 | |
2474 | static int |
2475 | parse_http_status_code(const unsigned char *line, const unsigned char *lineend) |
2476 | { |
2477 | const unsigned char *next_token; |
2478 | int tokenlen; |
2479 | char response_code_chars[4]; |
2480 | int32_t status_code = 0; |
2481 | |
2482 | /* |
2483 | * The first token is the HTTP Version. |
2484 | */ |
2485 | tokenlen = get_token_len(line, lineend, &next_token); |
2486 | if (tokenlen == 0) |
2487 | return 0; |
2488 | line = next_token; |
2489 | |
2490 | /* |
2491 | * The second token is the Status Code. |
2492 | */ |
2493 | tokenlen = get_token_len(line, lineend, &next_token); |
2494 | if (tokenlen != 3) |
2495 | return 0; |
2496 | |
2497 | memcpy(response_code_chars, line, 3); |
2498 | response_code_chars[3] = '\0'; |
2499 | if (!ws_strtoi32(response_code_chars, NULL((void*)0), &status_code)) |
2500 | return 0; |
2501 | |
2502 | return status_code; |
2503 | } |
2504 | |
2505 | static void |
2506 | basic_response_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___attribute__((unused)), http_req_res_t *curr) |
2509 | { |
2510 | const unsigned char *next_token; |
2511 | int tokenlen; |
2512 | char response_code_chars[4]; |
2513 | proto_item *r_ti; |
2514 | http_info_value_t *stat_info = p_get_proto_data(pinfo->pool, pinfo, proto_http, HTTP_PROTO_DATA_INFO1); |
2515 | |
2516 | /* |
2517 | * The first token is the HTTP Version. |
2518 | */ |
2519 | tokenlen = get_token_len(line, lineend, &next_token); |
2520 | if (tokenlen == 0) |
2521 | return; |
2522 | proto_tree_add_item(tree, hf_http_response_version, tvb, offset, tokenlen, |
2523 | ENC_ASCII0x00000000); |
2524 | /* Advance to the start of the next token. */ |
2525 | offset += (int) (next_token - line); |
2526 | line = next_token; |
2527 | |
2528 | /* |
2529 | * The second token is the Status Code. |
2530 | */ |
2531 | tokenlen = get_token_len(line, lineend, &next_token); |
2532 | if (tokenlen < 3) |
2533 | return; |
2534 | |
2535 | /* The Status Code characters must be copied into a null-terminated |
2536 | * buffer for strtoul() to parse them into an unsigned integer value. |
2537 | */ |
2538 | memcpy(response_code_chars, line, 3); |
2539 | response_code_chars[3] = '\0'; |
2540 | |
2541 | stat_info->response_code = |
2542 | (unsigned)strtoul(response_code_chars, NULL((void*)0), 10); |
2543 | if (curr) { |
2544 | curr->response_code = stat_info->response_code; |
2545 | } |
2546 | |
2547 | proto_tree_add_uint(tree, hf_http_response_code, tvb, offset, 3, |
2548 | stat_info->response_code); |
2549 | |
2550 | r_ti = proto_tree_add_string(tree, hf_http_response_code_desc, |
2551 | tvb, offset, 3, val_to_str(stat_info->response_code, |
2552 | vals_http_status_code, "Unknown (%d)")); |
2553 | |
2554 | proto_item_set_generated(r_ti); |
2555 | |
2556 | /* Advance to the start of the next token. */ |
2557 | offset += (int) (next_token - line); |
2558 | line = next_token; |
2559 | |
2560 | /* |
2561 | * The remaining tokens in the line comprise the Reason Phrase. |
2562 | */ |
2563 | tokenlen = (int) (lineend - line); |
2564 | if (tokenlen >= 1) { |
2565 | proto_tree_add_item(tree, hf_http_response_phrase, tvb, offset, |
2566 | tokenlen, ENC_ASCII0x00000000); |
2567 | } |
2568 | } |
2569 | |
2570 | /* |
2571 | * Dissect the http data chunks and add them to the tree. |
2572 | */ |
2573 | static unsigned |
2574 | chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo, |
2575 | proto_tree *tree, int offset) |
2576 | { |
2577 | tvbuff_t*tvb; |
2578 | uint32_t datalen; |
2579 | uint32_t orig_datalen; |
2580 | int chunked_data_size; |
2581 | proto_tree*subtree; |
2582 | proto_item*pi_chunked = NULL((void*)0); |
2583 | uint8_t*raw_data; |
2584 | int raw_len; |
2585 | int chunk_counter = 0; |
2586 | int last_chunk_id = -1; |
2587 | |
2588 | if ((tvb_ptr == NULL((void*)0)) || (*tvb_ptr == NULL((void*)0))) { |
2589 | return 0; |
2590 | } |
2591 | |
2592 | tvb = *tvb_ptr; |
2593 | |
2594 | datalen = tvb_reported_length_remaining(tvb, offset); |
2595 | |
2596 | subtree = proto_tree_add_subtree(tree, tvb, offset, datalen, |
2597 | ett_http_chunked_response, &pi_chunked, |
2598 | "HTTP chunked response"); |
2599 | |
2600 | /* Dechunk the "chunked response" to a new memory buffer */ |
2601 | /* XXX: Composite tvbuffers do work now, so we should probably |
2602 | * use that to avoid the memcpys unless necessary. |
2603 | */ |
2604 | orig_datalen = datalen; |
2605 | raw_data = (uint8_t *)wmem_alloc(pinfo->pool, datalen); |
2606 | raw_len = 0; |
2607 | chunked_data_size = 0; |
2608 | |
2609 | while (datalen > 0) { |
2610 | uint32_t chunk_size; |
2611 | int chunk_offset; |
2612 | uint8_t *chunk_string; |
2613 | int linelen; |
2614 | char *c; |
2615 | |
2616 | linelen = tvb_find_line_end(tvb, offset, -1, &chunk_offset, true1); |
2617 | |
2618 | if (linelen <= 0) { |
2619 | /* Can't get the chunk size line */ |
2620 | break; |
2621 | } |
2622 | |
2623 | chunk_string = tvb_get_string_enc(pinfo->pool, tvb, offset, linelen, ENC_ASCII0x00000000); |
2624 | |
2625 | if (chunk_string == NULL((void*)0)) { |
2626 | /* Can't get the chunk size line */ |
2627 | break; |
2628 | } |
2629 | |
2630 | c = (char*)chunk_string; |
2631 | |
2632 | /* |
2633 | * We don't care about the extensions. |
2634 | */ |
2635 | if ((c = strchr(c, ';'))) { |
2636 | *c = '\0'; |
2637 | } |
2638 | |
2639 | chunk_size = (uint32_t)strtol((char*)chunk_string, NULL((void*)0), 16); |
2640 | |
2641 | if (chunk_size > datalen) { |
2642 | /* |
2643 | * The chunk size is more than what's in the tvbuff, |
2644 | * so either the user hasn't enabled decoding, or all |
2645 | * of the segments weren't captured. |
2646 | */ |
2647 | chunk_size = datalen; |
2648 | } |
2649 | |
2650 | chunked_data_size += chunk_size; |
2651 | |
2652 | DISSECTOR_ASSERT((raw_len+chunk_size) <= orig_datalen)((void) (((raw_len+chunk_size) <= orig_datalen) ? (void)0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http.c", 2652, "(raw_len+chunk_size) <= orig_datalen" )))); |
2653 | tvb_memcpy(tvb, (uint8_t *)(raw_data + raw_len), chunk_offset, chunk_size); |
2654 | raw_len += chunk_size; |
2655 | |
2656 | ++chunk_counter; |
2657 | |
2658 | if (subtree) { |
2659 | proto_tree *chunk_subtree; |
2660 | proto_item *chunk_size_item; |
2661 | |
2662 | if(chunk_size == 0) { |
2663 | chunk_subtree = proto_tree_add_subtree(subtree, tvb, |
2664 | offset, |
2665 | chunk_offset - offset + chunk_size + 2, |
2666 | ett_http_chunk_data, NULL((void*)0), |
2667 | "End of chunked encoding"); |
2668 | last_chunk_id = chunk_counter - 1; |
2669 | } else { |
2670 | chunk_subtree = proto_tree_add_subtree_format(subtree, tvb, |
2671 | offset, |
2672 | chunk_offset - offset + chunk_size + 2, |
2673 | ett_http_chunk_data, NULL((void*)0), |
2674 | "Data chunk (%u octets)", chunk_size); |
2675 | } |
2676 | |
2677 | chunk_size_item = proto_tree_add_uint(chunk_subtree, hf_http_chunk_size, tvb, offset, |
2678 | 1, chunk_size); |
2679 | proto_item_set_len(chunk_size_item, chunk_offset - offset); |
2680 | |
2681 | /* last-chunk does not have chunk-data CRLF. */ |
2682 | if (chunk_size > 0) { |
2683 | /* |
2684 | * Adding the chunk as FT_BYTES means that, in |
2685 | * TShark, you get the entire chunk dumped |
2686 | * out in hex, in addition to whatever |
2687 | * dissection is done on the reassembled data. |
2688 | */ |
2689 | proto_tree_add_item(chunk_subtree, hf_http_chunk_data, tvb, chunk_offset, chunk_size, ENC_NA0x00000000); |
2690 | proto_tree_add_item(chunk_subtree, hf_http_chunk_boundary, tvb, |
2691 | chunk_offset + chunk_size, 2, ENC_NA0x00000000); |
2692 | } |
2693 | } |
2694 | |
2695 | offset = chunk_offset + chunk_size; /* beginning of next chunk */ |
2696 | if (chunk_size > 0) offset += 2; /* CRLF of chunk */ |
2697 | datalen = tvb_reported_length_remaining(tvb, offset); |
2698 | |
2699 | /* This is the last chunk */ |
2700 | if (chunk_size == 0) { |
2701 | /* Check for: trailer-part CRLF. |
2702 | * trailer-part = *( header-field CRLF ) */ |
2703 | int trailer_offset = offset, trailer_len; |
2704 | int header_field_len; |
2705 | /* Skip all header-fields. */ |
2706 | do { |
2707 | trailer_len = trailer_offset - offset; |
2708 | header_field_len = tvb_find_line_end(tvb, |
2709 | trailer_offset, |
2710 | datalen - trailer_len, |
2711 | &trailer_offset, true1); |
2712 | } while (header_field_len > 0); |
2713 | if (trailer_len > 0) { |
2714 | proto_tree_add_item(subtree, |
2715 | hf_http_chunked_trailer_part, |
2716 | tvb, offset, trailer_len, ENC_ASCII0x00000000); |
2717 | offset += trailer_len; |
2718 | datalen -= trailer_len; |
2719 | } |
2720 | |
2721 | /* last CRLF of chunked-body is found. */ |
2722 | if (header_field_len == 0) { |
2723 | proto_tree_add_format_text(subtree, tvb, offset, |
2724 | trailer_offset - offset); |
2725 | datalen -= trailer_offset - offset; |
2726 | } |
2727 | break; |
2728 | } |
2729 | } |
2730 | |
2731 | /* datalen is the remaining bytes that are available for consumption. If |
2732 | * smaller than orig_datalen, then bytes were consumed. */ |
2733 | if (datalen < orig_datalen) { |
2734 | tvbuff_t *new_tvb; |
2735 | proto_item_set_len(pi_chunked, orig_datalen - datalen); |
2736 | new_tvb = tvb_new_child_real_data(tvb, raw_data, chunked_data_size, chunked_data_size); |
2737 | *tvb_ptr = new_tvb; |
2738 | } |
2739 | |
2740 | if (chunk_counter > 0) { |
2741 | proto_item* ti_http = proto_tree_get_parent(tree); |
2742 | proto_item_append_text(ti_http, ", has %d chunk%s%s", |
2743 | chunk_counter, plurality(chunk_counter, "", "s")((chunk_counter) == 1 ? ("") : ("s")), |
2744 | (last_chunk_id < 0 ? "" : " (including last chunk)")); |
2745 | |
2746 | if (last_chunk_id == 0) { |
2747 | /* only append text to column while starting with last chunk */ |
2748 | col_append_sep_str(pinfo->cinfo, COL_INFO, " ", "[Last Chunk]"); |
2749 | } |
2750 | } |
2751 | |
2752 | /* Size of chunked-body or 0 if none was found. */ |
2753 | return orig_datalen - datalen; |
2754 | } |
2755 | |
2756 | static bool_Bool |
2757 | conversation_dissector_is_http(conversation_t *conv, uint32_t frame_num) |
2758 | { |
2759 | dissector_handle_t conv_handle; |
2760 | |
2761 | if (conv == NULL((void*)0)) |
2762 | return false0; |
2763 | conv_handle = conversation_get_dissector(conv, frame_num); |
2764 | return conv_handle == http_handle || |
2765 | conv_handle == http_tcp_handle || |
2766 | conv_handle == http_sctp_handle; |
2767 | } |
2768 | |
2769 | /* Call a subdissector to handle HTTP CONNECT's traffic */ |
2770 | static void |
2771 | http_payload_subdissector(tvbuff_t *tvb, proto_tree *tree, |
2772 | packet_info *pinfo, http_conv_t *conv_data, void* data) |
2773 | { |
2774 | uint32_t *ptr = NULL((void*)0); |
2775 | uint32_t uri_port, saved_port, srcport, destport; |
2776 | char **strings; /* An array for splitting the request URI into hostname and port */ |
2777 | proto_item *item; |
2778 | proto_tree *proxy_tree; |
2779 | conversation_t *conv; |
2780 | bool_Bool from_server = pinfo->srcport == conv_data->server_port && |
2781 | addresses_equal(&conv_data->server_addr, &pinfo->src); |
2782 | |
2783 | /* Grab the destination port number from the request URI to find the right subdissector */ |
2784 | strings = wmem_strsplit(pinfo->pool, conv_data->req_res_tail->request_uri, ":", 2); |
2785 | |
2786 | if(strings[0] != NULL((void*)0) && strings[1] != NULL((void*)0)) { |
2787 | /* |
2788 | * The string was successfully split in two |
2789 | * Create a proxy-connect subtree |
2790 | */ |
2791 | if(tree) { |
2792 | item = proto_tree_add_item(tree, proto_http, tvb, 0, -1, ENC_NA0x00000000); |
2793 | proxy_tree = proto_item_add_subtree(item, ett_http); |
2794 | |
2795 | item = proto_tree_add_string(proxy_tree, hf_http_proxy_connect_host, |
2796 | tvb, 0, 0, strings[0]); |
2797 | proto_item_set_generated(item); |
2798 | |
2799 | item = proto_tree_add_uint(proxy_tree, hf_http_proxy_connect_port, |
2800 | tvb, 0, 0, (uint32_t)strtol(strings[1], NULL((void*)0), 10) ); |
2801 | proto_item_set_generated(item); |
2802 | } |
2803 | |
2804 | uri_port = (int)strtol(strings[1], NULL((void*)0), 10); /* Convert string to a base-10 integer */ |
2805 | |
2806 | if (!from_server) { |
2807 | srcport = pinfo->srcport; |
2808 | destport = uri_port; |
2809 | } else { |
2810 | srcport = uri_port; |
2811 | destport = pinfo->destport; |
2812 | } |
2813 | |
2814 | conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, CONVERSATION_TCP, srcport, destport, 0); |
2815 | |
2816 | /* We may get stuck in a recursion loop if we let process_tcp_payload() call us. |
2817 | * So, if the port in the URI is one we're registered for or we have set up a |
2818 | * conversation (e.g., one we detected heuristically or via Decode-As) call the data |
2819 | * dissector directly. |
2820 | */ |
2821 | if (value_is_in_range(http_tcp_range, uri_port) || |
2822 | conversation_dissector_is_http(conv, pinfo->num)) { |
2823 | call_data_dissector(tvb, pinfo, tree); |
2824 | } else { |
2825 | /* set pinfo->{src/dst port} and call the TCP sub-dissector lookup */ |
2826 | if (!from_server) |
2827 | ptr = &pinfo->destport; |
2828 | else |
2829 | ptr = &pinfo->srcport; |
2830 | |
2831 | /* Increase pinfo->can_desegment because we are traversing |
2832 | * http and want to preserve desegmentation functionality for |
2833 | * the proxied protocol |
2834 | */ |
2835 | if( pinfo->can_desegment>0 ) |
2836 | pinfo->can_desegment++; |
2837 | |
2838 | saved_port = *ptr; |
2839 | *ptr = uri_port; |
2840 | decode_tcp_ports(tvb, 0, pinfo, tree, |
2841 | pinfo->srcport, pinfo->destport, NULL((void*)0), |
2842 | (struct tcpinfo *)data); |
2843 | *ptr = saved_port; |
2844 | } |
2845 | } |
2846 | } |
2847 | |
2848 | |
2849 | |
2850 | /* |
2851 | * XXX - this won't handle HTTP 0.9 replies, but they're all data |
2852 | * anyway. |
2853 | */ |
2854 | static int |
2855 | is_http_request_or_reply(packet_info *pinfo, const char *data, int linelen, media_container_type_t *type, |
2856 | ReqRespDissector *reqresp_dissector, |
2857 | http_conv_t *conv_data _U___attribute__((unused))) |
2858 | { |
2859 | http_info_value_t *stat_info = p_get_proto_data(pinfo->pool, pinfo, proto_http, HTTP_PROTO_DATA_INFO1); |
2860 | int isHttpRequestOrReply = false0; |
2861 | |
2862 | /* |
2863 | * From RFC 2774 - An HTTP Extension Framework |
2864 | * |
2865 | * Support the command prefix that identifies the presence of |
2866 | * a "mandatory" header. |
2867 | */ |
2868 | if (linelen >= 2 && strncmp(data, "M-", 2) == 0) { |
| 34 | | Assuming 'linelen' is >= 2 | |
|
| 35 | | Assuming the condition is false | |
|
2869 | data += 2; |
2870 | linelen -= 2; |
2871 | } |
2872 | |
2873 | /* |
2874 | * From draft-cohen-gena-client-01.txt, available from the uPnP forum: |
2875 | *NOTIFY, SUBSCRIBE, UNSUBSCRIBE |
2876 | * |
2877 | * From draft-ietf-dasl-protocol-00.txt, a now vanished Microsoft draft: |
2878 | *SEARCH |
2879 | */ |
2880 | if ((linelen >= 5 && strncmp(data, "HTTP/", 5) == 0) || |
| 36 | | Assuming 'linelen' is >= 5 | |
|
2881 | (linelen >= 3 && strncmp(data, "ICY", 3) == 0)) { |
2882 | *type = MEDIA_CONTAINER_HTTP_RESPONSE; |
2883 | isHttpRequestOrReply = true1;/* response */ |
2884 | if (reqresp_dissector36.1 | 'reqresp_dissector' is null |
36.1 | 'reqresp_dissector' is null | ) |
| |
2885 | *reqresp_dissector = basic_response_dissector; |
2886 | } else { |
2887 | const unsigned char * ptr = (const unsigned char *)data; |
2888 | int indx = 0; |
2889 | |
2890 | /* Look for the space following the Method */ |
2891 | while (indx < linelen) { |
2892 | if (*ptr == ' ') |
2893 | break; |
2894 | else { |
2895 | ptr++; |
2896 | indx++; |
2897 | } |
2898 | } |
2899 | |
2900 | /* Check the methods that have same length */ |
2901 | switch (indx) { |
2902 | |
2903 | case 3: |
2904 | if (strncmp(data, "GET", indx) == 0 || |
2905 | strncmp(data, "PUT", indx) == 0) { |
2906 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2907 | isHttpRequestOrReply = true1; |
2908 | } |
2909 | break; |
2910 | |
2911 | case 4: |
2912 | if (strncmp(data, "COPY", indx) == 0 || |
2913 | strncmp(data, "HEAD", indx) == 0 || |
2914 | strncmp(data, "LOCK", indx) == 0 || |
2915 | strncmp(data, "MOVE", indx) == 0 || |
2916 | strncmp(data, "POLL", indx) == 0 || |
2917 | strncmp(data, "POST", indx) == 0) { |
2918 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2919 | isHttpRequestOrReply = true1; |
2920 | } |
2921 | break; |
2922 | |
2923 | case 5: |
2924 | if (strncmp(data, "BCOPY", indx) == 0 || |
2925 | strncmp(data, "BMOVE", indx) == 0 || |
2926 | strncmp(data, "MKCOL", indx) == 0 || |
2927 | strncmp(data, "TRACE", indx) == 0 || |
2928 | strncmp(data, "PATCH", indx) == 0 || /* RFC 5789 */ |
2929 | strncmp(data, "LABEL", indx) == 0 || /* RFC 3253 8.2 */ |
2930 | strncmp(data, "MERGE", indx) == 0) { /* RFC 3253 11.2 */ |
2931 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2932 | isHttpRequestOrReply = true1; |
2933 | } |
2934 | break; |
2935 | |
2936 | case 6: |
2937 | if (strncmp(data, "DELETE", indx) == 0 || |
2938 | strncmp(data, "SEARCH", indx) == 0 || |
2939 | strncmp(data, "UNLOCK", indx) == 0 || |
2940 | strncmp(data, "REPORT", indx) == 0 || /* RFC 3253 3.6 */ |
2941 | strncmp(data, "UPDATE", indx) == 0) { /* RFC 3253 7.1 */ |
2942 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2943 | isHttpRequestOrReply = true1; |
2944 | } |
2945 | else if (strncmp(data, "NOTIFY", indx) == 0) { |
2946 | *type = MEDIA_CONTAINER_HTTP_NOTIFICATION; |
2947 | isHttpRequestOrReply = true1; |
2948 | } |
2949 | break; |
2950 | |
2951 | case 7: |
2952 | if (strncmp(data, "BDELETE", indx) == 0 || |
2953 | strncmp(data, "CONNECT", indx) == 0 || |
2954 | strncmp(data, "OPTIONS", indx) == 0 || |
2955 | strncmp(data, "CHECKIN", indx) == 0) { /* RFC 3253 4.4, 9.4 */ |
2956 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2957 | isHttpRequestOrReply = true1; |
2958 | } |
2959 | break; |
2960 | |
2961 | case 8: |
2962 | if (strncmp(data, "PROPFIND", indx) == 0 || |
2963 | strncmp(data, "CHECKOUT", indx) == 0 || /* RFC 3253 4.3, 9.3 */ |
2964 | strncmp(data, "CCM_POST", indx) == 0) { |
2965 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2966 | isHttpRequestOrReply = true1; |
2967 | } |
2968 | break; |
2969 | |
2970 | case 9: |
2971 | if (strncmp(data, "SUBSCRIBE", indx) == 0) { |
2972 | *type = MEDIA_CONTAINER_HTTP_NOTIFICATION; |
2973 | isHttpRequestOrReply = true1; |
2974 | } else if (strncmp(data, "PROPPATCH", indx) == 0 || |
2975 | strncmp(data, "BPROPFIND", indx) == 0) { |
2976 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2977 | isHttpRequestOrReply = true1; |
2978 | } |
2979 | break; |
2980 | |
2981 | case 10: |
2982 | if (strncmp(data, "BPROPPATCH", indx) == 0 || |
2983 | strncmp(data, "UNCHECKOUT", indx) == 0 || /* RFC 3253 4.5 */ |
2984 | strncmp(data, "MKACTIVITY", indx) == 0) { /* RFC 3253 13.5 */ |
2985 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2986 | isHttpRequestOrReply = true1; |
2987 | } |
2988 | break; |
2989 | |
2990 | case 11: |
2991 | if (strncmp(data, "MKWORKSPACE", indx) == 0 || /* RFC 3253 6.3 */ |
2992 | strncmp(data, "RPC_CONNECT", indx) == 0 || /* [MS-RPCH] 2.1.1.1.1 */ |
2993 | strncmp(data, "RPC_IN_DATA", indx) == 0) { /* [MS-RPCH] 2.1.2.1.1 */ |
2994 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
2995 | isHttpRequestOrReply = true1; |
2996 | } else if (strncmp(data, "UNSUBSCRIBE", indx) == 0) { |
2997 | *type = MEDIA_CONTAINER_HTTP_NOTIFICATION; |
2998 | isHttpRequestOrReply = true1; |
2999 | } |
3000 | break; |
3001 | |
3002 | case 12: |
3003 | if (strncmp(data, "RPC_OUT_DATA", indx) == 0) { /* [MS-RPCH] 2.1.2.1.2 */ |
3004 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
3005 | isHttpRequestOrReply = true1; |
3006 | } |
3007 | break; |
3008 | |
3009 | case 15: |
3010 | if (strncmp(data, "VERSION-CONTROL", indx) == 0) { /* RFC 3253 3.5 */ |
3011 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
3012 | isHttpRequestOrReply = true1; |
3013 | } |
3014 | break; |
3015 | |
3016 | case 16: |
3017 | if (strncmp(data, "BASELINE-CONTROL", indx) == 0) { /* RFC 3253 12.6 */ |
3018 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
3019 | isHttpRequestOrReply = true1; |
3020 | } else if (strncmp(data, "SSTP_DUPLEX_POST", indx) == 0) { /* MS SSTP */ |
3021 | *type = MEDIA_CONTAINER_HTTP_REQUEST; |
3022 | isHttpRequestOrReply = true1; |
3023 | } |
3024 | break; |
3025 | |
3026 | default: |
3027 | break; |
3028 | } |
3029 | |
3030 | if (isHttpRequestOrReply && reqresp_dissector) { |
3031 | *reqresp_dissector = basic_request_dissector; |
3032 | |
3033 | stat_info->request_method = wmem_strndup(pinfo->pool, data, indx); |
3034 | } |
3035 | |
3036 | |
3037 | |
3038 | } |
3039 | |
3040 | return isHttpRequestOrReply; |
| 38 | | Returning the value 1 (loaded from 'isHttpRequestOrReply'), which participates in a condition later | |
|
3041 | } |
3042 | |
3043 | /* |
3044 | * Process headers. |
3045 | */ |
3046 | typedef struct { |
3047 | const char*name; |
3048 | int*hf; |
3049 | intspecial; |
3050 | } header_info; |
3051 | |
3052 | #define HDR_NO_SPECIAL00 |
3053 | #define HDR_AUTHORIZATION11 |
3054 | #define HDR_AUTHENTICATE22 |
3055 | #define HDR_CONTENT_TYPE33 |
3056 | #define HDR_CONTENT_LENGTH44 |
3057 | #define HDR_CONTENT_ENCODING55 |
3058 | #define HDR_TRANSFER_ENCODING66 |
3059 | #define HDR_HOST77 |
3060 | #define HDR_UPGRADE88 |
3061 | #define HDR_COOKIE99 |
3062 | #define HDR_WEBSOCKET_PROTOCOL1010 |
3063 | #define HDR_WEBSOCKET_EXTENSIONS1111 |
3064 | #define HDR_REFERER1212 |
3065 | #define HDR_LOCATION1313 |
3066 | #define HDR_HTTP2_SETTINGS1414 |
3067 | #define HDR_RANGE15 15 |
3068 | #define HDR_CONTENT_RANGE1616 |
3069 | |
3070 | static const header_info headers[] = { |
3071 | { "Authorization", &hf_http_authorization, HDR_AUTHORIZATION1 }, |
3072 | { "Proxy-Authorization", &hf_http_proxy_authorization, HDR_AUTHORIZATION1 }, |
3073 | { "Proxy-Authenticate", &hf_http_proxy_authenticate, HDR_AUTHENTICATE2 }, |
3074 | { "WWW-Authenticate", &hf_http_www_authenticate, HDR_AUTHENTICATE2 }, |
3075 | { "Content-Type", &hf_http_content_type, HDR_CONTENT_TYPE3 }, |
3076 | { "Content-Length", &hf_http_content_length_header, HDR_CONTENT_LENGTH4 }, |
3077 | { "Content-Encoding", &hf_http_content_encoding, HDR_CONTENT_ENCODING5 }, |
3078 | { "Transfer-Encoding", &hf_http_transfer_encoding, HDR_TRANSFER_ENCODING6 }, |
3079 | { "Upgrade", &hf_http_upgrade, HDR_UPGRADE8 }, |
3080 | { "User-Agent",&hf_http_user_agent, HDR_NO_SPECIAL0 }, |
3081 | { "Host", &hf_http_host, HDR_HOST7 }, |
3082 | { "Range", &hf_http_range, HDR_RANGE15 }, |
3083 | { "Content-Range", &hf_http_content_range, HDR_CONTENT_RANGE16 }, |
3084 | { "Connection", &hf_http_connection, HDR_NO_SPECIAL0 }, |
3085 | { "Cookie", &hf_http_cookie, HDR_COOKIE9 }, |
3086 | { "Accept", &hf_http_accept, HDR_NO_SPECIAL0 }, |
3087 | { "Referer", &hf_http_referer, HDR_REFERER12 }, |
3088 | { "Accept-Language", &hf_http_accept_language, HDR_NO_SPECIAL0 }, |
3089 | { "Accept-Encoding", &hf_http_accept_encoding, HDR_NO_SPECIAL0 }, |
3090 | { "Date", &hf_http_date, HDR_NO_SPECIAL0 }, |
3091 | { "Cache-Control", &hf_http_cache_control, HDR_NO_SPECIAL0 }, |
3092 | { "Server", &hf_http_server, HDR_NO_SPECIAL0 }, |
3093 | { "Location", &hf_http_location, HDR_LOCATION13 }, |
3094 | { "Sec-WebSocket-Accept", &hf_http_sec_websocket_accept, HDR_NO_SPECIAL0 }, |
3095 | { "Sec-WebSocket-Extensions", &hf_http_sec_websocket_extensions, HDR_WEBSOCKET_EXTENSIONS11 }, |
3096 | { "Sec-WebSocket-Key", &hf_http_sec_websocket_key, HDR_NO_SPECIAL0 }, |
3097 | { "Sec-WebSocket-Protocol", &hf_http_sec_websocket_protocol, HDR_WEBSOCKET_PROTOCOL10 }, |
3098 | { "Sec-WebSocket-Version", &hf_http_sec_websocket_version, HDR_NO_SPECIAL0 }, |
3099 | { "Set-Cookie", &hf_http_set_cookie, HDR_NO_SPECIAL0 }, |
3100 | { "Last-Modified", &hf_http_last_modified, HDR_NO_SPECIAL0 }, |
3101 | { "X-Forwarded-For", &hf_http_x_forwarded_for, HDR_NO_SPECIAL0 }, |
3102 | { "HTTP2-Settings", &hf_http_http2_settings, HDR_HTTP2_SETTINGS14 }, |
3103 | }; |
3104 | |
3105 | /* |
3106 | * Look up a header name (assume lower-case header_name). |
3107 | */ |
3108 | static int* |
3109 | get_hf_for_header(char* header_name) |
3110 | { |
3111 | int* hf_id = NULL((void*)0); |
3112 | |
3113 | if (header_fields_hash) { |
3114 | hf_id = (int*) g_hash_table_lookup(header_fields_hash, header_name); |
3115 | } else { |
3116 | hf_id = NULL((void*)0); |
3117 | } |
3118 | |
3119 | return hf_id; |
3120 | } |
3121 | |
3122 | /* |
3123 | * |
3124 | */ |
3125 | static void |
3126 | deregister_header_fields(void) |
3127 | { |
3128 | if (dynamic_hf) { |
3129 | /* Deregister all fields */ |
3130 | for (unsigned i = 0; i < dynamic_hf_size; i++) { |
3131 | proto_deregister_field (proto_http, *(dynamic_hf[i].p_id)); |
3132 | g_free (dynamic_hf[i].p_id); |
3133 | } |
3134 | |
3135 | proto_add_deregistered_data (dynamic_hf); |
3136 | dynamic_hf = NULL((void*)0); |
3137 | dynamic_hf_size = 0; |
3138 | } |
3139 | |
3140 | if (header_fields_hash) { |
3141 | g_hash_table_destroy (header_fields_hash); |
3142 | header_fields_hash = NULL((void*)0); |
3143 | } |
3144 | } |
3145 | |
3146 | static void |
3147 | header_fields_post_update_cb(void) |
3148 | { |
3149 | int* hf_id; |
3150 | char* header_name; |
3151 | char* header_name_key; |
3152 | |
3153 | deregister_header_fields(); |
3154 | |
3155 | if (num_header_fields) { |
3156 | header_fields_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL((void*)0)); |
3157 | dynamic_hf = g_new0(hf_register_info, num_header_fields)((hf_register_info *) g_malloc0_n ((num_header_fields), sizeof (hf_register_info))); |
3158 | dynamic_hf_size = num_header_fields; |
3159 | |
3160 | for (unsigned i = 0; i < dynamic_hf_size; i++) { |
3161 | hf_id = g_new(int,1)((int *) g_malloc_n ((1), sizeof (int))); |
3162 | *hf_id = -1; |
3163 | header_name = g_strdup(header_fields[i].header_name)g_strdup_inline (header_fields[i].header_name); |
3164 | header_name_key = g_ascii_strdown(header_name, -1); |
3165 | |
3166 | dynamic_hf[i].p_id = hf_id; |
3167 | dynamic_hf[i].hfinfo.name = header_name; |
3168 | dynamic_hf[i].hfinfo.abbrev = ws_strdup_printf("http.header.%s", header_name)wmem_strdup_printf(((void*)0), "http.header.%s", header_name); |
3169 | dynamic_hf[i].hfinfo.type = FT_STRING; |
3170 | dynamic_hf[i].hfinfo.display = BASE_NONE; |
3171 | dynamic_hf[i].hfinfo.strings = NULL((void*)0); |
3172 | dynamic_hf[i].hfinfo.bitmask = 0; |
3173 | dynamic_hf[i].hfinfo.blurb = g_strdup(header_fields[i].header_desc)g_strdup_inline (header_fields[i].header_desc); |
3174 | HFILL_INIT(dynamic_hf[i])(dynamic_hf[i]).hfinfo.id = -1; (dynamic_hf[i]).hfinfo.parent = 0; (dynamic_hf[i]).hfinfo.ref_type = HF_REF_TYPE_NONE; (dynamic_hf [i]).hfinfo.same_name_prev_id = -1; (dynamic_hf[i]).hfinfo.same_name_next = ((void*)0);; |
3175 | |
3176 | g_hash_table_insert(header_fields_hash, header_name_key, hf_id); |
3177 | } |
3178 | |
3179 | proto_register_field_array(proto_http, dynamic_hf, dynamic_hf_size); |
3180 | } |
3181 | } |
3182 | |
3183 | static void |
3184 | header_fields_reset_cb(void) |
3185 | { |
3186 | deregister_header_fields(); |
3187 | } |
3188 | |
3189 | /** |
3190 | * Parses the transfer-coding, returning true if everything was fully understood |
3191 | * or false when unknown names were encountered. |
3192 | */ |
3193 | static bool_Bool |
3194 | http_parse_transfer_coding(const char *value, headers_t *eh_ptr) |
3195 | { |
3196 | bool_Bool is_fully_parsed = true1; |
3197 | |
3198 | /* Mark header as set, but with unknown encoding. */ |
3199 | eh_ptr->transfer_encoding = HTTP_TE_UNKNOWN; |
3200 | |
3201 | while (*value) { |
3202 | /* skip OWS (SP / HTAB) and commas; stop at the end. */ |
3203 | while (*value == ' ' || *value == '\t' || *value == ',') |
3204 | value++; |
3205 | if (!*value) |
3206 | break; |
3207 | |
3208 | if (g_str_has_prefix(value, "chunked")(__builtin_constant_p ("chunked")? __extension__ ({ const char * const __str = (value); const char * const __prefix = ("chunked" ); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (value, "chunked") )) { |
3209 | eh_ptr->transfer_encoding_chunked = true1; |
3210 | value += sizeof("chunked") - 1; |
3211 | continue; |
3212 | } |
3213 | |
3214 | /* For now assume that chunked can only combined with exactly |
3215 | * one other (compression) encoding. Anything else is |
3216 | * unsupported. */ |
3217 | if (eh_ptr->transfer_encoding != HTTP_TE_UNKNOWN) { |
3218 | /* No more transfer codings are expected. */ |
3219 | is_fully_parsed = false0; |
3220 | break; |
3221 | } |
3222 | |
3223 | if (g_str_has_prefix(value, "compress")(__builtin_constant_p ("compress")? __extension__ ({ const char * const __str = (value); const char * const __prefix = ("compress" ); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (value, "compress") )) { |
3224 | eh_ptr->transfer_encoding = HTTP_TE_COMPRESS; |
3225 | value += sizeof("compress") - 1; |
3226 | } else if (g_str_has_prefix(value, "deflate")(__builtin_constant_p ("deflate")? __extension__ ({ const char * const __str = (value); const char * const __prefix = ("deflate" ); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (value, "deflate") )) { |
3227 | eh_ptr->transfer_encoding = HTTP_TE_DEFLATE; |
3228 | value += sizeof("deflate") - 1; |
3229 | } else if (g_str_has_prefix(value, "gzip")(__builtin_constant_p ("gzip")? __extension__ ({ const char * const __str = (value); const char * const __prefix = ("gzip" ); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (value, "gzip") )) { |
3230 | eh_ptr->transfer_encoding = HTTP_TE_GZIP; |
3231 | value += sizeof("gzip") - 1; |
3232 | } else if (g_str_has_prefix(value, "identity")(__builtin_constant_p ("identity")? __extension__ ({ const char * const __str = (value); const char * const __prefix = ("identity" ); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (value, "identity") )) { |
3233 | eh_ptr->transfer_encoding = HTTP_TE_IDENTITY; |
3234 | value += sizeof("identity") - 1; |
3235 | } else if (g_str_has_prefix(value, "x-compress")(__builtin_constant_p ("x-compress")? __extension__ ({ const char * const __str = (value); const char * const __prefix = ("x-compress" ); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (value, "x-compress") )) { |
3236 | eh_ptr->transfer_encoding = HTTP_TE_COMPRESS; |
3237 | value += sizeof("x-compress") - 1; |
3238 | } else if (g_str_has_prefix(value, "x-gzip")(__builtin_constant_p ("x-gzip")? __extension__ ({ const char * const __str = (value); const char * const __prefix = ("x-gzip" ); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (value, "x-gzip") )) { |
3239 | eh_ptr->transfer_encoding = HTTP_TE_GZIP; |
3240 | value += sizeof("x-gzip") - 1; |
3241 | } else { |
3242 | /* Unknown transfer encoding, skip until next comma. |
3243 | * Stop when no more names are found. */ |
3244 | is_fully_parsed = false0; |
3245 | value = strchr(value, ','); |
3246 | if (!value) |
3247 | break; |
3248 | } |
3249 | } |
3250 | |
3251 | return is_fully_parsed; |
3252 | } |
3253 | |
3254 | static bool_Bool |
3255 | is_token_char(char c) |
3256 | { |
3257 | /* tchar according to https://tools.ietf.org/html/rfc7230#section-3.2.6 */ |
3258 | return strchr("!#$%&\\:*+-.^_`|~", c) || g_ascii_isalnum(c)((g_ascii_table[(guchar) (c)] & G_ASCII_ALNUM) != 0); |
3259 | } |
3260 | |
3261 | static bool_Bool |
3262 | valid_header_name(const unsigned char *line, int header_len) |
3263 | { |
3264 | |
3265 | /* |
3266 | * Validate the header name. This allows no space between the field name |
3267 | * and colon (RFC 7230, Section. 3.2.4). |
3268 | */ |
3269 | if (header_len == 0) { |
3270 | return false0; |
3271 | } |
3272 | for (int i = 0; i < header_len; i++) { |
3273 | /* |
3274 | * NUL is not a valid character; treat it specially |
3275 | * due to C's notion that strings are NUL-terminated. |
3276 | */ |
3277 | if (line[i] == '\0') { |
3278 | return false0; |
3279 | } |
3280 | if (!is_token_char(line[i])) { |
3281 | return false0; |
3282 | } |
3283 | } |
3284 | return true1; |
3285 | } |
3286 | |
3287 | static bool_Bool |
3288 | process_header(tvbuff_t *tvb, int offset, int next_offset, |
3289 | const unsigned char *line, int linelen, int colon_offset, |
3290 | packet_info *pinfo, proto_tree *tree, headers_t *eh_ptr, |
3291 | http_conv_t *conv_data, media_container_type_t http_type, wmem_map_t *header_value_map, |
3292 | bool_Bool streaming_chunk_mode) |
3293 | { |
3294 | int len; |
3295 | int line_end_offset; |
3296 | int header_len; |
3297 | int hf_index; |
3298 | unsigned char c; |
3299 | int value_offset; |
3300 | int value_len, value_bytes_len; |
3301 | uint8_t *value_bytes; |
3302 | char *value; |
3303 | char *header_name; |
3304 | char *p; |
3305 | unsigned char *up; |
3306 | proto_item *hdr_item, *it; |
3307 | int f; |
3308 | int* hf_id; |
3309 | tap_credential_t* auth; |
3310 | http_req_res_t *curr_req_res = (http_req_res_t *)p_get_proto_data(wmem_file_scope(), pinfo, |
3311 | proto_http, HTTP_PROTO_DATA_REQRES0); |
3312 | http_info_value_t *stat_info = p_get_proto_data(pinfo->pool, pinfo, proto_http, HTTP_PROTO_DATA_INFO1); |
3313 | wmem_allocator_t *scope = (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && streaming_chunk_mode) ? wmem_file_scope() : |
3314 | ((PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && streaming_chunk_mode) ? NULL((void*)0) : pinfo->pool); |
3315 | |
3316 | len = next_offset - offset; |
3317 | line_end_offset = offset + linelen; |
3318 | header_len = colon_offset - offset; |
3319 | |
3320 | /** |
3321 | * Not a valid header name? Just add a line plus expert info. |
3322 | */ |
3323 | if (!valid_header_name(line, header_len)) { |
3324 | if (http_check_ascii_headers) { |
3325 | /* If we're offering the chance for other dissectors to parse, |
3326 | * we shouldn't add any tree items ourselves. |
3327 | */ |
3328 | return false0; |
3329 | } |
3330 | if (http_type == MEDIA_CONTAINER_HTTP_REQUEST) { |
3331 | hf_index = hf_http_request_line; |
3332 | } else if (http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
3333 | hf_index = hf_http_response_line; |
3334 | } else { |
3335 | hf_index = hf_http_unknown_header; |
3336 | } |
3337 | it = proto_tree_add_item(tree, hf_index, tvb, offset, len, ENC_NA0x00000000|ENC_ASCII0x00000000); |
3338 | proto_item_set_text(it, "%s", format_text(pinfo->pool, line, len)); |
3339 | expert_add_info(pinfo, it, &ei_http_bad_header_name); |
3340 | return false0; |
3341 | } |
3342 | |
3343 | /* |
3344 | * Make a null-terminated, all-lower-case version of the header |
3345 | * name. |
3346 | */ |
3347 | header_name = wmem_ascii_strdown(pinfo->pool, &line[0], header_len); |
3348 | |
3349 | hf_index = find_header_hf_value(tvb, offset, header_len); |
3350 | |
3351 | /* |
3352 | * Skip whitespace after the colon. |
3353 | */ |
3354 | value_offset = colon_offset + 1; |
3355 | while (value_offset < line_end_offset |
3356 | && ((c = line[value_offset - offset]) == ' ' || c == '\t')) |
3357 | value_offset++; |
3358 | |
3359 | /* |
3360 | * Fetch the value. |
3361 | * |
3362 | * XXX - RFC 9110 5.5 "Specification for newly defined fields |
3363 | * SHOULD limit their values to visible US-ASCII octets (VCHAR), |
3364 | * SP, and HTAB. A recipient SHOULD treat other allowed octets in |
3365 | * field content (i.e., obs-text [%x80-FF]) as opaque data... |
3366 | * Field values containing CR, LF, or NUL characters are invalid |
3367 | * and dangerous." (Up to RFC 7230, an obsolete "line-folding" |
3368 | * mechanism that included CRLF was allowed.) |
3369 | * |
3370 | * So NUL is not allowed, and we should have one or more |
3371 | * expert infos if the field value has anything other than |
3372 | * ASCII printable + TAB. (Possibly different severities |
3373 | * depending on whether it contains obsolete characters |
3374 | * like \x80-\xFF vs characters never allowed like NUL.) |
3375 | * All known field types respect this (using Base64, etc.) |
3376 | * Unknown field types (possibly including those registered |
3377 | * through the UAT) should be treated like FT_BYTES with |
3378 | * BASE_SHOW_ASCII_PRINTABLE instead of FT_STRING, but it's |
3379 | * more difficult to do that with the custom formatting |
3380 | * that uses the header name. |
3381 | * |
3382 | * Instead, for now for display purposes we will treat strings |
3383 | * as ASCII and pass the raw value to subdissectors via the |
3384 | * header_value_map. For the latter, we allocate a buffer that's |
3385 | * value_bytes_len+1 bytes long, copy value_bytes_len bytes, and |
3386 | * stick in a NUL terminator, so that the buffer for value actually |
3387 | * has value_bytes_len bytes in it. |
3388 | */ |
3389 | value_bytes_len = line_end_offset - value_offset; |
3390 | value_bytes = (char *)wmem_alloc((scope ? scope : pinfo->pool), value_bytes_len+1); |
3391 | memcpy(value_bytes, &line[value_offset - offset], value_bytes_len); |
3392 | value_bytes[value_bytes_len] = '\0'; |
3393 | value = tvb_get_string_enc(pinfo->pool, tvb, value_offset, value_bytes_len, ENC_ASCII0x00000000); |
3394 | /* The length of the value might change after UTF-8 sanitization */ |
3395 | value_len = (int)strlen(value); |
3396 | |
3397 | if (scope == pinfo->pool) { |
3398 | wmem_map_insert(header_value_map, header_name, value_bytes); |
3399 | } else if (scope) { /* (!PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */ |
3400 | wmem_map_insert(header_value_map, wmem_strdup(scope, header_name), value_bytes); |
3401 | } /* else skip while (PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */ |
3402 | |
3403 | if (hf_index == -1) { |
3404 | /* |
3405 | * Not a header we know anything about. |
3406 | * Check if a HF generated from UAT information exists. |
3407 | */ |
3408 | hf_id = get_hf_for_header(header_name); |
3409 | |
3410 | if (tree) { |
3411 | if (!hf_id) { |
3412 | if (http_type == MEDIA_CONTAINER_HTTP_REQUEST || |
3413 | http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
3414 | it = proto_tree_add_item(tree, |
3415 | http_type == MEDIA_CONTAINER_HTTP_RESPONSE ? |
3416 | hf_http_response_line : |
3417 | hf_http_request_line, |
3418 | tvb, offset, len, |
3419 | ENC_NA0x00000000|ENC_ASCII0x00000000); |
3420 | proto_item_set_text(it, "%s", |
3421 | format_text(pinfo->pool, line, len)); |
3422 | } else { |
3423 | char* str = format_text(pinfo->pool, line, len); |
3424 | proto_tree_add_string_format(tree, hf_http_unknown_header, tvb, offset, |
3425 | len, str, "%s", str); |
3426 | } |
3427 | |
3428 | } else { |
3429 | proto_tree_add_string_format(tree, |
3430 | *hf_id, tvb, offset, len, value, |
3431 | "%s", format_text(pinfo->pool, line, len)); |
3432 | if (http_type == MEDIA_CONTAINER_HTTP_REQUEST || |
3433 | http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
3434 | it = proto_tree_add_item(tree, |
3435 | http_type == MEDIA_CONTAINER_HTTP_RESPONSE ? |
3436 | hf_http_response_line : |
3437 | hf_http_request_line, |
3438 | tvb, offset, len, |
3439 | ENC_NA0x00000000|ENC_ASCII0x00000000); |
3440 | proto_item_set_text(it, "%s", |
3441 | format_text(pinfo->pool, line, len)); |
3442 | proto_item_set_hidden(it); |
3443 | } |
3444 | } |
3445 | } |
3446 | } else { |
3447 | /* |
3448 | * Add it to the protocol tree as a particular field, |
3449 | * but display the line as is. |
3450 | */ |
3451 | if (tree) { |
3452 | header_field_info *hfinfo; |
3453 | uint32_t tmp; |
3454 | |
3455 | hfinfo = proto_registrar_get_nth(*headers[hf_index].hf); |
3456 | switch(hfinfo->type){ |
3457 | case FT_UINT8: |
3458 | case FT_UINT16: |
3459 | case FT_UINT24: |
3460 | case FT_UINT32: |
3461 | case FT_INT8: |
3462 | case FT_INT16: |
3463 | case FT_INT24: |
3464 | case FT_INT32: |
3465 | tmp=(uint32_t)strtol(value, NULL((void*)0), 10); |
3466 | hdr_item = proto_tree_add_uint(tree, *headers[hf_index].hf, tvb, offset, len, tmp); |
3467 | if (http_type == MEDIA_CONTAINER_HTTP_REQUEST || |
3468 | http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
3469 | it = proto_tree_add_item(tree, |
3470 | http_type == MEDIA_CONTAINER_HTTP_RESPONSE ? |
3471 | hf_http_response_line : |
3472 | hf_http_request_line, |
3473 | tvb, offset, len, |
3474 | ENC_NA0x00000000|ENC_ASCII0x00000000); |
3475 | proto_item_set_text(it, "%d", tmp); |
3476 | proto_item_set_hidden(it); |
3477 | } |
3478 | break; |
3479 | default: |
3480 | hdr_item = proto_tree_add_string_format(tree, |
3481 | *headers[hf_index].hf, tvb, offset, len, |
3482 | value, |
3483 | "%s", format_text(pinfo->pool, line, len)); |
3484 | if (http_type == MEDIA_CONTAINER_HTTP_REQUEST || |
3485 | http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
3486 | it = proto_tree_add_item(tree, |
3487 | http_type == MEDIA_CONTAINER_HTTP_RESPONSE ? |
3488 | hf_http_response_line : |
3489 | hf_http_request_line, |
3490 | tvb, offset, len, |
3491 | ENC_NA0x00000000|ENC_ASCII0x00000000); |
3492 | proto_item_set_text(it, "%s", |
3493 | format_text(pinfo->pool, line, len)); |
3494 | proto_item_set_hidden(it); |
3495 | } |
3496 | } |
3497 | } else |
3498 | hdr_item = NULL((void*)0); |
3499 | |
3500 | /* |
3501 | * Do any special processing that particular headers |
3502 | * require. |
3503 | */ |
3504 | switch (headers[hf_index].special) { |
3505 | |
3506 | case HDR_AUTHORIZATION1: |
3507 | if (check_auth_ntlmssp(hdr_item, tvb, pinfo, value)) |
3508 | break;/* dissected NTLMSSP */ |
3509 | if (check_auth_basic(hdr_item, tvb, pinfo, value)) |
3510 | break; /* dissected basic auth */ |
3511 | if (check_auth_citrixbasic(hdr_item, tvb, pinfo, value, offset)) |
3512 | break; /* dissected citrix basic auth */ |
3513 | if (check_auth_kerberos(hdr_item, tvb, pinfo, value)) |
3514 | break; |
3515 | if (check_auth_digest(hdr_item, tvb, pinfo, value, offset, value_len)) |
3516 | break;/* dissected digest basic auth */ |
3517 | auth = wmem_new0(pinfo->pool, tap_credential_t)((tap_credential_t*)wmem_alloc0((pinfo->pool), sizeof(tap_credential_t ))); |
3518 | auth->num = pinfo->num; |
3519 | auth->password_hf_id = *headers[hf_index].hf; |
3520 | auth->proto = "HTTP header auth"; |
3521 | auth->username = wmem_strdup(pinfo->pool, TAP_CREDENTIALS_PLACEHOLDER"n.a."); |
3522 | tap_queue_packet(credentials_tap, pinfo, auth); |
3523 | break; |
3524 | |
3525 | case HDR_AUTHENTICATE2: |
3526 | if (check_auth_ntlmssp(hdr_item, tvb, pinfo, value)) |
3527 | break; /* dissected NTLMSSP */ |
3528 | check_auth_kerberos(hdr_item, tvb, pinfo, value); |
3529 | break; |
3530 | |
3531 | case HDR_CONTENT_TYPE3: |
3532 | if (scope == NULL((void*)0)) { /* identical to (PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */ |
3533 | break; /* eh_ptr->content_type[_parameters] must have been set during first scan */ |
3534 | } |
3535 | eh_ptr->content_type = wmem_strdup(scope, value); |
3536 | |
3537 | for (f = 0; f < value_len; f++) { |
3538 | c = value[f]; |
3539 | if (c == ';' || g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)) { |
3540 | /* |
3541 | * End of subtype - either |
3542 | * white space or a ";" |
3543 | * separating the subtype from |
3544 | * a parameter. |
3545 | */ |
3546 | break; |
3547 | } |
3548 | |
3549 | /* |
3550 | * Map the character to lower case; |
3551 | * content types are case-insensitive. |
3552 | */ |
3553 | eh_ptr->content_type[f] = g_ascii_tolower(eh_ptr->content_type[f]); |
3554 | } |
3555 | eh_ptr->content_type[f] = '\0'; |
3556 | /* |
3557 | * Now find the start of the optional parameters; |
3558 | * skip the optional white space and the semicolon |
3559 | * if this has not been done before. |
3560 | */ |
3561 | f++; |
3562 | while (f < value_len) { |
3563 | c = eh_ptr->content_type[f]; |
3564 | if (c == ';' || g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)) |
3565 | /* Skip till start of parameters */ |
3566 | f++; |
3567 | else |
3568 | break; |
3569 | } |
3570 | if (f < value_len) |
3571 | eh_ptr->content_type_parameters = eh_ptr->content_type + f; |
3572 | else |
3573 | eh_ptr->content_type_parameters = NULL((void*)0); |
3574 | break; |
3575 | |
3576 | case HDR_CONTENT_LENGTH4: |
3577 | DISSECTOR_ASSERT_HINT(!streaming_chunk_mode, "In streaming chunk mode, there will never be content-length header.")((void) ((!streaming_chunk_mode) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\" (%s)", "epan/dissectors/packet-http.c" , 3577, "!streaming_chunk_mode", "In streaming chunk mode, there will never be content-length header." )))); |
3578 | errno(*__errno_location ()) = 0; |
3579 | eh_ptr->content_length = g_ascii_strtoll(value, &p, 10); |
3580 | up = (unsigned char *)p; |
3581 | if (eh_ptr->content_length < 0 || |
3582 | p == value || |
3583 | errno(*__errno_location ()) == ERANGE34 || |
3584 | (*up != '\0' && !g_ascii_isspace(*up)((g_ascii_table[(guchar) (*up)] & G_ASCII_SPACE) != 0))) { |
3585 | /* |
3586 | * Content length not valid; pretend |
3587 | * we don't have it. |
3588 | */ |
3589 | eh_ptr->have_content_length = false0; |
3590 | } else { |
3591 | proto_tree *header_tree; |
3592 | proto_item *tree_item; |
3593 | /* |
3594 | * We do have a valid content length. |
3595 | */ |
3596 | eh_ptr->have_content_length = true1; |
3597 | header_tree = proto_item_add_subtree(hdr_item, ett_http_header_item); |
3598 | tree_item = proto_tree_add_uint64(header_tree, hf_http_content_length, |
3599 | tvb, offset, len, eh_ptr->content_length); |
3600 | proto_item_set_generated(tree_item); |
3601 | if (eh_ptr->transfer_encoding != HTTP_TE_NONE) { |
3602 | expert_add_info(pinfo, hdr_item, &ei_http_te_and_length); |
3603 | } |
3604 | } |
3605 | break; |
3606 | |
3607 | case HDR_CONTENT_ENCODING5: |
3608 | if (scope == NULL((void*)0)) { /* identical to (PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */ |
3609 | break; /* eh_ptr->content_encoding must have been set during first scan */ |
3610 | } |
3611 | eh_ptr->content_encoding = wmem_strndup(scope, value, value_len); |
3612 | break; |
3613 | |
3614 | case HDR_TRANSFER_ENCODING6: |
3615 | if (eh_ptr->have_content_length) { |
3616 | expert_add_info(pinfo, hdr_item, &ei_http_te_and_length); |
3617 | } |
3618 | if (!http_parse_transfer_coding(value, eh_ptr)) { |
3619 | expert_add_info(pinfo, hdr_item, &ei_http_te_unknown); |
3620 | } |
3621 | break; |
3622 | |
3623 | case HDR_HOST7: |
3624 | stat_info->http_host = wmem_strndup(pinfo->pool, value, value_len); |
3625 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && curr_req_res) { |
3626 | curr_req_res->http_host = wmem_strndup(wmem_file_scope(), value, value_len); |
3627 | } |
3628 | break; |
3629 | |
3630 | case HDR_UPGRADE8: |
3631 | if (scope == NULL((void*)0)) { /* identical to (PINFO_FD_VISITED(pinfo) && streaming_chunk_mode) */ |
3632 | break; |
3633 | } |
3634 | eh_ptr->upgrade = wmem_ascii_strdown(scope, value, value_len); |
3635 | break; |
3636 | |
3637 | case HDR_COOKIE9: |
3638 | if (hdr_item) { |
3639 | proto_tree *cookie_tree; |
3640 | char *part, *part_end; |
3641 | int part_len; |
3642 | |
3643 | cookie_tree = proto_item_add_subtree(hdr_item, ett_http_header_item); |
3644 | for (f = 0; f < value_len; ) { |
3645 | /* skip whitespace and ';' (terminates at '\0' or earlier) */ |
3646 | c = value[f]; |
3647 | while (c == ';' || g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)) |
3648 | c = value[++f]; |
3649 | |
3650 | if (f >= value_len) |
3651 | break; |
3652 | |
3653 | /* find "cookie=foo " in "cookie=foo ; bar" */ |
3654 | part = value + f; |
3655 | part_end = (char *)memchr(part, ';', value_len - f); |
3656 | if (part_end) |
3657 | part_len =(int)(part_end - part); |
3658 | else |
3659 | part_len = value_len - f; |
3660 | |
3661 | /* finally add cookie to tree */ |
3662 | proto_tree_add_item(cookie_tree, hf_http_cookie_pair, |
3663 | tvb, value_offset + f, part_len, ENC_NA0x00000000|ENC_ASCII0x00000000); |
3664 | f += part_len; |
3665 | } |
3666 | } |
3667 | break; |
3668 | |
3669 | case HDR_WEBSOCKET_PROTOCOL10: |
3670 | if (http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
3671 | conv_data->websocket_protocol = wmem_strndup(wmem_file_scope(), value, value_len); |
3672 | } |
3673 | break; |
3674 | |
3675 | case HDR_WEBSOCKET_EXTENSIONS11: |
3676 | if (http_type == MEDIA_CONTAINER_HTTP_RESPONSE) { |
3677 | conv_data->websocket_extensions = wmem_strndup(wmem_file_scope(), value, value_len); |
3678 | } |
3679 | break; |
3680 | |
3681 | case HDR_REFERER12: |
3682 | stat_info->referer_uri = wmem_strndup(pinfo->pool, value, value_len); |
3683 | break; |
3684 | |
3685 | case HDR_LOCATION13: |
3686 | if (curr_req_res && curr_req_res->request_uri){ |
3687 | stat_info->location_target = wmem_strndup(pinfo->pool, value, value_len); |
3688 | stat_info->location_base_uri = wmem_strdup(pinfo->pool, curr_req_res->full_uri); |
3689 | } |
3690 | break; |
3691 | case HDR_HTTP2_SETTINGS14: |
3692 | { |
3693 | proto_tree* settings_tree = proto_item_add_subtree(hdr_item, ett_http_http2_settings_item); |
3694 | tvbuff_t* new_tvb = base64uri_tvb_to_new_tvb(tvb, value_offset, value_bytes_len); |
3695 | add_new_data_source(pinfo, new_tvb, "Base64uri decoded"); |
3696 | TRY{ except_t *volatile exc; volatile int except_state = 0; static const except_id_t catch_spec[] = { { 1, 0 } }; { struct except_stacknode except_sn; struct except_catch except_ch; except_setup_try(& except_sn, &except_ch, catch_spec, 1); if (_setjmp (except_ch .except_jmp)) *(&exc) = &except_ch.except_obj; else * (&exc) = 0; if(except_state & 1) except_state |= 2; except_state &= ~1; if (except_state == 0 && exc == 0){ |
3697 | dissect_http2_settings_ext(new_tvb, pinfo, settings_tree, 0); |
3698 | } CATCH_ALLif (except_state == 0 && exc != 0 && (except_state |=1)){ |
3699 | show_exception(tvb, pinfo, settings_tree, EXCEPT_CODE((exc)->except_id.except_code), GET_MESSAGE((exc)->except_message)); |
3700 | } |
3701 | ENDTRYif(!(except_state&1) && exc != 0) except_rethrow( exc); except_free(except_ch.except_obj.except_dyndata); except_pop (); };}; |
3702 | |
3703 | break; |
3704 | } |
3705 | case HDR_RANGE15: |
3706 | { |
3707 | /* THIS IS A GET REQUEST |
3708 | * Note: GET is the only method that employs ranges. |
3709 | * (Unless the data has errors or is noncompliant.) |
3710 | */ |
3711 | if (curr_req_res && !pinfo->fd->visited) { |
3712 | /* |
3713 | * Unlike protocols such as NFS and SMB, the HTTP protocol (RFC 9110) does not |
3714 | * provide an identifier with which to match requests and responses. Instead, |
3715 | * matching is solely based upon the order in which responses are received. |
3716 | * HTTP I/O is 'asynchronously ordered' such that, for example, the first of four |
3717 | * GET responses are matched with the first outstanding request, the next |
3718 | * response with the second oldest outstanding request and so on (FIFO). |
3719 | * The previous method instead matched responses with the last of several |
3720 | * async requests rather than the first (LIFO), and did not handle requests |
3721 | * with no responses such as the case where one or more HTTP packets were |
3722 | * not captured. Whenever there were multiple outstanding requests, the SRT |
3723 | * (RTT) stats were incorrect, in some cases massively so. |
3724 | * |
3725 | * While RFC 9110 expressly prohibits matching via byte ranges because, among |
3726 | * other things, the server may return fewer bytes than requested, |
3727 | * the first number of the range does not change. Unlike HTTP implementations, |
3728 | * Wireshark has the problem of requests/responses missing from the capture |
3729 | * file. In such cases resumption of correct matching was virtually impossible. |
3730 | * In addition, all matching was incorrect from that point on. |
3731 | * |
3732 | * The method of matching used herein is able to recover from packet loss, |
3733 | * any number of missing frames, and duplicate range requests. The |
3734 | * method used is explained within the comments. |
3735 | */ |
3736 | /* https://www.rfc-editor.org/rfc/rfc9110.html#name-range-requests |
3737 | * Note that RFC 9110 16.5 defines a registry for |
3738 | * range units, but only bytes are registered. |
3739 | * ABNF: |
3740 | * Range = ranges-specifier |
3741 | * ranges-specifier = range-unit "=" range-set |
3742 | * range-set = 1#range-spec |
3743 | * range-spec = int-range / suffix-range / other-range |
3744 | * 1# is an ABNF extension defined in RFC 9110 5.6.1 |
3745 | * which covers comma separated list with optional |
3746 | * white space: |
3747 | * 1#element => element *( OWS "," OWS element ) |
3748 | * We don't care about other-range, but will try to |
3749 | * handle int-range and suffix-range. |
3750 | * This ignores any entries past the first in a list, |
3751 | * though responses to such would be multipart. |
3752 | * As mentioned above, this breaks down if the |
3753 | * response does not include all requested ranges |
3754 | * fully in one response. |
3755 | */ |
3756 | const char *pos = strchr(value, '='); |
3757 | if (pos == NULL((void*)0)) { |
3758 | break; |
3759 | } |
3760 | pos++; |
3761 | uint64_t first_range_num = 0; |
3762 | /* Get the first range number */ |
3763 | ws_strtou64(pos, &pos, &first_range_num); |
3764 | /* If the first number of the range is missing or '0', |
3765 | * use the second number in the range instead if we can. |
3766 | * XXX - Unlike strtoul, we can check the return value |
3767 | * of ws_strtou64() to distinguish between "converted |
3768 | * successfully as 0" and "failed conversion." |
3769 | * Note that strtoul allows an unsigned integer to |
3770 | * begin with a negative sign and applies unsigned |
3771 | * integer wraparound rules. |
3772 | * ws_strtouXX rejects an initial hyphen-minus, which |
3773 | * is good, as we want to properly handle: |
3774 | * suffix-range = "-" suffix-length |
3775 | */ |
3776 | if (first_range_num == 0 && *pos == '-') { |
3777 | pos++; |
3778 | /* Pass in an end pointer to convert |
3779 | * a list of ranges, the first of which is |
3780 | * a suffix-range. |
3781 | */ |
3782 | ws_strtou64(pos, &pos, &first_range_num); |
3783 | } |
3784 | /* req_list is used for req/resp matching and the deletion (and freeing) of matching |
3785 | * requests and any orphans that preceed them. A GSList is used instead of a wmem map |
3786 | * because there are rarely more than 10 requests in the list." |
3787 | */ |
3788 | if (first_range_num > 0) { |
3789 | request_trans_t* req_trans = wmem_new(wmem_file_scope(), request_trans_t)((request_trans_t*)wmem_alloc((wmem_file_scope()), sizeof(request_trans_t ))); |
3790 | req_trans->first_range_num = first_range_num; |
3791 | req_trans->req_frame = pinfo->num; |
3792 | req_trans->abs_time = pinfo->fd->abs_ts; |
3793 | req_trans->request_uri = curr_req_res->request_uri; |
3794 | |
3795 | /* XXX - This leaks if matching responses aren't |
3796 | * found (the data does not, but the list node |
3797 | * does.) A wmem_list would prevent that. |
3798 | */ |
3799 | conv_data->req_list = g_slist_append(conv_data->req_list, GUINT_TO_POINTER(req_trans)((gpointer) (gulong) (req_trans))); |
3800 | curr_req_res->req_has_range = true1; |
3801 | } |
3802 | } |
3803 | |
3804 | break; |
3805 | } |
3806 | case HDR_CONTENT_RANGE16: |
3807 | /* |
3808 | * THIS IS A GET RESPONSE |
3809 | * GET is the only method that employs ranges. |
3810 | * XXX - Except that RFC 9110 14.4 & 14.5 note that by |
3811 | * private agreement it can be included in a request |
3812 | * to request a partial PUT. |
3813 | * ABNF: |
3814 | * Content-Range = range-unit SP ( range-resp / unsatisfied-range ) |
3815 | * range-resp = incl-range "/" ( complete-length / "*" ) |
3816 | * We do not attempt to handle unsatisfied-range. |
3817 | * Note that only one range can be included; multiple |
3818 | * ranges are transmitted with the media type of |
3819 | * "multipart/byteranges" and each body part contains |
3820 | * its own Content-Type and Content-Range fields. |
3821 | * The multipart dissector does not handle this nor |
3822 | * access the request list. |
3823 | */ |
3824 | if (curr_req_res && !pinfo->fd->visited) { |
3825 | request_trans_t *req_trans; |
3826 | match_trans_t *match_trans = NULL((void*)0); |
3827 | nstime_t ns; |
3828 | GSList *iter = NULL((void*)0); |
3829 | |
3830 | /* Note SP instead of '=' in ABNF. */ |
3831 | const char *pos = strchr(value, ' '); |
3832 | if (pos == NULL((void*)0)) { |
3833 | break; |
3834 | } |
3835 | pos++; |
3836 | uint64_t first_crange_num = 0; |
3837 | /* Get the first content range number */ |
3838 | ws_strtou64(pos, &pos, &first_crange_num); |
3839 | |
3840 | if (first_crange_num == 0 && *pos == '-') { |
3841 | pos++; |
3842 | ws_strtou64(pos, &pos, &first_crange_num); |
3843 | } |
3844 | |
3845 | /* Get the position of the matching request if any in the reqs_table. |
3846 | * This is used to remove and free the matching request, and the unmatched |
3847 | * requests (orphans) that preceed it. |
3848 | * XXX - There is *NO* guarantee that there is |
3849 | * a perfectly matching request, see 15.3.7: |
3850 | * "However, a server might want to send only a |
3851 | * subset of the data requested for reasons of |
3852 | * its own... A client MUST inspect a 206 |
3853 | * response's Content-Type and Content-Range |
3854 | * field(s) to determine what parts are enclosed |
3855 | * and whether additional requests are needed." |
3856 | * Also 15.3.7.2 Multiple Parts, noting that |
3857 | * the response may be sent in a Content-Type |
3858 | * multipart/byteranges, also "When multiple |
3859 | * ranges are requested, a server MAY coalesce |
3860 | * any of the ranges that overlap, or that are |
3861 | * separated by a gap that is smaller than the |
3862 | * overhead of sending multiple parts, regardless |
3863 | * of the order in which the corresponding range- |
3864 | * spec appeared in the received Range header |
3865 | * field." and 15.3.7.3 Combining Parts. |
3866 | * However, as mentioned above, the LIFO method |
3867 | * had issues with that as well. Truly proper |
3868 | * handling of such edge cases is more difficult. |
3869 | */ |
3870 | req_trans = NULL((void*)0); |
3871 | if (conv_data->req_list && conv_data->req_list->data) { |
3872 | for (iter = conv_data->req_list; iter; iter = iter->next) { |
3873 | if (((request_trans_t*)iter->data)->first_range_num == first_crange_num) { |
3874 | req_trans = iter->data; |
3875 | break; |
3876 | } |
3877 | } |
3878 | } |
3879 | |
3880 | if (first_crange_num != 0 && req_trans) { |
3881 | match_trans = wmem_new(wmem_file_scope(), match_trans_t)((match_trans_t*)wmem_alloc((wmem_file_scope()), sizeof(match_trans_t ))); |
3882 | match_trans->req_frame = req_trans->req_frame; |
3883 | match_trans->resp_frame = pinfo->num; |
3884 | nstime_delta(&ns, &pinfo->fd->abs_ts, &req_trans->abs_time); |
3885 | match_trans->delta_time = ns; |
3886 | match_trans->request_uri = req_trans->request_uri; |
3887 | match_trans->http_host = curr_req_res->http_host; |
3888 | |
3889 | wmem_map_insert(conv_data->matches_table, |
3890 | GUINT_TO_POINTER(match_trans->req_frame)((gpointer) (gulong) (match_trans->req_frame)), (void *)match_trans); |
3891 | wmem_map_insert(conv_data->matches_table, |
3892 | GUINT_TO_POINTER(match_trans->resp_frame)((gpointer) (gulong) (match_trans->resp_frame)), (void *)match_trans); |
3893 | |
3894 | /* Remove and free all of the list entries up to and including the |
3895 | * matching one from req_list. */ |
3896 | if (conv_data->req_list) { |
3897 | GSList *top_of_list = NULL((void*)0); |
3898 | |
3899 | top_of_list = conv_data->req_list; |
3900 | while (top_of_list && top_of_list->data != req_trans) { |
3901 | |
3902 | top_of_list = g_slist_delete_link(top_of_list, top_of_list); |
3903 | } |
3904 | if (top_of_list && top_of_list->data == req_trans) { |
3905 | |
3906 | top_of_list = g_slist_delete_link(top_of_list, top_of_list); |
3907 | } |
3908 | conv_data->req_list = top_of_list; |
3909 | } |
3910 | } |
3911 | } |
3912 | if (curr_req_res) |
3913 | curr_req_res->resp_has_range = true1; |
3914 | break; |
3915 | } |
3916 | } |
3917 | return true1; |
3918 | } |
3919 | |
3920 | /* Returns index of header tag in headers */ |
3921 | static int |
3922 | find_header_hf_value(tvbuff_t *tvb, int offset, unsigned header_len) |
3923 | { |
3924 | unsigned i; |
3925 | |
3926 | for (i = 0; i < array_length(headers)(sizeof (headers) / sizeof (headers)[0]); i++) { |
3927 | if (header_len == strlen(headers[i].name) && |
3928 | tvb_strncaseeql(tvb, offset, |
3929 | headers[i].name, header_len) == 0) |
3930 | return i; |
3931 | } |
3932 | |
3933 | return -1; |
3934 | } |
3935 | |
3936 | /* |
3937 | * Dissect Microsoft's abomination called NTLMSSP over HTTP. |
3938 | */ |
3939 | static bool_Bool |
3940 | check_auth_ntlmssp(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo, char *value) |
3941 | { |
3942 | static const char *ntlm_headers[] = { |
3943 | "NTLM ", |
3944 | "Negotiate ", |
3945 | NULL((void*)0) |
3946 | }; |
3947 | const char **header; |
3948 | size_t hdrlen; |
3949 | proto_tree *hdr_tree; |
3950 | |
3951 | /* |
3952 | * Check for NTLM credentials and challenge; those can |
3953 | * occur with WWW-Authenticate. |
3954 | */ |
3955 | for (header = &ntlm_headers[0]; *header != NULL((void*)0); header++) { |
3956 | hdrlen = strlen(*header); |
3957 | if (strncmp(value, *header, hdrlen) == 0) { |
3958 | if (hdr_item != NULL((void*)0)) { |
3959 | hdr_tree = proto_item_add_subtree(hdr_item, |
3960 | ett_http_ntlmssp); |
3961 | } else |
3962 | hdr_tree = NULL((void*)0); |
3963 | value += hdrlen; |
3964 | dissect_http_ntlmssp(tvb, pinfo, hdr_tree, value); |
3965 | return true1; |
3966 | } |
3967 | } |
3968 | return false0; |
3969 | } |
3970 | |
3971 | static tap_credential_t* |
3972 | basic_auth_credentials(wmem_allocator_t *scope, const char* str) |
3973 | { |
3974 | char **tokens = g_strsplit(str, ":", -1); |
3975 | |
3976 | if (!tokens || !tokens[0] || !tokens[1]) { |
3977 | g_strfreev(tokens); |
3978 | return NULL((void*)0); |
3979 | } |
3980 | |
3981 | tap_credential_t* auth = wmem_new0(scope, tap_credential_t)((tap_credential_t*)wmem_alloc0((scope), sizeof(tap_credential_t ))); |
3982 | |
3983 | auth->username = wmem_strdup(scope, tokens[0]); |
3984 | auth->proto = "HTTP basic auth"; |
3985 | |
3986 | g_strfreev(tokens); |
3987 | |
3988 | return auth; |
3989 | } |
3990 | |
3991 | /* |
3992 | * Dissect HTTP Basic authorization. |
3993 | */ |
3994 | static bool_Bool |
3995 | check_auth_basic(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo, char *value) |
3996 | { |
3997 | static const char *basic_headers[] = { |
3998 | "Basic ", |
3999 | NULL((void*)0) |
4000 | }; |
4001 | const char **header; |
4002 | size_t hdrlen; |
4003 | const uint8_t *decoded_value; |
4004 | proto_tree *hdr_tree; |
4005 | tvbuff_t *auth_tvb; |
4006 | |
4007 | for (header = &basic_headers[0]; *header != NULL((void*)0); header++) { |
4008 | hdrlen = strlen(*header); |
4009 | if (strncmp(value, *header, hdrlen) == 0) { |
4010 | if (hdr_item != NULL((void*)0)) { |
4011 | hdr_tree = proto_item_add_subtree(hdr_item, |
4012 | ett_http_ntlmssp); |
4013 | } else |
4014 | hdr_tree = NULL((void*)0); |
4015 | value += hdrlen; |
4016 | |
4017 | auth_tvb = base64_to_tvb(tvb, value); |
4018 | add_new_data_source(pinfo, auth_tvb, "Basic Credentials"); |
4019 | /* RFC 7617 says that the character encoding is only |
4020 | * known to be UTF-8 if the 'charset' parameter was |
4021 | * used. Otherwise, after Base64 decoding it could be |
4022 | * any character encoding. |
4023 | * XXX: Perhaps the field should be a FT_BYTES with |
4024 | * BASE_SHOW_UTF_8_PRINTABLE? |
4025 | */ |
4026 | proto_tree_add_item_ret_string(hdr_tree, hf_http_basic, auth_tvb, 0, tvb_reported_length(auth_tvb), ENC_UTF_80x00000002, pinfo->pool, &decoded_value); |
4027 | tap_credential_t* auth = basic_auth_credentials(pinfo->pool, decoded_value); |
4028 | if (auth) { |
4029 | auth->num = auth->username_num = pinfo->num; |
4030 | auth->password_hf_id = hf_http_basic; |
4031 | tap_queue_packet(credentials_tap, pinfo, auth); |
4032 | } |
4033 | |
4034 | return true1; |
4035 | } |
4036 | } |
4037 | return false0; |
4038 | } |
4039 | |
4040 | /* |
4041 | * Dissect HTTP Digest authorization. |
4042 | */ |
4043 | static bool_Bool |
4044 | check_auth_digest(proto_item* hdr_item, tvbuff_t* tvb, packet_info* pinfo _U___attribute__((unused)), char* value, int offset, int len) |
4045 | { |
4046 | proto_tree* hdr_tree; |
4047 | int queried_offset; |
4048 | |
4049 | if (strncmp(value, "Digest", 6) == 0) { |
4050 | if (hdr_item != NULL((void*)0)) { |
4051 | hdr_tree = proto_item_add_subtree(hdr_item, ett_http_ntlmssp); |
4052 | } else { |
4053 | hdr_tree = NULL((void*)0); |
4054 | } |
4055 | offset += 21; |
4056 | len -= 21; |
4057 | while (len > 0) { |
4058 | /* Find comma/end of line */ |
4059 | queried_offset = tvb_find_uint8(tvb, offset, len, ','); |
4060 | if (queried_offset > 0) { |
4061 | proto_tree_add_format_text(hdr_tree, tvb, offset, queried_offset - offset); |
4062 | len -= (queried_offset - offset); |
4063 | offset = queried_offset + 1; |
4064 | } else { |
4065 | len = 0; |
4066 | } |
4067 | } |
4068 | return true1; |
4069 | } else { |
4070 | return false0; |
4071 | } |
4072 | } |
4073 | /* |
4074 | * Dissect HTTP CitrixAGBasic authorization. |
4075 | */ |
4076 | static bool_Bool |
4077 | check_auth_citrixbasic(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo, char *value, int offset) |
4078 | { |
4079 | static const char *basic_headers[] = { |
4080 | "CitrixAGBasic ", |
4081 | NULL((void*)0) |
4082 | }; |
4083 | const char **header; |
4084 | size_t hdrlen; |
4085 | proto_tree *hdr_tree; |
4086 | char *ch_ptr; |
4087 | int data_len; |
4088 | tvbuff_t *data_tvb; |
4089 | proto_item *hidden_item; |
4090 | proto_item *pi; |
4091 | const uint8_t *user = NULL((void*)0), *passwd = NULL((void*)0); |
4092 | |
4093 | for (header = &basic_headers[0]; *header != NULL((void*)0); header++) { |
4094 | hdrlen = strlen(*header); |
4095 | if (strncmp(value, *header, hdrlen) == 0) { |
4096 | if (hdr_item != NULL((void*)0)) { |
4097 | hdr_tree = proto_item_add_subtree(hdr_item, |
4098 | ett_http_ntlmssp); |
4099 | } else |
4100 | hdr_tree = NULL((void*)0); |
4101 | value += hdrlen; |
4102 | offset += (int)hdrlen + 15; |
4103 | hidden_item = proto_tree_add_boolean(hdr_tree, |
4104 | hf_http_citrix, tvb, 0, 0, 1); |
4105 | proto_item_set_hidden(hidden_item); |
4106 | |
4107 | if(strncmp(value, "username=\"", 10) == 0) { |
4108 | value += 10; |
4109 | offset += 10; |
4110 | ch_ptr = strchr(value, '"'); |
4111 | if ( ch_ptr != NULL((void*)0) ) { |
4112 | data_len = (int)(ch_ptr - value); |
4113 | if (data_len) { |
4114 | data_tvb = base64_tvb_to_new_tvb(tvb, offset, data_len); |
4115 | add_new_data_source(pinfo, data_tvb, "Username"); |
4116 | /* XXX: We don't know for certain the string encoding here. */ |
4117 | pi = proto_tree_add_item_ret_string(hdr_tree, hf_http_citrix_user, data_tvb, 0, tvb_reported_length(data_tvb), ENC_UTF_80x00000002, pinfo->pool, &user); |
4118 | } else { |
4119 | pi = proto_tree_add_string(hdr_tree, hf_http_citrix_user, tvb, offset, 0, ""); |
4120 | } |
4121 | proto_item_set_generated(pi); |
4122 | value += data_len + 1; |
4123 | offset += data_len + 1; |
4124 | } |
4125 | } |
4126 | if(strncmp(value, "; domain=\"", 10) == 0) { |
4127 | value += 10; |
4128 | offset += 10; |
4129 | ch_ptr = strchr(value, '"'); |
4130 | if ( ch_ptr != NULL((void*)0) ) { |
4131 | data_len = (int)(ch_ptr - value); |
4132 | if (data_len) { |
4133 | data_tvb = base64_tvb_to_new_tvb(tvb, offset, data_len); |
4134 | add_new_data_source(pinfo, data_tvb, "Domain"); |
4135 | pi = proto_tree_add_item(hdr_tree, hf_http_citrix_domain, data_tvb, 0, tvb_reported_length(data_tvb), ENC_UTF_80x00000002); |
4136 | } else { |
4137 | pi = proto_tree_add_string(hdr_tree, hf_http_citrix_domain, tvb, offset, 0, ""); |
4138 | } |
4139 | proto_item_set_generated(pi); |
4140 | value += data_len + 1; |
4141 | offset += data_len + 1; |
4142 | } |
4143 | } |
4144 | if(strncmp(value, "; password=\"", 12) == 0) { |
4145 | value += 12; |
4146 | offset += 12; |
4147 | ch_ptr = strchr(value, '"'); |
4148 | if ( ch_ptr != NULL((void*)0) ) { |
4149 | data_len = (int)(ch_ptr - value); |
4150 | if (data_len) { |
4151 | data_tvb = base64_tvb_to_new_tvb(tvb, offset, data_len); |
4152 | add_new_data_source(pinfo, data_tvb, "Password"); |
4153 | pi = proto_tree_add_item_ret_string(hdr_tree, hf_http_citrix_passwd, data_tvb, 0, tvb_reported_length(data_tvb), ENC_UTF_80x00000002, pinfo->pool, &passwd); |
4154 | } else { |
4155 | pi = proto_tree_add_string(hdr_tree, hf_http_citrix_passwd, tvb, offset, 0, ""); |
4156 | } |
4157 | proto_item_set_generated(pi); |
4158 | value += data_len + 1; |
4159 | offset += data_len + 1; |
4160 | } |
4161 | } |
4162 | if(strncmp(value, "; AGESessionId=\"", 16) == 0) { |
4163 | value += 16; |
4164 | offset += 16; |
4165 | ch_ptr = strchr(value, '"'); |
4166 | if ( ch_ptr != NULL((void*)0) ) { |
4167 | data_len = (int)(ch_ptr - value); |
4168 | if (data_len) { |
4169 | data_tvb = base64_tvb_to_new_tvb(tvb, offset, data_len); |
4170 | add_new_data_source(pinfo, data_tvb, "Session ID"); |
4171 | pi = proto_tree_add_item(hdr_tree, hf_http_citrix_session, data_tvb, 0, tvb_reported_length(data_tvb), ENC_UTF_80x00000002); |
4172 | } else { |
4173 | pi = proto_tree_add_string(hdr_tree, hf_http_citrix_session, tvb, |
4174 | offset, 0, ""); |
4175 | } |
4176 | proto_item_set_generated(pi); |
4177 | } |
4178 | } |
4179 | if (user != NULL((void*)0) && passwd != NULL((void*)0)) { |
4180 | |
4181 | tap_credential_t* auth = wmem_new0(pinfo->pool, tap_credential_t)((tap_credential_t*)wmem_alloc0((pinfo->pool), sizeof(tap_credential_t ))); |
4182 | |
4183 | auth->username = wmem_strdup(pinfo->pool, user); |
4184 | auth->proto = "HTTP CitrixAGBasic auth"; |
4185 | auth->num = auth->username_num = pinfo->num; |
4186 | auth->password_hf_id = hf_http_citrix_passwd; |
4187 | tap_queue_packet(credentials_tap, pinfo, auth); |
4188 | } |
4189 | return true1; |
4190 | } |
4191 | } |
4192 | return false0; |
4193 | } |
4194 | |
4195 | static bool_Bool |
4196 | check_auth_kerberos(proto_item *hdr_item, tvbuff_t *tvb, packet_info *pinfo, const char *value) |
4197 | { |
4198 | proto_tree *hdr_tree; |
4199 | |
4200 | if (strncmp(value, "Kerberos ", 9) == 0) { |
4201 | if (hdr_item != NULL((void*)0)) { |
4202 | hdr_tree = proto_item_add_subtree(hdr_item, ett_http_kerberos); |
4203 | } else |
4204 | hdr_tree = NULL((void*)0); |
4205 | |
4206 | dissect_http_kerberos(tvb, pinfo, hdr_tree, value); |
4207 | return true1; |
4208 | } |
4209 | return false0; |
4210 | } |
4211 | |
4212 | static int |
4213 | dissect_http_on_stream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
4214 | http_conv_t *conv_data, bool_Bool end_of_stream, const uint32_t *seq) |
4215 | { |
4216 | intoffset = 0; |
4217 | intlen = 0; |
4218 | |
4219 | while (tvb_reported_length_remaining(tvb, offset) > 0) { |
4220 | /* Switch protocol if the data starts after response headers. */ |
4221 | if (conv_data->startframe && |
4222 | (pinfo->num > conv_data->startframe || |
4223 | (pinfo->num == conv_data->startframe && offset >= conv_data->startoffset))) { |
4224 | /* Increase pinfo->can_desegment because we are traversing |
4225 | * http and want to preserve desegmentation functionality for |
4226 | * the proxied protocol |
4227 | */ |
4228 | if (pinfo->can_desegment > 0) |
4229 | pinfo->can_desegment++; |
4230 | if (conv_data->next_handle) { |
4231 | call_dissector_only(conv_data->next_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL((void*)0)); |
4232 | } else { |
4233 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), pinfo, tree); |
4234 | } |
4235 | /* |
4236 | * If a subdissector requests reassembly, be sure not to |
4237 | * include the preceding HTTP headers. |
4238 | */ |
4239 | if (pinfo->desegment_len) { |
4240 | pinfo->desegment_offset += offset; |
4241 | } |
4242 | break; |
4243 | } |
4244 | len = dissect_http_message(tvb, offset, pinfo, tree, conv_data, "HTTP", proto_http, end_of_stream, seq); |
4245 | if (len < 0) |
4246 | break; |
4247 | offset += len; |
4248 | |
4249 | /* |
4250 | * OK, we've set the Protocol and Info columns for the |
4251 | * first HTTP message; set a fence so that subsequent |
4252 | * HTTP messages don't overwrite the Info column. |
4253 | */ |
4254 | col_set_fence(pinfo->cinfo, COL_INFO); |
4255 | } |
4256 | /* dissect_http_message() returns -2 if message is not valid HTTP */ |
4257 | return (len == -2) |
4258 | ? 0 |
4259 | : (int)tvb_captured_length(tvb); |
4260 | } |
4261 | |
4262 | static int |
4263 | dissect_http_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) |
4264 | { |
4265 | struct tcpinfo *tcpinfo = (struct tcpinfo *)data; |
4266 | conversation_t *conversation; |
4267 | http_conv_t *conv_data; |
4268 | bool_Bool end_of_stream; |
4269 | |
4270 | conv_data = get_http_conversation_data(pinfo, &conversation); |
4271 | |
4272 | /* Call HTTP2 dissector directly when detected via heuristics, but not |
4273 | * when it was upgraded (the conversation started with HTTP). */ |
4274 | if (conversation_get_proto_data(conversation, proto_http2) && |
4275 | !conv_data->startframe) { |
4276 | if (pinfo->can_desegment > 0) |
4277 | pinfo->can_desegment++; |
4278 | return call_dissector_only(http2_handle, tvb, pinfo, tree, data); |
4279 | } |
4280 | |
4281 | /* |
4282 | * Check if this is proxied connection and if so, hand of dissection to the |
4283 | * payload-dissector. |
4284 | * Response code 200 means "OK" and strncmp() == 0 means the strings match exactly */ |
4285 | http_req_res_t *curr_req_res = conv_data->req_res_tail; |
4286 | if(pinfo->num >= conv_data->startframe && |
4287 | curr_req_res && |
4288 | curr_req_res->response_code == 200 && |
4289 | curr_req_res->request_method && |
4290 | strncmp(curr_req_res->request_method, "CONNECT", 7) == 0 && |
4291 | curr_req_res->request_uri) { |
4292 | if (conv_data->startframe == 0 && !PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) { |
4293 | conv_data->startframe = pinfo->num; |
4294 | conv_data->startoffset = 0; |
4295 | copy_address_wmem(wmem_file_scope(), &conv_data->server_addr, &pinfo->dst); |
4296 | conv_data->server_port = pinfo->destport; |
4297 | } |
4298 | http_payload_subdissector(tvb, tree, pinfo, conv_data, data); |
4299 | |
4300 | return tvb_captured_length(tvb); |
4301 | } |
4302 | |
4303 | /* XXX - how to detect end-of-stream without tcpinfo */ |
4304 | end_of_stream = (tcpinfo && IS_TH_FIN(tcpinfo->flags)(tcpinfo->flags & 0x0001)); |
4305 | return dissect_http_on_stream(tvb, pinfo, tree, conv_data, end_of_stream, tcpinfo ? &tcpinfo->seq : NULL((void*)0)); |
4306 | } |
4307 | |
4308 | static bool_Bool |
4309 | dissect_http_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
4310 | { |
4311 | int offset = 0, next_offset, linelen; |
4312 | conversation_t *conversation; |
4313 | |
4314 | |
4315 | /* Check if we have a line terminated by CRLF |
4316 | * Return the length of the line (not counting the line terminator at |
4317 | * the end), or, if we don't find a line terminator: |
4318 | * |
4319 | *if "deseg" is true, return -1; |
4320 | */ |
4321 | linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, true1); |
4322 | if((linelen == -1)||(linelen == 8)){ |
4323 | return false0; |
4324 | } |
4325 | |
4326 | /* Check if the line start or ends with the HTTP token */ |
4327 | if((tvb_strncaseeql(tvb, linelen-8, "HTTP/1.", 7) == 0)||(tvb_strncaseeql(tvb, 0, "HTTP/1.", 7) == 0)){ |
4328 | conversation = find_or_create_conversation(pinfo); |
4329 | conversation_set_dissector_from_frame_number(conversation, pinfo->num, http_tcp_handle); |
4330 | dissect_http_tcp(tvb, pinfo, tree, data); |
4331 | return true1; |
4332 | } |
4333 | |
4334 | return false0; |
4335 | } |
4336 | |
4337 | static int |
4338 | dissect_http_tls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U___attribute__((unused))) |
4339 | { |
4340 | conversation_t *conversation; |
4341 | http_conv_t *conv_data; |
4342 | bool_Bool end_of_stream; |
4343 | |
4344 | conv_data = get_http_conversation_data(pinfo, &conversation); |
4345 | |
4346 | struct tlsinfo *tlsinfo = (struct tlsinfo *)data; |
4347 | end_of_stream = (tlsinfo && tlsinfo->end_of_stream); |
4348 | return dissect_http_on_stream(tvb, pinfo, tree, conv_data, end_of_stream, tlsinfo ? &tlsinfo->seq : NULL((void*)0)); |
4349 | } |
4350 | |
4351 | static bool_Bool |
4352 | dissect_http_heur_tls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
4353 | { |
4354 | int offset = 0, next_offset, linelen; |
4355 | conversation_t *conversation; |
4356 | http_conv_t*conv_data; |
4357 | |
4358 | conversation = find_or_create_conversation(pinfo); |
4359 | conv_data = (http_conv_t *)conversation_get_proto_data(conversation, proto_http); |
4360 | /* A http conversation was previously started, assume it is still active */ |
4361 | if (conv_data) { |
4362 | dissect_http_tls(tvb, pinfo, tree, data); |
4363 | return true1; |
4364 | } |
4365 | |
4366 | /* Check if we have a line terminated by CRLF |
4367 | * Return the length of the line (not counting the line terminator at |
4368 | * the end), or, if we don't find a line terminator: |
4369 | * |
4370 | *if "deseg" is true, return -1; |
4371 | */ |
4372 | linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, true1); |
4373 | if((linelen == -1)||(linelen == 8)){ |
4374 | return false0; |
4375 | } |
4376 | |
4377 | /* Check if the line start or ends with the HTTP token */ |
4378 | if((tvb_strncaseeql(tvb, linelen-8, "HTTP/1.", 7) != 0) && (tvb_strncaseeql(tvb, 0, "HTTP/1.", 7) != 0)) { |
4379 | /* we couldn't find the Magic Hello HTTP/1.X. */ |
4380 | return false0; |
4381 | } |
4382 | |
4383 | dissect_http_tls(tvb, pinfo, tree, data); |
4384 | return true1; |
4385 | } |
4386 | |
4387 | static int |
4388 | dissect_http_sctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U___attribute__((unused))) |
4389 | { |
4390 | conversation_t *conversation; |
4391 | http_conv_t *conv_data; |
4392 | |
4393 | conv_data = get_http_conversation_data(pinfo, &conversation); |
4394 | |
4395 | /* |
4396 | * XXX - we need to provide an end-of-stream indication. |
4397 | */ |
4398 | return dissect_http_on_stream(tvb, pinfo, tree, conv_data, false0, NULL((void*)0)); |
4399 | } |
4400 | |
4401 | static int |
4402 | dissect_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U___attribute__((unused))) |
4403 | { |
4404 | conversation_t *conversation; |
4405 | http_conv_t *conv_data; |
4406 | |
4407 | conv_data = get_http_conversation_data(pinfo, &conversation); |
4408 | |
4409 | /* |
4410 | * XXX - what should be done about reassembly, pipelining, etc. |
4411 | * here? |
4412 | */ |
4413 | return dissect_http_on_stream(tvb, pinfo, tree, conv_data, false0, NULL((void*)0)); |
4414 | } |
4415 | |
4416 | static int |
4417 | dissect_ssdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U___attribute__((unused))) |
4418 | { |
4419 | conversation_t *conversation; |
4420 | http_conv_t*conv_data; |
4421 | |
4422 | conv_data = get_http_conversation_data(pinfo, &conversation); |
4423 | dissect_http_message(tvb, 0, pinfo, tree, conv_data, "SSDP", proto_ssdp, false0, NULL((void*)0)); |
4424 | return tvb_captured_length(tvb); |
4425 | } |
4426 | |
4427 | static void |
4428 | range_delete_http_tls_callback(uint32_t port, void *ptr _U___attribute__((unused))) { |
4429 | ssl_dissector_delete(port, http_tls_handle); |
4430 | } |
4431 | |
4432 | static void |
4433 | range_add_http_tls_callback(uint32_t port, void *ptr _U___attribute__((unused))) { |
4434 | ssl_dissector_add(port, http_tls_handle); |
4435 | } |
4436 | |
4437 | static void reinit_http(void) { |
4438 | http_tcp_range = prefs_get_range_value("http", "tcp.port"); |
4439 | |
4440 | http_sctp_range = prefs_get_range_value("http", "sctp.port"); |
4441 | |
4442 | range_foreach(http_tls_range, range_delete_http_tls_callback, NULL((void*)0)); |
4443 | wmem_free(wmem_epan_scope(), http_tls_range); |
4444 | http_tls_range = range_copy(wmem_epan_scope(), global_http_tls_range); |
4445 | range_foreach(http_tls_range, range_add_http_tls_callback, NULL((void*)0)); |
4446 | } |
4447 | |
4448 | void |
4449 | proto_register_http(void) |
4450 | { |
4451 | static hf_register_info hf[] = { |
4452 | { &hf_http_notification, |
4453 | { "Notification", "http.notification", |
4454 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0, |
4455 | "true if HTTP notification", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4456 | { &hf_http_response, |
4457 | { "Response", "http.response", |
4458 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0, |
4459 | "true if HTTP response", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4460 | { &hf_http_request, |
4461 | { "Request", "http.request", |
4462 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0, |
4463 | "true if HTTP request", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4464 | { &hf_http_basic, |
4465 | { "Credentials", "http.authbasic", |
4466 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4467 | { &hf_http_citrix, |
4468 | { "Citrix AG Auth", "http.authcitrix", |
4469 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0, |
4470 | "true if CitrixAGBasic Auth", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4471 | { &hf_http_citrix_user, |
4472 | { "Citrix AG Username", "http.authcitrix.user", |
4473 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4474 | { &hf_http_citrix_domain, |
4475 | { "Citrix AG Domain", "http.authcitrix.domain", |
4476 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4477 | { &hf_http_citrix_passwd, |
4478 | { "Citrix AG Password", "http.authcitrix.password", |
4479 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4480 | { &hf_http_citrix_session, |
4481 | { "Citrix AG Session ID", "http.authcitrix.session", |
4482 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4483 | { &hf_http_response_line, |
4484 | { "Response line", "http.response.line", |
4485 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4486 | { &hf_http_request_line, |
4487 | { "Request line", "http.request.line", |
4488 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4489 | { &hf_http_request_method, |
4490 | { "Request Method", "http.request.method", |
4491 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4492 | "HTTP Request Method", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4493 | { &hf_http_request_uri, |
4494 | { "Request URI", "http.request.uri", |
4495 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4496 | "HTTP Request-URI", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4497 | { &hf_http_request_path, |
4498 | { "Request URI Path", "http.request.uri.path", |
4499 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4500 | "HTTP Request-URI Path", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4501 | { &hf_http_request_path_segment, |
4502 | { "Request URI Path Segment", "http.request.uri.path.segment", |
4503 | FT_STRING, BASE_NONE, NULL((void*)0), 0, |
4504 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } }, |
4505 | { &hf_http_request_query, |
4506 | { "Request URI Query", "http.request.uri.query", |
4507 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4508 | "HTTP Request-URI Query", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4509 | { &hf_http_request_query_parameter, |
4510 | { "Request URI Query Parameter", "http.request.uri.query.parameter", |
4511 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4512 | "HTTP Request-URI Query Parameter", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4513 | { &hf_http_request_version, |
4514 | { "Request Version", "http.request.version", |
4515 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4516 | "HTTP Request HTTP-Version", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4517 | { &hf_http_response_version, |
4518 | { "Response Version", "http.response.version", |
4519 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4520 | "HTTP Response HTTP-Version", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4521 | { &hf_http_request_full_uri, |
4522 | { "Full request URI", "http.request.full_uri", |
4523 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4524 | "The full requested URI (including host name)", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4525 | { &hf_http_response_code, |
4526 | { "Status Code", "http.response.code", |
4527 | FT_UINT24, BASE_DEC, NULL((void*)0), 0x0, |
4528 | "HTTP Response Status Code", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4529 | { &hf_http_response_code_desc, |
4530 | { "Status Code Description", "http.response.code.desc", |
4531 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4532 | "HTTP Response Status Code Description", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4533 | { &hf_http_response_phrase, |
4534 | { "Response Phrase", "http.response.phrase", |
4535 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4536 | "HTTP Response Reason Phrase", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4537 | { &hf_http_authorization, |
4538 | { "Authorization", "http.authorization", |
4539 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4540 | "HTTP Authorization header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4541 | { &hf_http_proxy_authenticate, |
4542 | { "Proxy-Authenticate", "http.proxy_authenticate", |
4543 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4544 | "HTTP Proxy-Authenticate header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4545 | { &hf_http_proxy_authorization, |
4546 | { "Proxy-Authorization", "http.proxy_authorization", |
4547 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4548 | "HTTP Proxy-Authorization header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4549 | { &hf_http_proxy_connect_host, |
4550 | { "Proxy-Connect-Hostname", "http.proxy_connect_host", |
4551 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4552 | "HTTP Proxy Connect Hostname", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4553 | { &hf_http_proxy_connect_port, |
4554 | { "Proxy-Connect-Port", "http.proxy_connect_port", |
4555 | FT_UINT16, BASE_DEC, NULL((void*)0), 0x0, |
4556 | "HTTP Proxy Connect Port", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4557 | { &hf_http_www_authenticate, |
4558 | { "WWW-Authenticate", "http.www_authenticate", |
4559 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4560 | "HTTP WWW-Authenticate header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4561 | { &hf_http_content_type, |
4562 | { "Content-Type", "http.content_type", |
4563 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4564 | "HTTP Content-Type header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4565 | { &hf_http_content_length_header, |
4566 | { "Content-Length", "http.content_length_header", |
4567 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4568 | "HTTP Content-Length header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4569 | { &hf_http_content_length, |
4570 | { "Content length", "http.content_length", |
4571 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0, |
4572 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4573 | { &hf_http_content_encoding, |
4574 | { "Content-Encoding", "http.content_encoding", |
4575 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4576 | "HTTP Content-Encoding header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4577 | { &hf_http_transfer_encoding, |
4578 | { "Transfer-Encoding", "http.transfer_encoding", |
4579 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4580 | "HTTP Transfer-Encoding header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4581 | { &hf_http_upgrade, |
4582 | { "Upgrade", "http.upgrade", |
4583 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4584 | "HTTP Upgrade header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4585 | { &hf_http_user_agent, |
4586 | { "User-Agent", "http.user_agent", |
4587 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4588 | "HTTP User-Agent header", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4589 | { &hf_http_host, |
4590 | { "Host", "http.host", |
4591 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4592 | "HTTP Host", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4593 | { &hf_http_range, |
4594 | { "Range", "http.range", |
4595 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4596 | "HTTP Range", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4597 | { &hf_http_content_range, |
4598 | { "Content-Range", "http.content_range", |
4599 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4600 | "HTTP Content-Range", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4601 | { &hf_http_connection, |
4602 | { "Connection", "http.connection", |
4603 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4604 | "HTTP Connection", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4605 | { &hf_http_cookie, |
4606 | { "Cookie", "http.cookie", |
4607 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4608 | "HTTP Cookie", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4609 | { &hf_http_cookie_pair, |
4610 | { "Cookie pair", "http.cookie_pair", |
4611 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4612 | "A name/value HTTP cookie pair", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4613 | { &hf_http_accept, |
4614 | { "Accept", "http.accept", |
4615 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4616 | "HTTP Accept", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4617 | { &hf_http_referer, |
4618 | { "Referer", "http.referer", |
4619 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4620 | "HTTP Referer", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4621 | { &hf_http_accept_language, |
4622 | { "Accept-Language", "http.accept_language", |
4623 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4624 | "HTTP Accept Language", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4625 | { &hf_http_accept_encoding, |
4626 | { "Accept Encoding", "http.accept_encoding", |
4627 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4628 | "HTTP Accept Encoding", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4629 | { &hf_http_date, |
4630 | { "Date", "http.date", |
4631 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4632 | "HTTP Date", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4633 | { &hf_http_cache_control, |
4634 | { "Cache-Control", "http.cache_control", |
4635 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4636 | "HTTP Cache Control", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4637 | { &hf_http_server, |
4638 | { "Server", "http.server", |
4639 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4640 | "HTTP Server", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4641 | { &hf_http_location, |
4642 | { "Location", "http.location", |
4643 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4644 | "HTTP Location", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4645 | { &hf_http_sec_websocket_accept, |
4646 | { "Sec-WebSocket-Accept", "http.sec_websocket_accept", |
4647 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4648 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4649 | { &hf_http_sec_websocket_extensions, |
4650 | { "Sec-WebSocket-Extensions", "http.sec_websocket_extensions", |
4651 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4652 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4653 | { &hf_http_sec_websocket_key, |
4654 | { "Sec-WebSocket-Key", "http.sec_websocket_key", |
4655 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4656 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4657 | { &hf_http_sec_websocket_protocol, |
4658 | { "Sec-WebSocket-Protocol", "http.sec_websocket_protocol", |
4659 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4660 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4661 | { &hf_http_sec_websocket_version, |
4662 | { "Sec-WebSocket-Version", "http.sec_websocket_version", |
4663 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4664 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4665 | { &hf_http_set_cookie, |
4666 | { "Set-Cookie", "http.set_cookie", |
4667 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4668 | "HTTP Set Cookie", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4669 | { &hf_http_last_modified, |
4670 | { "Last-Modified", "http.last_modified", |
4671 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4672 | "HTTP Last Modified", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4673 | { &hf_http_x_forwarded_for, |
4674 | { "X-Forwarded-For", "http.x_forwarded_for", |
4675 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4676 | "HTTP X-Forwarded-For", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4677 | { &hf_http_http2_settings, |
4678 | { "HTTP2-Settings", "http.http2_settings", |
4679 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0, |
4680 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4681 | { &hf_http_request_in, |
4682 | { "Request in frame", "http.request_in", |
4683 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST)((gpointer) (glong) (FT_FRAMENUM_REQUEST)), 0, |
4684 | "This packet is a response to the packet with this number", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4685 | { &hf_http_response_in, |
4686 | { "Response in frame", "http.response_in", |
4687 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE)((gpointer) (glong) (FT_FRAMENUM_RESPONSE)), 0, |
4688 | "This packet will be responded in the packet with this number", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4689 | { &hf_http_time, |
4690 | { "Time since request", "http.time", |
4691 | FT_RELATIVE_TIME, BASE_NONE, NULL((void*)0), 0, |
4692 | "Time since the request was sent", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4693 | { &hf_http_chunked_trailer_part, |
4694 | { "trailer-part", "http.chunked_trailer_part", |
4695 | FT_STRING, BASE_NONE, NULL((void*)0), 0, |
4696 | "Optional trailer in a chunked body", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4697 | { &hf_http_chunk_boundary, |
4698 | { "Chunk boundary", "http.chunk_boundary", |
4699 | FT_BYTES, BASE_NONE, NULL((void*)0), 0, |
4700 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4701 | { &hf_http_chunk_size, |
4702 | { "Chunk size", "http.chunk_size", |
4703 | FT_UINT32, BASE_DEC|BASE_UNIT_STRING0x00001000, &units_octet_octets, 0, |
4704 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4705 | { &hf_http_chunk_data, |
4706 | { "Chunk data", "http.chunk_data", |
4707 | FT_BYTES, BASE_NONE, NULL((void*)0), 0, |
4708 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4709 | { &hf_http_file_data, |
4710 | { "File Data", "http.file_data", |
4711 | FT_BYTES, BASE_NONE, NULL((void*)0), 0, |
4712 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4713 | { &hf_http_unknown_header, |
4714 | { "Unknown header", "http.unknown_header", |
4715 | FT_STRING, BASE_NONE, NULL((void*)0), 0, |
4716 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4717 | { &hf_http_http2_settings_uri, |
4718 | { "HTTP2 Settings URI", "http.http2_settings_uri", |
4719 | FT_BYTES, BASE_NONE, NULL((void*)0), 0, |
4720 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
4721 | |
4722 | /* Body fragments */ |
4723 | REASSEMBLE_INIT_HF_ITEMS(http_body, "HTTP Chunked Body", "http.body"){ &hf_http_body_fragments, { "Reassembled " "HTTP Chunked Body" " fragments", "http.body" ".fragments", FT_NONE, BASE_NONE, ( (void*)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0) } }, { &hf_http_body_fragment, { "HTTP Chunked Body" " fragment", "http.body" ".fragment", FT_FRAMENUM, BASE_NONE , ((void*)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ( (void*)0) } }, { &hf_http_body_fragment_overlap, { "HTTP Chunked Body" " fragment overlap", "http.body" ".fragment.overlap", FT_BOOLEAN , BASE_NONE, ((void*)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0) } }, { &hf_http_body_fragment_overlap_conflicts , { "HTTP Chunked Body" " fragment overlapping with conflicting data" , "http.body" ".fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE , ((void*)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ( (void*)0) } }, { &hf_http_body_fragment_multiple_tails, { "HTTP Chunked Body" " has multiple tail fragments", "http.body" ".fragment.multiple_tails", FT_BOOLEAN, BASE_NONE, ((void*)0 ), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) } }, { &hf_http_body_fragment_too_long_fragment, { "HTTP Chunked Body" " fragment too long", "http.body" ".fragment.too_long_fragment" , FT_BOOLEAN, BASE_NONE, ((void*)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0) } }, { &hf_http_body_fragment_error, { "HTTP Chunked Body" " defragment error", "http.body" ".fragment.error", FT_FRAMENUM , BASE_NONE, ((void*)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0) } }, { &hf_http_body_fragment_count, { "HTTP Chunked Body" " fragment count", "http.body" ".fragment.count", FT_UINT32, BASE_DEC, ((void*)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0) } }, { &hf_http_body_reassembled_in, { "Reassembled in" , "http.body" ".reassembled.in", FT_FRAMENUM, BASE_NONE, ((void *)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void*)0 ) } }, { &hf_http_body_reassembled_length, { "Reassembled length" , "http.body" ".reassembled.length", FT_UINT32, BASE_DEC, ((void *)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void*)0 ) } }, { &hf_http_body_reassembled_data, { "Reassembled data" , "http.body" ".reassembled.data", FT_BYTES, BASE_NONE, ((void *)0), 0x0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void*)0 ) } }, { &hf_http_body_segment, { "HTTP Chunked Body" " segment" , "http.body" ".segment", FT_BYTES, BASE_NONE, ((void*)0), 0x0 , ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void*)0)} }, |
4724 | }; |
4725 | static int *ett[] = { |
4726 | &ett_http, |
4727 | &ett_http_ntlmssp, |
4728 | &ett_http_kerberos, |
4729 | &ett_http_request, |
4730 | &ett_http_request_uri, |
4731 | &ett_http_request_path, |
4732 | &ett_http_request_query, |
4733 | &ett_http_chunked_response, |
4734 | &ett_http_chunk_data, |
4735 | &ett_http_encoded_entity, |
4736 | &ett_http_header_item, |
4737 | &ett_http_http2_settings_item, |
4738 | REASSEMBLE_INIT_ETT_ITEMS(http_body)&ett_http_body_fragment, &ett_http_body_fragments, |
4739 | }; |
4740 | |
4741 | static ei_register_info ei[] = { |
4742 | { &ei_http_te_and_length, { "http.te_and_length", PI_MALFORMED0x07000000, PI_WARN0x00600000, "The Content-Length and Transfer-Encoding header must not be set together", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, |
4743 | { &ei_http_te_unknown, { "http.te_unknown", PI_UNDECODED0x05000000, PI_WARN0x00600000, "Unknown transfer coding name in Transfer-Encoding header", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, |
4744 | { &ei_http_subdissector_failed, { "http.subdissector_failed", PI_MALFORMED0x07000000, PI_NOTE0x00400000, "HTTP body subdissector failed, trying heuristic subdissector", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, |
4745 | { &ei_http_tls_port, { "http.tls_port", PI_SECURITY0x0a000000, PI_WARN0x00600000, "Unencrypted HTTP protocol detected over encrypted port, could indicate a dangerous misconfiguration.", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, |
4746 | { &ei_http_excess_data, { "http.excess_data", PI_PROTOCOL0x09000000, PI_WARN0x00600000, "Excess data after a body (not a new request/response), previous Content-Length bogus?", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, |
4747 | { &ei_http_leading_crlf, { "http.leading_crlf", PI_MALFORMED0x07000000, PI_ERROR0x00800000, "Leading CRLF previous message in the stream may have extra CRLF", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, |
4748 | { &ei_http_bad_header_name, { "http.bad_header_name", PI_PROTOCOL0x09000000, PI_WARN0x00600000, "Illegal characters found in header name", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, |
4749 | { &ei_http_decompression_failed, { "http.decompression_failed", PI_UNDECODED0x05000000, PI_WARN0x00600000, "Decompression failed", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }}, |
4750 | { &ei_http_decompression_disabled, { "http.decompression_disabled", PI_UNDECODED0x05000000, PI_CHAT0x00200000, "Decompression disabled", EXPFILL0, ((void*)0), 0, {0, {((void*)0), ((void*)0), FT_NONE, BASE_NONE , ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE, -1, ((void *)0)}} }} |
4751 | |
4752 | }; |
4753 | |
4754 | /* UAT for header fields */ |
4755 | static uat_field_t custom_header_uat_fields[] = { |
4756 | UAT_FLD_CSTRING(header_fields, header_name, "Header name", "HTTP header name"){"header_name", "Header name", PT_TXTMOD_STRING,{uat_fld_chk_str ,header_fields_header_name_set_cb,header_fields_header_name_tostr_cb },{0,0,0},0,"HTTP header name",((void*)0)}, |
4757 | UAT_FLD_CSTRING(header_fields, header_desc, "Field desc", "Description of the value contained in the header"){"header_desc", "Field desc", PT_TXTMOD_STRING,{uat_fld_chk_str ,header_fields_header_desc_set_cb,header_fields_header_desc_tostr_cb },{0,0,0},0,"Description of the value contained in the header" ,((void*)0)}, |
4758 | UAT_END_FIELDS{((void*)0),((void*)0),PT_TXTMOD_NONE,{0,0,0},{0,0,0},0,0,((void *)0)} |
4759 | }; |
4760 | |
4761 | module_t *http_module; |
4762 | expert_module_t* expert_http; |
4763 | uat_t* headers_uat; |
4764 | |
4765 | proto_http = proto_register_protocol("Hypertext Transfer Protocol", "HTTP", "http"); |
4766 | proto_ssdp = proto_register_protocol("Simple Service Discovery Protocol", "SSDP", "ssdp"); |
4767 | |
4768 | proto_register_field_array(proto_http, hf, array_length(hf)(sizeof (hf) / sizeof (hf)[0])); |
4769 | proto_register_subtree_array(ett, array_length(ett)(sizeof (ett) / sizeof (ett)[0])); |
4770 | expert_http = expert_register_protocol(proto_http); |
4771 | expert_register_field_array(expert_http, ei, array_length(ei)(sizeof (ei) / sizeof (ei)[0])); |
4772 | |
4773 | http_handle = register_dissector("http", dissect_http, proto_http); |
4774 | http_tcp_handle = register_dissector("http-over-tcp", dissect_http_tcp, proto_http); |
4775 | http_tls_handle = register_dissector("http-over-tls", dissect_http_tls, proto_http); /* RFC 2818 */ |
4776 | http_sctp_handle = register_dissector("http-over-sctp", dissect_http_sctp, proto_http); |
4777 | |
4778 | reassembly_table_register(&http_streaming_reassembly_table, &addresses_ports_reassembly_table_functions); |
4779 | |
4780 | http_module = prefs_register_protocol(proto_http, reinit_http); |
4781 | prefs_register_bool_preference(http_module, "desegment_headers", |
4782 | "Reassemble HTTP headers spanning multiple TCP segments", |
4783 | "Whether the HTTP dissector should reassemble headers " |
4784 | "of a request spanning multiple TCP segments. " |
4785 | "To use this option, you must also enable " |
4786 | "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", |
4787 | &http_desegment_headers); |
4788 | prefs_register_bool_preference(http_module, "desegment_body", |
4789 | "Reassemble HTTP bodies spanning multiple TCP segments", |
4790 | "Whether the HTTP dissector should use the " |
4791 | "\"Content-length:\" value, if present, to reassemble " |
4792 | "the body of a request spanning multiple TCP segments, " |
4793 | "and reassemble chunked data spanning multiple TCP segments. " |
4794 | "To use this option, you must also enable " |
4795 | "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", |
4796 | &http_desegment_body); |
4797 | prefs_register_bool_preference(http_module, "dechunk_body", |
4798 | "Reassemble chunked transfer-coded bodies", |
4799 | "Whether to reassemble bodies of entities that are transferred " |
4800 | "using the \"Transfer-Encoding: chunked\" method", |
4801 | &http_dechunk_body); |
4802 | #if defined(HAVE_ZLIB1) || defined(HAVE_ZLIBNG) || defined(HAVE_BROTLI1) |
4803 | prefs_register_bool_preference(http_module, "decompress_body", |
4804 | "Uncompress entity bodies", |
4805 | "Whether to uncompress entity bodies that are compressed " |
4806 | "using \"Content-Encoding: \"", |
4807 | &http_decompress_body); |
4808 | #endif |
4809 | prefs_register_bool_preference(http_module, "check_ascii_headers", |
4810 | "Reject non-ASCII headers as invalid HTTP", |
4811 | "Whether to treat non-ASCII in headers as non-HTTP data " |
4812 | "and allow other dissectors to process it", |
4813 | &http_check_ascii_headers); |
4814 | prefs_register_obsolete_preference(http_module, "tcp_alternate_port"); |
4815 | |
4816 | range_convert_str(wmem_epan_scope(), &global_http_tls_range, TLS_DEFAULT_RANGE"443", 65535); |
4817 | prefs_register_range_preference(http_module, "tls.port", "SSL/TLS Ports", |
4818 | "SSL/TLS Ports range", |
4819 | &global_http_tls_range, 65535); |
4820 | prefs_register_obsolete_preference(http_module, "ssl.port"); |
4821 | /* UAT */ |
4822 | headers_uat = uat_new("Custom HTTP Header Fields", |
4823 | sizeof(header_field_t), |
4824 | "custom_http_header_fields", |
4825 | true1, |
4826 | &header_fields, |
4827 | &num_header_fields, |
4828 | /* specifies named fields, so affects dissection |
4829 | and the set of named fields */ |
4830 | UAT_AFFECTS_DISSECTION0x00000001|UAT_AFFECTS_FIELDS0x00000002, |
4831 | NULL((void*)0), |
4832 | header_fields_copy_cb, |
4833 | header_fields_update_cb, |
4834 | header_fields_free_cb, |
4835 | header_fields_post_update_cb, |
4836 | header_fields_reset_cb, |
4837 | custom_header_uat_fields |
4838 | ); |
4839 | |
4840 | prefs_register_uat_preference(http_module, "custom_http_header_fields", "Custom HTTP header fields", |
4841 | "A table to define custom HTTP header for which fields can be setup and used for filtering/data extraction etc.", |
4842 | headers_uat); |
4843 | |
4844 | /* |
4845 | * Dissectors shouldn't register themselves in this table; |
4846 | * instead, they should call "http_tcp_dissector_add()", and |
4847 | * we'll register the port number they specify as a port |
4848 | * for HTTP, and register them in our subdissector table. |
4849 | * |
4850 | * This only works for protocols such as IPP that run over |
4851 | * HTTP on a specific non-HTTP port. |
4852 | */ |
4853 | port_subdissector_table = register_dissector_table("http.port", |
4854 | "TCP port for protocols using HTTP", proto_http, FT_UINT16, BASE_DEC); |
4855 | |
4856 | /* |
4857 | * Maps the lowercase Upgrade header value. |
4858 | * https://tools.ietf.org/html/rfc7230#section-8.6 |
4859 | */ |
4860 | upgrade_subdissector_table = register_dissector_table("http.upgrade", "HTTP Upgrade", proto_http, FT_STRING, STRING_CASE_SENSITIVE0); |
4861 | |
4862 | /* |
4863 | * Heuristic dissectors SHOULD register themselves in |
4864 | * this table using the standard heur_dissector_add() |
4865 | * function. |
4866 | */ |
4867 | heur_subdissector_list = register_heur_dissector_list_with_description("http", "HTTP payload fallback", proto_http); |
4868 | |
4869 | /* |
4870 | * Register for tapping |
4871 | */ |
4872 | http_tap = register_tap("http"); /* HTTP statistics tap */ |
4873 | http_follow_tap = register_tap("http_follow"); /* HTTP Follow tap */ |
4874 | credentials_tap = register_tap("credentials"); /* credentials tap */ |
4875 | |
4876 | register_follow_stream(proto_http, "http_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter, |
4877 | tcp_port_to_display, follow_tvb_tap_listener, |
4878 | get_tcp_stream_count, NULL((void*)0)); |
4879 | http_eo_tap = register_export_object(proto_http, http_eo_packet, NULL((void*)0)); |
4880 | |
4881 | /* compile patterns, excluding "/" */ |
4882 | ws_mempbrk_compile(&pbrk_gen_delims, ":?#[]@"); |
4883 | /* exclude "=", separating key and value should be done separately */ |
4884 | ws_mempbrk_compile(&pbrk_sub_delims, "!$&'()*+,;"); |
4885 | |
4886 | } |
4887 | |
4888 | /* |
4889 | * Called by dissectors for protocols that run atop HTTP/TCP. |
4890 | */ |
4891 | void |
4892 | http_tcp_dissector_add(uint32_t port, dissector_handle_t handle) |
4893 | { |
4894 | /* |
4895 | * Register ourselves as the handler for that port number |
4896 | * over TCP. "Auto-preference" not needed |
4897 | */ |
4898 | dissector_add_uint("tcp.port", port, http_tcp_handle); |
4899 | |
4900 | /* |
4901 | * And register them in *our* table for that port. |
4902 | */ |
4903 | dissector_add_uint("http.port", port, handle); |
4904 | } |
4905 | |
4906 | WS_DLL_PUBLIC__attribute__ ((visibility ("default"))) extern |
4907 | void http_tcp_dissector_delete(uint32_t port) |
4908 | { |
4909 | /* |
4910 | * Unregister ourselves as the handler for that port number |
4911 | * over TCP. "Auto-preference" not needed |
4912 | */ |
4913 | dissector_delete_uint("tcp.port", port, NULL((void*)0)); |
4914 | |
4915 | /* |
4916 | * And unregister them in *our* table for that port. |
4917 | */ |
4918 | dissector_delete_uint("http.port", port, NULL((void*)0)); |
4919 | } |
4920 | |
4921 | void |
4922 | http_tcp_port_add(uint32_t port) |
4923 | { |
4924 | /* |
4925 | * Register ourselves as the handler for that port number |
4926 | * over TCP. We rely on our caller having registered |
4927 | * themselves for the appropriate media type. |
4928 | * No "auto-preference" used. |
4929 | */ |
4930 | dissector_add_uint("tcp.port", port, http_tcp_handle); |
4931 | } |
4932 | |
4933 | void |
4934 | proto_reg_handoff_http(void) |
4935 | { |
4936 | dissector_handle_t ssdp_handle; |
4937 | |
4938 | media_handle = find_dissector_add_dependency("media", proto_http); |
4939 | http2_handle = find_dissector("http2"); |
4940 | /* |
4941 | * XXX - is there anything to dissect in the body of an SSDP |
4942 | * request or reply? I.e., should there be an SSDP dissector? |
4943 | */ |
4944 | ssdp_handle = create_dissector_handle(dissect_ssdp, proto_ssdp); |
4945 | dissector_add_uint_with_preference("udp.port", UDP_PORT_SSDP1900, ssdp_handle); |
4946 | |
4947 | /* |
4948 | * TLS Application-Layer Protocol Negotiation (ALPN) protocol ID. |
4949 | */ |
4950 | dissector_add_string("tls.alpn", "http/1.1", http_tls_handle); |
4951 | |
4952 | ntlmssp_handle = find_dissector_add_dependency("ntlmssp", proto_http); |
4953 | gssapi_handle = find_dissector_add_dependency("gssapi", proto_http); |
4954 | sstp_handle = find_dissector_add_dependency("sstp", proto_http); |
4955 | |
4956 | stats_tree_cfg *st_config; |
4957 | st_config = stats_tree_register("http", "http", "HTTP" STATS_TREE_MENU_SEPARATOR"//" "Packet Counter", 0, http_stats_tree_packet, http_stats_tree_init, NULL((void*)0) ); |
4958 | stats_tree_set_first_column_name(st_config, "Packet Type"); |
4959 | 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((void*)0) ); |
4960 | stats_tree_set_first_column_name(st_config, "Request Type"); |
4961 | 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((void*)0) ); |
4962 | stats_tree_set_first_column_name(st_config, "Packet Type"); |
4963 | 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((void*)0) ); |
4964 | stats_tree_set_first_column_name(st_config, "Sequence Type"); |
4965 | |
4966 | dissector_add_uint("acdr.tls_application_port", 443, http_handle); |
4967 | dissector_add_uint("acdr.tls_application", TLS_APP_HTTP, http_handle); |
4968 | dissector_add_uint("acdr.tls_application", TLS_APP_TR069, http_handle); |
4969 | dissector_add_uint("ippusb", 0, http_tcp_handle); |
4970 | } |
4971 | |
4972 | /* |
4973 | * Content-Type: message/http |
4974 | */ |
4975 | |
4976 | static int proto_message_http; |
4977 | static int ett_message_http; |
4978 | |
4979 | static int |
4980 | dissect_message_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U___attribute__((unused))) |
4981 | { |
4982 | proto_tree*subtree; |
4983 | proto_item*ti; |
4984 | intoffset = 0, next_offset; |
4985 | intlen; |
4986 | |
4987 | col_append_str(pinfo->cinfo, COL_INFO, " (message/http)"); |
4988 | if (tree) { |
4989 | ti = proto_tree_add_item(tree, proto_message_http, |
4990 | tvb, 0, -1, ENC_NA0x00000000); |
4991 | subtree = proto_item_add_subtree(ti, ett_message_http); |
4992 | while (tvb_offset_exists(tvb, offset)) { |
4993 | len = tvb_find_line_end(tvb, offset, |
4994 | tvb_ensure_captured_length_remaining(tvb, offset), |
4995 | &next_offset, false0); |
4996 | if (len == -1) |
4997 | break; |
4998 | proto_tree_add_format_text(subtree, tvb, offset, len); |
4999 | offset = next_offset; |
5000 | } |
5001 | } |
5002 | return tvb_captured_length(tvb); |
5003 | } |
5004 | |
5005 | void |
5006 | proto_register_message_http(void) |
5007 | { |
5008 | static int *ett[] = { |
5009 | &ett_message_http, |
5010 | }; |
5011 | |
5012 | proto_message_http = proto_register_protocol("Media Type: message/http", "message/http", "message-http"); |
5013 | proto_register_subtree_array(ett, array_length(ett)(sizeof (ett) / sizeof (ett)[0])); |
5014 | } |
5015 | |
5016 | void |
5017 | proto_reg_handoff_message_http(void) |
5018 | { |
5019 | dissector_handle_t message_http_handle; |
5020 | |
5021 | message_http_handle = create_dissector_handle(dissect_message_http, |
5022 | proto_message_http); |
5023 | |
5024 | dissector_add_string("media_type", "message/http", message_http_handle); |
5025 | |
5026 | heur_dissector_add("tcp", dissect_http_heur_tcp, "HTTP over TCP", "http_tcp", proto_http, HEURISTIC_ENABLE); |
5027 | heur_dissector_add("tls", dissect_http_heur_tls, "HTTP over TLS", "http_tls", proto_http, HEURISTIC_ENABLE); |
5028 | |
5029 | proto_http2 = proto_get_id_by_filter_name("http2"); |
5030 | |
5031 | dissector_add_uint_range_with_preference("tcp.port", TCP_DEFAULT_RANGE"80,3128,3132,5985,8080,8088,11371,1900,2869,2710", http_tcp_handle); |
5032 | dissector_add_uint_range_with_preference("sctp.port", SCTP_DEFAULT_RANGE"80", http_sctp_handle); |
5033 | |
5034 | /* |
5035 | * Get the content type and Internet media type table |
5036 | */ |
5037 | media_type_subdissector_table = find_dissector_table("media_type"); |
5038 | |
5039 | streaming_content_type_dissector_table = find_dissector_table("streaming_content_type"); |
5040 | |
5041 | reinit_http(); |
5042 | } |
5043 | |
5044 | /* |
5045 | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
5046 | * |
5047 | * Local variables: |
5048 | * c-basic-offset: 8 |
5049 | * tab-width: 8 |
5050 | * indent-tabs-mode: t |
5051 | * End: |
5052 | * |
5053 | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
5054 | * :indentSize=8:tabSize=8:noTabs=false: |
5055 | */ |