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