### Bug Summary

File: | builds/wireshark/wireshark/epan/dissectors/packet-http3.c
Warning: | line 1769, column 13 Value stored to 'datalen' is never read

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
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-http3.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 -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -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-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan/dissectors -isystem /builds/wireshark/wireshark/build/epan/dissectors -isystem /usr/include/mit-krb5 -isystem /usr/include/libxml2 -isystem /builds/wireshark/wireshark/epan -D CARES_NO_DEPRECATED -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 -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../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-format-nonliteral -std=gnu17 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-07-08-100337-3595-1 -x c /builds/wireshark/wireshark/epan/dissectors/packet-http3.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* packet-http3.c
2 | * Routines for HTTP/3 dissection
3 | * Copyright 2019, Peter Wu <peter@lekensteyn.nl>
4 | * Copyright 2023, Omer Shapira <oesh@github.com>
5 | *
6 | * Wireshark - Network traffic analyzer
7 | * By Gerald Combs <gerald@wireshark.org>
8 | * Copyright 1998 Gerald Combs
9 | *
10 | * SPDX-License-Identifier: GPL-2.0-or-later
11 | */
12 |
13 | /*
14 | * https://tools.ietf.org/html/draft-ietf-quic-http-29
15 | * https://tools.ietf.org/html/draft-ietf-quic-qpack-16
16 | *
17 | * Depends on the QUIC dissector for providing a reassembled stream of data, see
18 | * packet-quic.c for details about supported QUIC draft versions.
19 | * Depends on nghttp3 for HTTP header dissection.
20 | * Currently supported HTTP/3 versions: h3-23 up to and including h3-29.
21 | */
22 |
23 | #include "config.h"
24 | #define WS_LOG_DOMAIN"HTTP3" "HTTP3"
25 |
26 | #include <stdint.h>
27 | #include <string.h>
28 |
29 | #include <epan/wmem_scopes.h>
30 | #include <epan/addr_resolv.h>
31 | #include <epan/charsets.h>
32 | #include <epan/conversation_table.h>
33 | #include <epan/decode_as.h>
34 | #include <epan/exceptions.h>
35 | #include <epan/expert.h>
36 | #include <epan/follow.h>
37 | #include <epan/packet.h>
38 | #include <epan/proto_data.h>
39 | #include <epan/reassemble.h>
40 | #include <epan/to_str.h>
41 | #include <epan/uat.h>
42 |
43 | #include <epan/dissectors/packet-http3.h>
44 | #include <epan/dissectors/packet-http.h> /* for getting status reason-phrase */
45 | #include <epan/dissectors/packet-media-type.h>
46 | #include <epan/dissectors/packet-quic.h>
47 | #include <epan/dissectors/packet-udp.h>
48 |
49 | #include <wsutil/pint.h>
50 | #include <wsutil/str_util.h>
51 | #include <wsutil/ws_assert.h>
52 | #include <wsutil/zlib_compat.h>
53 |
54 | #ifdef HAVE_NGHTTP31
55 | #include <nghttp3/nghttp3.h>
56 | #include <epan/export_object.h>
57 | #endif
58 |
59 | void proto_reg_handoff_http3(void);
60 | void proto_register_http3(void);
61 |
62 | static dissector_handle_t http3_handle;
63 | static dissector_handle_t http3_datagram_handle;
64 |
65 | static int http3_follow_tap;
66 |
67 | #ifdef HAVE_NGHTTP31
68 | static reassembly_table http3_body_reassembly_table;
69 |
70 | static dissector_table_t media_type_dissector_table;
71 |
72 | static dissector_handle_t media_handle;
73 |
74 | static int http_eo_tap;
75 |
76 | /*
77 | * Decompression of content-encoded entities.
78 | */
79 | #if defined(HAVE_ZLIB1) || defined(HAVE_ZLIBNG) || defined(HAVE_BROTLI1) || defined(HAVE_ZSTD1)
80 | static bool_Bool http3_decompress_body = true1;
81 | #else
82 | static bool_Bool http3_decompress_body;
83 | #endif
84 |
85 | #endif /* HAVE_NGHTTP3 */
86 |
87 | #define PROTO_DATA_KEY_HEADER0 0
88 | #define PROTO_DATA_KEY_QPACK1 1
89 |
90 | static int proto_http3;
91 | static int hf_http3_stream_uni;
92 | static int hf_http3_stream_uni_type;
93 | static int hf_http3_stream_bidi;
94 | static int hf_http3_stream_id;
95 | static int hf_http3_push_id;
96 | static int hf_http3_frame;
97 | static int hf_http3_frame_type;
98 | static int hf_http3_frame_length;
99 | static int hf_http3_frame_payload;
100 |
101 | static int hf_http3_time;
102 | static int hf_http3_request_in;
103 | static int hf_http3_response_in;
104 |
105 | static int hf_http3_data;
106 | static int hf_http3_encoded_entity;
107 | static int hf_http3_body_fragments;
108 | static int hf_http3_body_fragment;
109 | static int hf_http3_body_fragment_overlap;
110 | static int hf_http3_body_fragment_overlap_conflicts;
111 | static int hf_http3_body_fragment_multiple_tails;
112 | static int hf_http3_body_fragment_too_long_fragment;
113 | static int hf_http3_body_fragment_error;
114 | static int hf_http3_body_fragment_count;
115 | static int hf_http3_body_reassembled_in;
116 | static int hf_http3_body_reassembled_length;
117 | static int hf_http3_body_reassembled_data;
118 |
119 | static int hf_http3_headers_count;
120 | static int hf_http3_header;
121 | static int hf_http3_headers_decoded_length;
122 | static int hf_http3_header_name_length;
123 | static int hf_http3_header_name;
124 | static int hf_http3_header_value_length;
125 | static int hf_http3_header_value;
126 | static int hf_http3_header_request_full_uri;
127 |
128 | static int hf_http3_header_qpack_blocked;
129 | static int hf_http3_header_qpack_blocked_stream_rcint;
130 | static int hf_http3_header_qpack_blocked_decoder_wicnt;
131 |
132 | #ifdef HAVE_NGHTTP31
133 | /* Static HTTP3 headers */
134 | static int hf_http3_headers_status;
135 | static int hf_http3_headers_path;
136 | static int hf_http3_headers_protocol;
137 | static int hf_http3_headers_method;
138 | static int hf_http3_headers_scheme;
139 | static int hf_http3_headers_accept;
140 | static int hf_http3_headers_accept_charset;
141 | static int hf_http3_headers_accept_encoding;
142 | static int hf_http3_headers_accept_language;
143 | static int hf_http3_headers_accept_ranges;
144 | static int hf_http3_headers_access_control_allow_origin;
145 | static int hf_http3_headers_age;
146 | static int hf_http3_headers_allow;
147 | static int hf_http3_headers_authorization;
148 | static int hf_http3_headers_authority;
149 | static int hf_http3_headers_cache_control;
150 | static int hf_http3_headers_content_disposition;
151 | static int hf_http3_headers_content_encoding;
152 | static int hf_http3_headers_content_language;
153 | static int hf_http3_headers_content_length;
154 | static int hf_http3_headers_content_location;
155 | static int hf_http3_headers_content_range;
156 | static int hf_http3_headers_content_type;
157 | static int hf_http3_headers_cookie;
158 | static int hf_http3_headers_date;
159 | static int hf_http3_headers_etag;
160 | static int hf_http3_headers_expect;
161 | static int hf_http3_headers_expires;
162 | static int hf_http3_headers_from;
163 | static int hf_http3_headers_if_match;
164 | static int hf_http3_headers_if_modified_since;
165 | static int hf_http3_headers_if_none_match;
166 | static int hf_http3_headers_if_range;
167 | static int hf_http3_headers_if_unmodified_since;
168 | static int hf_http3_headers_last_modified;
169 | static int hf_http3_headers_link;
170 | static int hf_http3_headers_location;
171 | static int hf_http3_headers_max_forwards;
172 | static int hf_http3_headers_proxy_authenticate;
173 | static int hf_http3_headers_proxy_authorization;
174 | static int hf_http3_headers_range;
175 | static int hf_http3_headers_referer;
176 | static int hf_http3_headers_refresh;
177 | static int hf_http3_headers_retry_after;
178 | static int hf_http3_headers_server;
179 | static int hf_http3_headers_set_cookie;
180 | static int hf_http3_headers_strict_transport_security;
181 | static int hf_http3_headers_user_agent;
182 | static int hf_http3_headers_vary;
183 | static int hf_http3_headers_via;
184 | static int hf_http3_headers_www_authenticate;
185 | #endif
186 |
187 | static int hf_http3_qpack_encoder;
188 | static int hf_http3_qpack_encoder_icnt;
189 | static int hf_http3_qpack_encoder_icnt_inc;
190 | static int hf_http3_qpack_encoder_opcode_insert_indexed;
191 | static int hf_http3_qpack_encoder_opcode_insert_indexed_ref;
192 | static int hf_http3_qpack_encoder_opcode_insert_indexed_val;
193 | static int hf_http3_qpack_encoder_opcode_insert_indexed_hval;
194 | static int hf_http3_qpack_encoder_opcode_insert;
195 | static int hf_http3_qpack_encoder_opcode_insert_name;
196 | static int hf_http3_qpack_encoder_opcode_insert_hname;
197 | static int hf_http3_qpack_encoder_opcode_insert_val;
198 | static int hf_http3_qpack_encoder_opcode_insert_hval;
199 | static int hf_http3_qpack_encoder_opcode_duplicate;
200 | static int hf_http3_qpack_encoder_opcode_dtable_cap;
201 | static int hf_http3_qpack_encoder_opcode_dtable_cap_val;
202 |
203 | static int hf_http3_qpack_decoder;
204 | static int hf_http3_qpack_decoder_opcode_section_ack;
205 | static int hf_http3_qpack_decoder_opcode_section_ack_stream_id;
206 | static int hf_http3_qpack_decoder_opcode_cancel_stream;
207 | static int hf_http3_qpack_decoder_opcode_cancel_stream_id;
208 | static int hf_http3_qpack_decoder_opcode_icnt_increment;
209 | static int hf_http3_qpack_decoder_opcode_icnt_increment_value;
210 |
211 | static int hf_http3_settings;
212 | static int hf_http3_settings_identifier;
213 | static int hf_http3_settings_value;
214 | static int hf_http3_settings_qpack_max_table_capacity;
215 | static int hf_http3_settings_max_field_section_size;
216 | static int hf_http3_settings_qpack_blocked_streams;
217 | static int hf_http3_settings_extended_connect;
218 | static int hf_http3_settings_webtransport;
219 | static int hf_http3_settings_h3_datagram;
220 | static int hf_http3_settings_h3_datagram_draft04;
221 | static int hf_http3_priority_update_element_id;
222 | static int hf_http3_priority_update_field_value;
223 |
224 | static int hf_http3_datagram;
225 | static int hf_http3_datagram_quarter_stream_id;
226 | static int hf_http3_datagram_request_stream_id;
227 | static int hf_http3_datagram_payload;
228 |
229 | static expert_field ei_http3_qpack_failed;
230 | static expert_field ei_http3_prefix_int_failed;
231 | static expert_field ei_http3_huffman_failed;
232 | /* HTTP3 dissection EIs */
233 | static expert_field ei_http3_unknown_stream_type;
234 | /* Encoded data EIs */
235 | static expert_field ei_http3_header_encoded_state;
236 | /* HTTP3 header decoding EIs */
237 | static expert_field ei_http3_header_decoding_failed;
238 | static expert_field ei_http3_header_decoding_blocked;
239 | static expert_field ei_http3_header_decoding_no_output;
240 | static expert_field ei_http3_header_size;
241 | static expert_field ei_http3_header_transfer_encoding;
242 | /* HTTP3 body decoding EIs */
243 | static expert_field ei_http3_body_decompression_failed;
244 | /* HTTP3 datagram prefix EIs */
245 | static expert_field ei_http3_datagram_invalid_stream_id;
246 |
247 | /* Initialize the subtree pointers */
248 | static int ett_http3;
249 | static int ett_http3_stream_uni;
250 | static int ett_http3_stream_bidi;
251 | static int ett_http3_frame;
252 | static int ett_http3_body_fragment;
253 | static int ett_http3_body_fragments;
254 | static int ett_http3_encoded_entity;
255 | static int ett_http3_settings;
256 | static int ett_http3_headers;
257 | static int ett_http3_headers_qpack_blocked;
258 | static int ett_http3_qpack_update;
259 | static int ett_http3_qpack_opcode;
260 | static int ett_http3_datagram;
261 | static int ett_http3_datagram_stream_id;
262 |
263 | #ifdef HAVE_NGHTTP31
264 | static const fragment_items http3_body_fragment_items = {
265 | /* Fragment subtrees */
266 | &ett_http3_body_fragment,
267 | &ett_http3_body_fragments,
268 | /* Fragment fields */
269 | &hf_http3_body_fragments,
270 | &hf_http3_body_fragment,
271 | &hf_http3_body_fragment_overlap,
272 | &hf_http3_body_fragment_overlap_conflicts,
273 | &hf_http3_body_fragment_multiple_tails,
274 | &hf_http3_body_fragment_too_long_fragment,
275 | &hf_http3_body_fragment_error,
276 | &hf_http3_body_fragment_count,
277 | &hf_http3_body_reassembled_in,
278 | &hf_http3_body_reassembled_length,
279 | &hf_http3_body_reassembled_data,
280 | "Body fragments"
281 | };
282 | #endif
283 |
284 | /**
285 | * HTTP3 header constants.
286 | * The below constants are used for dissecting the
287 | * code. This is not an exhaustive list.
288 | */
289 | #define HTTP3_HEADER_NAME_CONTENT_ENCODING"content-encoding" "content-encoding"
290 | #define HTTP3_HEADER_NAME_CONTENT_TYPE"content-type" "content-type"
291 | #define HTTP3_HEADER_NAME_CONTENT_LENGTH"content-length" "content-length"
292 | #define HTTP3_HEADER_NAME_TRANSFER_ENCODING"transfer-encoding" "transfer-encoding"
293 | #define HTTP3_HEADER_NAME_AUTHORITY":authority" ":authority"
294 | #define HTTP3_HEADER_NAME_METHOD":method" ":method"
295 | #define HTTP3_HEADER_NAME_PATH":path" ":path"
296 | #define HTTP3_HEADER_NAME_PROTOCOL":protocol" ":protocol"
297 | #define HTTP3_HEADER_NAME_SCHEME":scheme" ":scheme"
298 | #define HTTP3_HEADER_NAME_STATUS":status" ":status"
299 |
300 | #define HTTP3_HEADER_METHOD_CONNECT"CONNECT" "CONNECT"
301 | #define HTTP3_HEADER_STATUS_PARTIAL_CONTENT"206" "206"
302 |
303 | #define HTTP3_HEADER_UNKNOWN"<unknown>" "<unknown>"
304 |
305 | /**
306 | * Unidirectional stream types.
307 | * https://tools.ietf.org/html/draft-ietf-quic-http-29#section-6.2
308 | * https://tools.ietf.org/html/draft-ietf-quic-qpack-16#section-4.2
309 | */
310 | enum http3_stream_type {
311 | HTTP3_STREAM_TYPE_CONTROL,
312 | HTTP3_STREAM_TYPE_PUSH,
313 | HTTP3_STREAM_TYPE_QPACK_ENCODER,
314 | HTTP3_STREAM_TYPE_QPACK_DECODER,
315 | HTTP3_STREAM_TYPE_WEBTRANSPORT = 0x54, // draft-ietf-webtrans-http3-03
316 | };
317 |
318 | /**
319 | * Unidirectional stream types (62-bit code space).
320 | * https://tools.ietf.org/html/draft-ietf-quic-http-29#section-11.2.4
321 | */
322 | // clang-format off
323 | static const val64_string http3_stream_types[] = {
324 | /* 0x00 - 0x3f Assigned via Standards Action or IESG Approval. */
325 | { 0x00, "Control Stream" },
326 | { 0x01, "Push Stream" },
327 | { 0x02, "QPACK Encoder Stream" },
328 | { 0x03, "QPACK Decoder Stream" },
329 | { 0x54, "WebTransport Stream" },
330 | /* 0x40 - 0x3FFFFFFFFFFFFFFF Assigned via Specification Required policy */
331 | { 0, NULL((void*)0) }
332 | };
333 | // clang-format on
334 |
335 | /**
336 | * Frame type codes (62-bit code space).
337 | * https://tools.ietf.org/html/draft-ietf-quic-http-29#section-11.2.1
338 | */
339 | #define HTTP3_DATA0x0 0x0
340 | #define HTTP3_HEADERS0x1 0x1
341 | #define HTTP3_CANCEL_PUSH0x3 0x3
342 | #define HTTP3_SETTINGS0x4 0x4
343 | #define HTTP3_PUSH_PROMISE0x5 0x5
344 | #define HTTP3_GOAWAY0x7 0x7
345 | #define HTTP3_MAX_PUSH_ID0xD 0xD
346 | #define HTTP3_WEBTRANSPORT_BISTREAM0x41 0x41
347 | #define HTTP3_PRIORITY_UPDATE_REQUEST_STREAM0xF0700 0xF0700
348 | #define HTTP3_PRIORITY_UPDATE_PUSH_STREAM0xF0701 0xF0701
349 |
350 | static const val64_string http3_frame_types[] = {
351 | /* 0x00 - 0x3f Assigned via Standards Action or IESG Approval. */
352 | { HTTP3_DATA0x0, "DATA" },
353 | { HTTP3_HEADERS0x1, "HEADERS" },
354 | { 0x02, "Reserved" }, // "PRIORITY" in draft-22 and before
355 | { HTTP3_CANCEL_PUSH0x3, "CANCEL_PUSH" },
356 | { HTTP3_SETTINGS0x4, "SETTINGS" },
357 | { HTTP3_PUSH_PROMISE0x5, "PUSH_PROMISE" },
358 | { 0x06, "Reserved" },
359 | { HTTP3_GOAWAY0x7, "GOAWAY" },
360 | { 0x08, "Reserved" },
361 | { 0x09, "Reserved" },
362 | { HTTP3_MAX_PUSH_ID0xD, "MAX_PUSH_ID" },
363 | { 0x0e, "Reserved" }, // "DUPLICATE_PUSH" in draft-26 and before
364 | /* 0x40 - 0x3FFFFFFFFFFFFFFF Assigned via Specification Required policy */
365 | { HTTP3_WEBTRANSPORT_BISTREAM0x41, "WEBTRANSPORT_BISTREAM" }, // draft-ietf-webtrans-http3-03
366 | { HTTP3_PRIORITY_UPDATE_REQUEST_STREAM0xF0700, "PRIORITY_UPDATE" }, // RFC 9218
367 | { HTTP3_PRIORITY_UPDATE_PUSH_STREAM0xF0701, "PRIORITY_UPDATE" }, // RFC 9218
368 | { 0, NULL((void*)0) }
369 | };
370 |
371 | /**
372 | * Settings parameter type codes (62-bit code space).
373 | * https://tools.ietf.org/html/draft-ietf-quic-http-29#name-http-2-settings-parameters
374 | */
375 | #define HTTP3_QPACK_MAX_TABLE_CAPACITY0x01 0x01
376 | #define HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE0x06 0x06
377 | #define HTTP3_QPACK_BLOCKED_STREAMS0x07 0x07
378 | #define HTTP3_EXTENDED_CONNECT0x08 0x08 /* https://datatracker.ietf.org/doc/draft-ietf-httpbis-h3-websockets */
379 | #define HTTP3_H3_DATAGRAM0x33 0x33 /* rfc9297 */
380 | #define HTTP3_H3_DATAGRAM_DRAFT040xffd277 0xffd277 /* draft-ietf-masque-h3-datagram-04 */
381 | #define HTTP3_WEBTRANSPORT0x2b603742 0x2b603742 /* draft-ietf-webtrans-http3-03 */
382 |
383 | static const val64_string http3_settings_vals[] = {
384 | { HTTP3_QPACK_MAX_TABLE_CAPACITY0x01, "Max Table Capacity" },
385 | { HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE0x06, "Max Field Section Size" },
386 | { HTTP3_QPACK_BLOCKED_STREAMS0x07, "Blocked Streams" },
387 | { HTTP3_EXTENDED_CONNECT0x08, "Extended CONNECT" },
388 | { HTTP3_WEBTRANSPORT0x2b603742, "Enable WebTransport" },
389 | { HTTP3_H3_DATAGRAM0x33, "Enable Datagram" },
390 | { HTTP3_H3_DATAGRAM_DRAFT040xffd277, "Enable Datagram Draft04" },
391 | { 0, NULL((void*)0) }
392 | };
393 |
394 | /**
395 | * QPACK encoder stream opcodes.
396 | */
397 | #define QPACK_OPCODE_MASK0xE0 0xE0
398 | #define QPACK_OPCODE_INSERT_INDEXED0x80 0x80
399 | #define QPACK_OPCODE_INSERT0x40 0x40
400 | #define QPACK_OPCODE_SET_DTABLE_CAP0x20 0x20
401 | #define QPACK_OPCODE_DUPLICATE0x00 0x00
402 |
403 | /**
404 | * QPACK decoder stream opcodes.
405 | */
406 | #define QPACK_OPCODE_SECTION_ACK0x80 0x80
407 | #define QPACK_OPCODE_STREAM_CANCEL0x40 0x40
408 | #define QPACK_OPCODE_ICNT_INCREMENT0x00 0x00
409 |
410 | #define QPACK_HUFFMAN_5_STRING0x20 0x20
411 | #define QPACK_HUFFMAN_6_STRING0x40 0x40
412 | #define QPACK_HUFFMAN_7_STRINGq0x80 q0x80
413 |
414 | typedef enum _http3_stream_dir {
415 | FROM_CLIENT_TO_SERVER = 0,
416 | FROM_SERVER_TO_CLIENT = 1,
417 | } http3_stream_dir;
418 |
419 | /**
420 | * Essential data structures.
421 | */
422 |
423 | /**
424 | * HTTP3 stream info - contains information about HTTP3 stream.
425 | * HTTP3 streams roughly correspond to QUIC streams, with the
426 | * HTTP3 Server Push being an exception to the rule.
427 | */
428 |
429 | /* HTTP/3 pseudo-header fields.
430 | *
431 | * This is a convenience structure that is used
432 | * to collect the values of the HTTP/3 pseudo-headers
433 | * while constructing the protocol tree,
434 | * and to construct the column info afterwards.
435 | * Pseudo-header fields defined for requests MUST NOT appear in responses;
436 | * pseudo-header fields defined for responses MUST NOT appear in requests.
437 | * Pseudo-header fields MUST NOT appear in trailer sections.
438 | * https://www.rfc-editor.org/rfc/rfc9114.html#name-http-control-data
439 | *
440 | * Therefore, we can store these at the bidirectional stream level.
441 | */
442 | typedef struct _http3_pseudo_header_fields {
443 | const char *authority;
444 | const char *method;
445 | const char *path;
446 | const char *protocol;
447 | const char *reason_phrase; /**< "pseudo" pseudo-header. */
448 | const char *scheme;
449 | const char *status;
450 | } http3_pseudo_header_fields_t;
451 |
452 | #define HTTP3_PSEUDO_HEADERS_INITIALIZER(http3_pseudo_header_fields_t){ .authority = ((void*)0), .method = ((void*)0), .path = ((void*)0), .protocol = ((void*)0), .reason_phrase = ((void*)0), .scheme = ((void*)0), .status = ((void*)0), } (http3_pseudo_header_fields_t){ \
453 | .authority = NULL((void*)0), \
454 | .method = NULL((void*)0), \
455 | .path = NULL((void*)0), \
456 | .protocol = NULL((void*)0), \
457 | .reason_phrase = NULL((void*)0), \
458 | .scheme = NULL((void*)0), \
459 | .status = NULL((void*)0), \
460 | }
461 | typedef struct _http3_stream_info {
462 | uint64_t id; /**< HTTP3 stream id */
463 | uint64_t uni_stream_type; /**< Unidirectional stream type */
464 | uint64_t broken_from_offset; /**< Unrecognized stream starting at offset (if non-zero). */
465 | http3_stream_dir direction;
466 | wmem_list_t *request_header_data; /**< List of request header data */
467 | wmem_list_t *response_header_data; /**< List of response header data */
468 | const char *protocol; /**< Protocol from extended CONNECT */
469 | dissector_handle_t next_handle; /**< Dissector for extended CONNECT protocol */
470 | http_upgrade_info_t *upgrade_info; /**< Data for new protocol */
471 | nstime_t request_ts; /**< Timestamp of request first HEADERS frame */
472 | uint32_t request_frame_num; /**< Frame number of request first HEADERS frame */
473 | uint32_t response_frame_num; /**< Frame number of response first HEADERS frame */
474 | bool_Bool is_connect; /**< Method is CONNECT (plain or extended) */
475 | http3_pseudo_header_fields_t pseudo_headers;
476 | } http3_stream_info_t;
477 |
478 | /**
479 | * HTTP3 session info - contains information about the HTTP3 session.
480 | * HTTP3 sessions roughly correspond to QUIC connections, at least
481 | * until the dissector will support connection migration and/or
482 | * Multipath QUIC. When that happens, a single HTTP3 session would
483 | * be mapped to multiple QUIC connections, or to multiple QUIC
484 | * paths (in the MP-QUIC terminology).
485 | */
486 |
487 | typedef void *qpack_decoder_t;
488 | typedef void *qpack_decoder_ctx_t;
489 | typedef struct _http3_session_info {
490 | unsigned id;
491 | qpack_decoder_t qpack_decoder[2]; /**< Decoders for outgoing/incoming QPACK streams. */
492 | http3_stream_info_t *current_stream; /**< Currently processed stream */
493 | } http3_session_info_t;
494 |
495 | /**
496 | * Lookup or create new HTTP3 session object for the pinfo.
497 | */
498 | static http3_session_info_t *http3_session_lookup_or_create(packet_info *pinfo);
499 |
500 | /**
501 | * HTTP3 Header dissection support.
502 | */
503 | #define QPACK_MAX_DTABLE_SIZE65536 65536 /**< Max size of the QPACK dynamic table. */
504 | #define QPACK_MAX_BLOCKED512 512 /**< Upper limit on number of streams blocked on QPACK updates. */
505 |
506 | /**
507 | * Limit the maximum header size to handle legitimate use cases while
508 | * protecting against hostile traffic (in practice, decompression bombs,
509 | * as libnghttp3 puts a limit on the compressed size of a single header.)
510 | *
511 | * Note that server limits tend to be lower than those supported by clients;
512 | * some clients have supported hundreds of MiB of headers (?!) at least in
513 | * the past. Chrome has had a consistent limit of 256 KiB. This is more than
514 | * enough, but still reasonably fast. We shouldn't need to limit the number
515 | * of headers after speeding up tvb_composite.
516 | *
517 | * https://stackoverflow.com/questions/686217/maximum-on-http-header-values
518 | * https://stackoverflow.com/questions/1097651/is-there-a-practical-http-header-length-limit/
519 | * https://stackoverflow.com/questions/3326210/can-http-headers-be-too-big-for-browsers
520 | */
521 | #define QPACK_MAX_HEADER_SIZE1048576 1048576 /**< Max size of decompressed headers (1 MiB) */
522 |
523 | /**
524 | * Header caching scheme
525 | *
526 | * The HTTP/3 headers are sent on the wire in QPACK-encoded form.
527 | * To dissect the headers, Wireshark needs to keep the decoded
528 | * header names and values in memory.
529 | *
530 | * To optimize dissection time, and to conserve memory
531 | * the HTTP/3 dissector keeps all *unique* combinations
532 | * of header-name, header-value in a cache.
533 | *
534 | * The cached values are stored in the "pstr" format:
535 | * name length (uint32_t)
536 | * name
537 | * value length (uint32_t)
538 | * value
539 | */
540 |
541 | /**
542 | * HTTP3 header field.
543 | *
544 | * The header field contains two sections:
545 | * - encoded points to the location of the encoded field in the *original* packet TVB.
546 | * - decoded points to the formatted header string, which is allocated in a cache map,
547 | * to conserve memory.
548 | * The decoded fields are used to create an auxiliary TVB which will
549 | * be used for dissection of decoded header values.
550 | */
551 | typedef struct _http3_header_field {
552 | struct {
553 | unsigned len; /**< Length of the encoded header field. */
554 | unsigned offset; /**< Offset of the encoded header field in the decrypted TVB. */
555 | } encoded;
556 | struct {
557 | const char *bytes; /**< Decoded header field bytes. */
558 | unsigned len; /**< Length of the decoded header field. */
559 | } decoded;
560 | } http3_header_field_t;
561 |
562 | /**
563 | * HTTP3 encoded header data block.
564 | *
565 | * This helper structure is used to support header dissection.
566 | */
567 | typedef struct _header_block_encoded_iter {
568 | uint8_t *bytes;
569 | uint32_t len;
570 | uint32_t pos;
571 | } header_block_encoded_iter_t;
572 |
573 | #define HEADER_BLOCK_ENC_ITER_PTR(hdata)((hdata)->encoded.bytes == ((void*)0) ? ((void*)0) : ((hdata )->encoded.pos == (hdata)->encoded.len) ? ((void*)0) : ( hdata)->encoded.bytes + (hdata)->encoded.pos) \
574 | ((hdata)->encoded.bytes == NULL((void*)0) \
575 | ? NULL((void*)0) \
576 | : ((hdata)->encoded.pos == (hdata)->encoded.len) ? NULL((void*)0) : (hdata)->encoded.bytes + (hdata)->encoded.pos)
577 |
578 | #define HEADER_BLOCK_ENC_ITER_REMAINING(hdata)((hdata)->encoded.bytes == ((void*)0) ? 0 : ((hdata)->encoded .len - (hdata)->encoded.pos)) \
579 | ((hdata)->encoded.bytes == NULL((void*)0) ? 0 : ((hdata)->encoded.len - (hdata)->encoded.pos))
580 |
581 | #define HEADER_BLOCK_ENC_ITER_INC(hdata, nread)do { if ((hdata)) { (hdata)->encoded.pos += (nread); ((void ) (((hdata)->encoded.pos <= (hdata)->encoded.len) ? ( void)0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http3.c", 581, "(hdata)->encoded.pos <= (hdata)->encoded.len" )))) ; } } while (0) \
582 | do { \
583 | if ((hdata)) { \
584 | (hdata)->encoded.pos += (nread); \
585 | DISSECTOR_ASSERT((hdata)->encoded.pos <= (hdata)->encoded.len)((void) (((hdata)->encoded.pos <= (hdata)->encoded.len ) ? (void)0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http3.c", 585, "(hdata)->encoded.pos <= (hdata)->encoded.len" )))); \
586 | } \
587 | } while (0)
588 |
589 | #define HTTP3_HD_DECODER_BLOCKED0x1 0x1
590 | #define HTTP3_HD_DECODER_ERROR0x2 0x2
591 | /**
592 | * HTTP3 header data block.
593 | *
594 | * The data block corresponds to contents of a single HTTP3 HEADERS frame.
595 | * If a packet contains multiple HTTP3 HEADERS frames,
596 | * the corresponding blocks will be chained using the `next'
597 | * pointer. In this case, individual headers blocks
598 | * will be identified by the `offset' field.
599 | */
600 | typedef struct _http3_header_data {
601 | #if 0
602 | /* XXX - This doesn't seem to be used. Would this be any different
603 | * than encoded.len ? */
604 | uint32_t len; /**< Length of the encoded headers block. */
605 | #endif
606 | uint32_t offset; /**< Offset of the headers block in the pinfo TVB. */
607 | int32_t ds_idx; /**< Index of the data source tvb in the pinfo. */
608 | uint16_t state; /**< See HTTP3_HD_DECODER_XXX above */
609 | int16_t error; /**< Decoding error code if any. */
610 | wmem_array_t * header_fields; /**< List of header fields contained in the header block. */
611 | header_block_encoded_iter_t encoded; /**< Used for dissection, not allocated. */
612 | struct _http3_header_data * next; /**< Next pointer in the chain. */
613 | bool_Bool header_size_exceeded; /**< True if decoding halted due to excessive size. */
614 | } http3_header_data_t;
615 |
616 |
617 | #ifdef HAVE_NGHTTP31
618 | #endif /* HAVE_NGHTTP3 */
619 |
620 | /* HTTP3 QPACK encoder state
621 | *
622 | * Store information about how many entries a QPACK encoder stream
623 | * has inserted into the decoder at a particular point in the capture
624 | * file (both the number newly inserted in the portion of the stream
625 | * contained in the current QUIC packet and the total up to that point.)
626 | * If a capture frame contains multiple encoder stream segments, the
627 | * corresponding blocks will be chained using the 'next' pointer. In this
628 | * case, individual blocks will be identified by the data source index
629 | * of the tvb within the capture frame and the offset in the ds_tvb.
630 | * (Both are necessary for multiple QUIC packets coalesced in a single
631 | * UDP datagram with multiple stream segments within a QUIC packet.)
632 | */
633 | typedef struct _http3_qpack_encoder_state {
634 | unsigned offset; /**< Offset of the headers block in the pinfo TVB. */
635 | int32_t ds_idx; /**< Index of the data source tvb in the pinfo. */
636 | uint32_t icnt_inc; /**< Number of insertions in this header segment. */
637 | uint64_t icnt; /**< Total number of insertions up to this point. */
638 | ptrdiff_t nread; /**< Number of bytes read; if negative, an error code. */
639 | struct _http3_qpack_encoder_state * next; /**< Next pointer in the chain. */
640 | } http3_qpack_encoder_state_t;
641 |
642 | /**
643 | * File-scoped context.
644 | * This data structure is used to maintain file-scoped
645 | * lookup tables. It is reset when the file-scoped
646 | * allocator is exited.
647 | */
648 | typedef struct _http3_file_local_ctx {
649 | wmem_map_t *conn_info_map;
650 | #ifdef HAVE_NGHTTP31
651 | wmem_map_t *hdr_cache_map;
652 | #endif
653 | } http3_file_local_ctx;
654 |
655 | /**
656 | * @function http3_get_file_local_ctx
657 | * @abstract Will create a new instance for the first time
658 | * the file is visited.
659 | * This function is not intended to be invoked directly,
660 | * but should be used via the `HTTP3_CONN_INFO_MAP` et. al. below.
661 | * @returns file-local context.
662 | */
663 | static http3_file_local_ctx *http3_get_file_local_ctx(void);
664 |
665 | #define HTTP3_CONN_INFO_MAPhttp3_get_file_local_ctx()->conn_info_map http3_get_file_local_ctx()->conn_info_map
666 |
667 | #ifdef HAVE_NGHTTP31
668 | #define HTTP3_HEADER_CACHEhttp3_get_file_local_ctx()->hdr_cache_map http3_get_file_local_ctx()->hdr_cache_map
669 | #endif
670 |
671 | /**
672 | * Check whether the argument represents a reserved code point,
673 | * for Stream Type, Frame Type, Error Code, etc.
674 | */
675 | static inline bool_Bool
676 | http3_is_reserved_code(uint64_t stream_type)
677 | {
678 | return (stream_type - 0x21) % 0x1f == 0;
679 | }
680 |
681 | /**
682 | * Attempt to parse QUIC-encoded variable integer.
683 | */
684 | static bool_Bool
685 | try_get_quic_varint(tvbuff_t *tvb, int offset, uint64_t *value, int *lenvar)
686 | {
687 | if (tvb_reported_length_remaining(tvb, offset) == 0) {
688 | return false0;
689 | }
690 | unsigned len = 1 << (tvb_get_uint8(tvb, offset) >> 6);
691 | if (tvb_reported_length_remaining(tvb, offset) < len) {
692 | return false0;
693 | }
694 | *lenvar = len;
695 | if (value) {
696 | unsigned n = tvb_get_varint(tvb, offset, -1, value, ENC_VARINT_QUIC0x00000004);
697 | DISSECTOR_ASSERT_CMPINT(n, ==, len)((void) ((n == len) ? (void)0 : (proto_report_dissector_bug("%s:%u: failed assertion " "n" " " "==" " " "len" " (" "%" "l" "d" " " "==" " " "%" "l" "d" ")", "epan/dissectors/packet-http3.c", 697, (int64_t)n, ( int64_t)len))));
698 | }
699 | return true1;
700 | }
701 |
702 | /**
703 | * Return the size of entire HTTP/3 frame.
704 | */
705 | static int
706 | get_http3_frame_size(tvbuff_t *tvb, int offset)
707 | {
708 | int type_size, length_size;
709 | uint64_t frame_length;
710 |
711 | if (!try_get_quic_varint(tvb, offset, NULL((void*)0), &type_size)) {
712 | return 0;
713 | }
714 | offset += type_size;
715 |
716 | if (!try_get_quic_varint(tvb, offset, &frame_length, &length_size)) {
717 | return 0;
718 | }
719 |
720 | uint64_t frame_size = type_size + length_size + frame_length;
721 | if (frame_size > INT32_MAX(2147483647)) {
722 | // We do not support such large frames.
723 | return 0;
724 | }
725 | return (int)frame_size;
726 | }
727 |
728 | /**
729 | * Check whether the pinfo contains at least one whole HTTP3 frame,
730 | * and adjust the pinfo desegmentation settings for the lower
731 | * layer (QUIC, generally) to continue the desegmentation process.
732 | */
733 | static bool_Bool
734 | http3_check_frame_size(tvbuff_t *tvb, packet_info *pinfo, int offset)
735 | {
736 | int frame_size = get_http3_frame_size(tvb, offset);
737 | int remaining = tvb_reported_length_remaining(tvb, offset);
738 | if (frame_size && frame_size <= remaining) {
739 | return true1;
740 | }
741 |
742 | pinfo->desegment_offset = offset;
743 | pinfo->desegment_len = frame_size ? (frame_size - remaining) : DESEGMENT_ONE_MORE_SEGMENT0x0fffffff;
744 | return false0;
745 | }
746 |
747 | static inline http3_stream_dir
748 | http3_packet_get_direction(quic_stream_info *stream_info)
749 | {
750 | return stream_info->from_server
751 | ? FROM_SERVER_TO_CLIENT
752 | : FROM_CLIENT_TO_SERVER;
753 | }
754 |
755 | uint64_t*
756 | http3_get_stream_id(packet_info *pinfo)
757 | {
758 | return p_get_proto_data(pinfo->pool, pinfo, hf_http3_stream_id, 0);
759 | }
760 |
761 | static const char*
762 | http3_get_request_full_uri(packet_info *pinfo, http3_stream_info_t *http3_stream)
763 | {
764 | const char* uri = NULL((void*)0);
765 | if (http3_stream->pseudo_headers.authority) {
766 | /* "All HTTP/3 requests MUST include exactly one value for the :method,
767 | * :scheme, and :path pseudo-header fields, unless the request is a
768 | * CONNECT request[.]"
769 | * https://www.rfc-editor.org/rfc/rfc9114.html#section-4.3.1-3
770 | */
771 | if (http3_stream->is_connect && !http3_stream->protocol) {
772 | /* Both plain CONNECT and CONNECT-UDP use only the `:authority' header.
773 | * https://www.rfc-editor.org/rfc/rfc9114.html#connect */
774 | uri = wmem_strdup(pinfo->pool, http3_stream->pseudo_headers.authority);
775 | } else {
776 | /* The Extended CONNECT uses the standard URL construction
777 | * https://www.rfc-editor.org/rfc/rfc8441.html#section-4 */
778 | uri = wmem_strdup_printf(pinfo->pool, "%s://%s%s",
779 | http3_stream->pseudo_headers.scheme,
780 | http3_stream->pseudo_headers.authority,
781 | http3_stream->pseudo_headers.path);
782 | }
783 | }
784 | return uri;
785 | }
786 |
787 | /**
788 | * Functions to support decompression of HTTP3 headers.
789 | */
790 | #ifdef HAVE_NGHTTP31
791 | /**
792 | * File-scoped callback to release resources allocated for the QPACK
793 | * decoder.
794 | */
795 | static bool_Bool
796 | qpack_decoder_del_cb(wmem_allocator_t *allocator _U___attribute__((unused)), wmem_cb_event_t event _U___attribute__((unused)), void *user_data)
797 | {
798 | nghttp3_qpack_decoder_del((nghttp3_qpack_decoder *)user_data);
799 | return false0;
800 | }
801 |
802 | /**
803 | * Memory allocation callbacks for nghttp3_qpack functionality.
804 | */
805 | static void *
806 | http3_nghttp3_malloc(size_t size, void *user_data _U___attribute__((unused)))
807 | {
808 | return wmem_alloc0(wmem_file_scope(), size);
809 | }
810 |
811 | static void
812 | http3_nghttp3_free(void *ptr, void *user_data _U___attribute__((unused)))
813 | {
814 | wmem_free(wmem_file_scope(), ptr);
815 | }
816 |
817 | static void *
818 | http3_nghttp3_calloc(size_t nmemb, size_t size, void *user_data _U___attribute__((unused)))
819 | {
820 | return wmem_alloc0(wmem_file_scope(), nmemb * size);
821 | }
822 |
823 | static void *
824 | http3_nghttp3_realloc(void *ptr, size_t size, void *user_data _U___attribute__((unused)))
825 | {
826 | return wmem_realloc(wmem_file_scope(), ptr, size);
827 | }
828 |
829 | static nghttp3_mem g_qpack_mem_allocator = {
830 | .malloc = http3_nghttp3_malloc,
831 | .free = http3_nghttp3_free,
832 | .calloc = http3_nghttp3_calloc,
833 | .realloc = http3_nghttp3_realloc,
834 | };
835 |
836 | static nghttp3_mem *
837 | qpack_mem_allocator(wmem_allocator_t *allocator _U___attribute__((unused)), int debug _U___attribute__((unused)))
838 | {
839 | nghttp3_mem *mem;
840 | mem = &g_qpack_mem_allocator;
841 | return mem;
842 | }
843 |
844 | /**
845 | * Initialization routine for the http3_session object.
846 | * Invoked during the creation of the new http3_session.
847 | */
848 | static void
849 | http3_initialize_qpack_decoders(http3_session_info_t *http3_session)
850 | {
851 | for (int dir = 0; dir < 2; dir++) {
852 | nghttp3_qpack_decoder **pdecoder = (nghttp3_qpack_decoder **)&(http3_session->qpack_decoder[dir]);
853 | nghttp3_qpack_decoder_new(pdecoder, QPACK_MAX_DTABLE_SIZE65536, QPACK_MAX_BLOCKED512,
854 | qpack_mem_allocator(wmem_file_scope(), 1));
855 | nghttp3_qpack_decoder_set_max_dtable_capacity(*pdecoder, QPACK_MAX_DTABLE_SIZE65536);
856 | wmem_register_callback(wmem_file_scope(), qpack_decoder_del_cb, *pdecoder);
857 | }
858 | }
859 |
860 | static GHashTable *header_fields_hash;
861 |
862 | static const char *
863 | cid_to_string(const quic_cid_t *cid, wmem_allocator_t *scope)
864 | {
865 | if (cid->len == 0) {
866 | return "(none)";
867 | }
868 | char *str = (char *)wmem_alloc0(scope, 2 * cid->len + 1);
869 | bytes_to_hexstr(str, cid->cid, cid->len);
870 | return str;
871 | }
872 |
873 | static http3_header_data_t *
874 | http3_get_header_data(packet_info *pinfo, tvbuff_t *tvb, unsigned offset)
875 | {
876 | http3_header_data_t *data, *prev = NULL((void*)0);
877 |
878 | unsigned raw_offset = tvb_raw_offset(tvb) + offset;
879 | /* The raw offset is relative to the original data source, which is
880 | * the decrypted QUIC packet. There can be multiple decrypted QUIC
881 | * packets in a single QUIC layer, so this guarantees the same raw
882 | * offset from different decrypted data gives different keys.
883 | */
884 | int32_t ds_idx = get_data_source_index_by_tvb(pinfo, tvb_get_ds_tvb(tvb));
885 | DISSECTOR_ASSERT(ds_idx >= 0)((void) ((ds_idx >= 0) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 885, "ds_idx >= 0"))));
886 |
887 | data = (http3_header_data_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_http3, PROTO_DATA_KEY_HEADER0);
888 |
889 | /*
890 | * Attempt to find existing header data block.
891 | * In most cases, data will be `NULL'
892 | * and this loop won't be visited.
893 | */
894 | while (data != NULL((void*)0)) {
895 | if (data->offset == raw_offset && data->ds_idx == ds_idx) {
896 | /*
897 | * We found the matching data. Return it.
898 | */
899 | return data;
900 | }
901 | prev = data;
902 | data = data->next;
903 | }
904 |
905 | /*
906 | * We did not find header data matching the offset.
907 | * Allocate a new header data block, and initialize
908 | * the offset marker.
909 | */
910 | data = wmem_new0(wmem_file_scope(), http3_header_data_t)((http3_header_data_t*)wmem_alloc0((wmem_file_scope()), sizeof (http3_header_data_t)));
911 | data->offset = raw_offset;
912 | data->ds_idx = ds_idx;
913 |
914 | /*
915 | * Check whether the newly allocated data should be linked
916 | * to the tail of existing header block chain, or whether
917 | * it is the head of a new header block chain.
918 | */
919 | if (prev != NULL((void*)0)) {
920 | prev->next = data;
921 | } else {
922 | p_add_proto_data(wmem_file_scope(), pinfo, proto_http3, PROTO_DATA_KEY_HEADER0, data);
923 | }
924 |
925 | return data;
926 | }
927 |
928 | static http3_qpack_encoder_state_t *
929 | http3_get_qpack_encoder_state(packet_info *pinfo, tvbuff_t *tvb, unsigned offset)
930 | {
931 | http3_qpack_encoder_state_t *data, *prev = NULL((void*)0);
932 |
933 | unsigned raw_offset = tvb_raw_offset(tvb) + offset;
934 | /* The raw offset is relative to the original data source, which is
935 | * the decrypted QUIC packet. There can be multiple decrypted QUIC
936 | * packets in a single QUIC layer, so this guarantees the same raw
937 | * offset from different decrypted data gives different keys.
938 | */
939 | int32_t ds_idx = get_data_source_index_by_tvb(pinfo, tvb_get_ds_tvb(tvb));
940 | DISSECTOR_ASSERT(ds_idx >= 0)((void) ((ds_idx >= 0) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 940, "ds_idx >= 0"))));
941 |
942 | data = (http3_qpack_encoder_state_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_http3, PROTO_DATA_KEY_QPACK1);
943 |
944 | /*
945 | * Attempt to find existing header data block.
946 | * In most cases, data will be `NULL'
947 | * and this loop won't be visited.
948 | */
949 | while (data != NULL((void*)0)) {
950 | if (data->offset == raw_offset && data->ds_idx == ds_idx) {
951 | /*
952 | * We found the matching data. Return it.
953 | */
954 | return data;
955 | }
956 | prev = data;
957 | data = data->next;
958 | }
959 |
960 | /*
961 | * We did not find header data matching the offset.
962 | * Allocate a new header data block, and initialize
963 | * the offset marker.
964 | */
965 | data = wmem_new0(wmem_file_scope(), http3_qpack_encoder_state_t)((http3_qpack_encoder_state_t*)wmem_alloc0((wmem_file_scope() ), sizeof(http3_qpack_encoder_state_t)));
966 | data->offset = raw_offset;
967 | data->ds_idx = ds_idx;
968 |
969 | /*
970 | * Check whether the newly allocated data should be linked
971 | * to the tail of existing header block chain, or whether
972 | * it is the head of a new header block chain.
973 | */
974 | if (prev != NULL((void*)0)) {
975 | prev->next = data;
976 | } else {
977 | p_add_proto_data(wmem_file_scope(), pinfo, proto_http3, PROTO_DATA_KEY_QPACK1, data);
978 | }
979 |
980 | return data;
981 | }
982 |
983 | static proto_item *
984 | try_add_named_header_field(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, uint32_t length,
985 | const char *header_name, const char *header_value)
986 | {
987 | int hf_id;
988 | header_field_info *hfi;
989 | proto_item *ti = NULL((void*)0);
990 |
991 | const int *entry = (const int *)g_hash_table_lookup(header_fields_hash, header_name);
992 | if (entry == NULL((void*)0)) {
993 | return NULL((void*)0);
994 | }
995 |
996 | hf_id = *entry;
997 |
998 | hfi = proto_registrar_get_nth(hf_id);
999 | DISSECTOR_ASSERT(hfi != NULL)((void) ((hfi != ((void*)0)) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 999, "hfi != ((void*)0)"))));
1000 |
1001 | if (FT_IS_UINT32(hfi->type)((hfi->type) == FT_CHAR || (hfi->type) == FT_UINT8 || ( hfi->type) == FT_UINT16 || (hfi->type) == FT_UINT24 || ( hfi->type) == FT_UINT32 || (hfi->type) == FT_FRAMENUM)) {
1002 | uint32_t value;
1003 | if (ws_strtou32(header_value, NULL((void*)0), &value)) {
1004 | ti = proto_tree_add_uint(tree, hf_id, tvb, offset, length, value);
1005 | }
1006 | } else if (FT_IS_UINT(hfi->type)(((hfi->type) == FT_CHAR || (hfi->type) == FT_UINT8 || ( hfi->type) == FT_UINT16 || (hfi->type) == FT_UINT24 || ( hfi->type) == FT_UINT32 || (hfi->type) == FT_FRAMENUM) || ((hfi->type) == FT_UINT40 || (hfi->type) == FT_UINT48 || (hfi->type) == FT_UINT56 || (hfi->type) == FT_UINT64))) {
1007 | uint64_t value;
1008 | if (ws_strtou64(header_value, NULL((void*)0), &value)) {
1009 | ti = proto_tree_add_uint64(tree, hf_id, tvb, offset, length, value);
1010 | }
1011 | } else {
1012 | ti = proto_tree_add_item(tree, hf_id, tvb, offset, length, ENC_BIG_ENDIAN0x00000000);
1013 | }
1014 | if (hf_id == hf_http3_headers_path) {
1015 | http_add_path_components_to_tree(tvb, pinfo, ti, offset, length);
1016 | }
1017 | return ti;
1018 | }
1019 |
1020 | static void
1021 | get_header_field_pstr(wmem_allocator_t *scratch, nghttp3_qpack_nv *header_nv, const char **outp, uint32_t *outlen)
1022 | {
1023 | char *pstr; /* The returned pstr, always from the cache. */
1024 | uint32_t pstr_len; /* The length of `pstr'. */
1025 | nghttp3_vec namev; /* Vector holding the bytes of the field's name. */
1026 | char *name; /* Typed pointer to field's name. */
1027 | uint32_t name_len; /* Field's name length. */
1028 | nghttp3_vec valuev; /* Vector holding the bytes of the field's value. */
1029 | char *value; /* Typed pointer to field's value. */
1030 | uint32_t value_len; /* Field's value length. */
1031 |
1032 | uint8_t *scratch_buffer;
1033 |
1034 | /* Extract the vectors from `header_nv'. */
1035 | namev = nghttp3_rcbuf_get_buf(header_nv->name);
1036 | name = (char *)namev.base;
1037 | name_len = (uint32_t)namev.len;
1038 | valuev = nghttp3_rcbuf_get_buf(header_nv->value);
1039 | value = (char *)valuev.base;
1040 | value_len = (uint32_t)valuev.len;
1041 |
1042 | ws_debug("HTTP header: %.*s: %.*s", name_len, name, value_len, value)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 1042, __func__, "HTTP header: %.*s: %.*s", name_len, name, value_len , value); } } while (0);
1043 |
1044 | /* Construct the pstr in the scratch buffer.
1045 | * The pstr format is described in the "Header caching scheme"
1046 | * comment above.
1047 | */
1048 | pstr_len = (4 + name_len) + (4 + value_len);
1049 | scratch_buffer = (uint8_t *)wmem_alloc(scratch, pstr_len);
1050 | phtonu32(&scratch_buffer[0], name_len);
1051 | memcpy(&scratch_buffer[4], name, name_len);
1052 | phtonu32(&scratch_buffer[4 + name_len], value_len);
1053 | memcpy(&scratch_buffer[4 + name_len + 4], value, value_len);
1054 |
1055 | /* Check whether the pstr is already in the cache,
1056 | * or allocate a new entry. */
1057 | pstr = (char *)wmem_map_lookup(HTTP3_HEADER_CACHEhttp3_get_file_local_ctx()->hdr_cache_map, scratch_buffer);
1058 | if (pstr == NULL((void*)0)) {
1059 | pstr = (char *)wmem_memdup(wmem_file_scope(), scratch_buffer, pstr_len);
1060 | wmem_map_insert(HTTP3_HEADER_CACHEhttp3_get_file_local_ctx()->hdr_cache_map, pstr, pstr);
1061 | }
1062 |
1063 | /* Decrement `nv' reference counts to avoid memory leaks. */
1064 | nghttp3_rcbuf_decref(header_nv->name);
1065 | nghttp3_rcbuf_decref(header_nv->value);
1066 |
1067 | *outp = pstr;
1068 | *outlen = pstr_len;
1069 | }
1070 |
1071 | static const char*
1072 | http3_get_header_value(packet_info *pinfo, const char* name, bool_Bool the_other_direction) {
1073 | wmem_list_t *header_data_list;
1074 | wmem_list_frame_t *frame;
1075 | http3_header_data_t *header_data;
1076 | http3_session_info_t *http3_session = http3_session_lookup_or_create(pinfo);
1077 | http3_stream_info_t *http3_stream = http3_session->current_stream;
1078 |
1079 | if (!http3_stream) {
1080 | return NULL((void*)0);
1081 | }
1082 |
1083 | if ((http3_stream->direction && the_other_direction) || (!http3_stream->direction && !the_other_direction)) {
1084 | header_data_list = http3_stream->request_header_data;
1085 | } else {
1086 | header_data_list = http3_stream->response_header_data;
1087 | }
1088 |
1089 | if (!header_data_list) {
1090 | return NULL((void*)0);
1091 | }
1092 |
1093 | for (frame = wmem_list_head(header_data_list);
1094 | frame;
1095 | frame = wmem_list_frame_next(frame))
1096 | {
1097 | header_data = (http3_header_data_t*)wmem_list_frame_data(frame);
1098 | if (!header_data) {
1099 | continue;
1100 | }
1101 | for (unsigned i = 0; i < wmem_array_get_count(header_data->header_fields); ++i) {
1102 | http3_header_field_t *in;
1103 | uint32_t name_len;
1104 | in = (http3_header_field_t *)wmem_array_index(header_data->header_fields, i);
1105 | name_len = pntohu32(in->decoded.bytes);
1106 | if (strlen(name) == name_len && strncmp(in->decoded.bytes + 4, name, name_len) == 0) {
1107 | return (const char*)get_ascii_string(pinfo->pool,
1108 | (uint8_t*)in->decoded.bytes + 4 + name_len + 4,
1109 | pntohu32(in->decoded.bytes + 4 + name_len));
1110 | }
1111 | }
1112 | }
1113 | return NULL((void*)0);
1114 | }
1115 |
1116 | static void
1117 | populate_http3_header_tracking(packet_info *pinfo _U___attribute__((unused)), http3_stream_info_t *http3_stream,
1118 | const char *header_name, const char *header_value)
1119 | {
1120 | /* HTTP/3 header tracking is simpler than HTTP/2 in that the header
1121 | * section is sent as a single HEADERS frame, and the trailer section
1122 | * as a single HEADERS frame.
1123 | * https://www.rfc-editor.org/rfc/rfc9114.html#section-4.1-4
1124 | * CONTINUATION frames do not exist:
1125 | * https://www.rfc-editor.org/rfc/rfc9114.html#appendix-A.2.5-1.20.1
1126 | * Headers may also be carried on PUSH PROMISE frames, which reference
1127 | * a push ID instead of a server-initiated stream ID. If PUSH_PROMISE
1128 | * is used, if the same push ID occurs in multiple frames, the "header
1129 | * sets MUST contain the same fields in the same order, and both the
1130 | * name and the value in each field MUST be exact matches." Server push
1131 | * is not handled yet.
1132 | *
1133 | * Thus, we could do some more error checking regarding those conditions.
1134 | */
1135 |
1136 | /* There are pseudo-header and header values we wish to save to the
1137 | * bidirectional stream for access across frames, and values we wish
1138 | * to save on a per-direction basis. Right now we don't do the latter,
1139 | * only retrieving them from the full header list, but it might be
1140 | * worth the optimization to pre-process here.
1141 | */
1142 | if (strcmp(header_name, HTTP3_HEADER_NAME_METHOD":method") == 0) {
1143 | http3_stream->pseudo_headers.method = wmem_strdup(wmem_file_scope(), header_value);
1144 | if (strcmp(header_value, "CONNECT") == 0) {
1145 | /* This is a variant of CONNECT method.
1146 | * Supported variants:
1147 | * 1. "Plain CONNECT"
1148 | * https://www.rfc-editor.org/rfc/rfc7231#section-4.3.6
1149 | * 2. "Extended CONNECT"
1150 | * https://www.rfc-editor.org/rfc/rfc9298.html#section-2
1151 | * https://www.rfc-editor.org/rfc/rfc9298.html#section-3.4
1152 | * https://www.rfc-editor.org/rfc/rfc8441.html#section-4
1153 | *
1154 | * "Plain CONNECT" utilizes the `:authority' pseudo-header as
1155 | * the connection target.
1156 | * The "Extended CONNECT" uses the pseudo-headers
1157 | * in the same way as other HTTP methods. */
1158 | http3_stream->is_connect = true1;
1159 | }
1160 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_PROTOCOL":protocol") == 0) {
1161 | http3_stream->pseudo_headers.protocol = wmem_strdup(wmem_file_scope(), header_value);
1162 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_PATH":path") == 0) {
1163 | http3_stream->pseudo_headers.path = wmem_strdup(wmem_file_scope(), header_value);
1164 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_AUTHORITY":authority") == 0) {
1165 | http3_stream->pseudo_headers.authority = wmem_strdup(wmem_file_scope(), header_value);
1166 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_SCHEME":scheme") == 0) {
1167 | http3_stream->pseudo_headers.scheme = wmem_strdup(wmem_file_scope(), header_value);
1168 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_STATUS":status") == 0) {
1169 | unsigned status_code;
1170 |
1171 | status_code = (unsigned)strtoul(header_value, NULL((void*)0), 10);
1172 | http3_stream->pseudo_headers.status = wmem_strdup(wmem_file_scope(), header_value);
1173 | http3_stream->pseudo_headers.reason_phrase = val_to_str_const(status_code, vals_http_status_code, "Unknown");
1174 | }
1175 | }
1176 |
1177 | static int
1178 | decode_http3_headers(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned tvb_offset, unsigned offset,
1179 | quic_stream_info *stream_info, http3_stream_info_t *http3_stream)
1180 | {
1181 | http3_header_data_t *header_data; /* The decoded header data block; populated on the first pass. */
1182 | http3_session_info_t *http3_session; /* The corresponding HTTP/3 session. */
1183 | tvbuff_t *header_tvb; /* Composite TVB containing the decoded header fields. */
1184 | unsigned header_len; /* Total length of the decoded header fields. */
1185 | int hoffset; /* Offset of a decoded header in the decoded TVB */
1186 | proto_item *ti; /* Temporary tree item; used in multiple ways when constructing proto trees. */
1187 | http3_pseudo_header_fields_t pseudo_headers; /* Pseudo-header values; populated when building proto trees; used when creating column info. */
1188 |
1189 | http3_session = http3_session_lookup_or_create(pinfo);
1190 | header_data = http3_get_header_data(pinfo, tvb, offset);
1191 |
1192 | ws_noisy("pdinfo visited=%d", PINFO_FD_VISITED(pinfo))do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 1192, __func__, "pdinfo visited=%d", ((pinfo)->fd->visited )); } } while (0);
1193 |
1194 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
1195 | nghttp3_qpack_decoder *decoder;
1196 | int length = 0;
1197 | http3_stream_dir packet_direction;
1198 |
1199 | /*
1200 | * This packet has not been processed yet, which means this is
1201 | * the first linear scan. We do header decompression only
1202 | * once in linear scan and cache the result. If we don't
1203 | * cache, already processed data will be fed into decompressor
1204 | * again and again since dissector will be called randomly.
1205 | * This makes context out-of-sync.
1206 | */
1207 |
1208 | length = tvb_reported_length_remaining(tvb, tvb_offset);
1209 | packet_direction = http3_packet_get_direction(stream_info);
1210 | decoder = http3_session->qpack_decoder[packet_direction];
1211 |
1212 | DISSECTOR_ASSERT(decoder)((void) ((decoder) ? (void)0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http3.c", 1212, "decoder"))));
1213 | DISSECTOR_ASSERT(header_data)((void) ((header_data) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 1213, "header_data"))));
1214 | DISSECTOR_ASSERT(header_data->encoded.bytes == NULL)((void) ((header_data->encoded.bytes == ((void*)0)) ? (void )0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http3.c", 1214, "header_data->encoded.bytes == ((void*)0)" ))));
1215 | DISSECTOR_ASSERT(header_data->encoded.len == 0)((void) ((header_data->encoded.len == 0) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 1215, "header_data->encoded.len == 0"))));
1216 | DISSECTOR_ASSERT(header_data->header_fields == NULL)((void) ((header_data->header_fields == ((void*)0)) ? (void )0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http3.c", 1216, "header_data->header_fields == ((void*)0)" ))));
1217 |
1218 | header_data->encoded.bytes = tvb_memdup(wmem_file_scope(), tvb, tvb_offset, length);
1219 | header_data->encoded.pos = 0;
1220 | header_data->encoded.len = length;
1221 |
1222 | nghttp3_qpack_stream_context *sctx = NULL((void*)0);
1223 | nghttp3_qpack_stream_context_new(&sctx, http3_stream->id, nghttp3_mem_default());
1224 |
1225 | ws_debug("Header data: %p %d %d", header_data->encoded.bytes, header_data->encoded.pos,do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 1226, __func__, "Header data: %p %d %d", header_data->encoded .bytes, header_data->encoded.pos, header_data->encoded. len); } } while (0)
1226 | header_data->encoded.len)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 1226, __func__, "Header data: %p %d %d", header_data->encoded .bytes, header_data->encoded.pos, header_data->encoded. len); } } while (0);
1227 |
1228 | header_len = 0;
1229 |
1230 | /*
1231 | * Attempt to decode headers.
1232 | *
1233 | * TODO: This may incorrectly put headers that were blocked
1234 | * for packet k in the past to this packet n. We will deal with this later
1235 | */
1236 | while (HEADER_BLOCK_ENC_ITER_REMAINING(header_data)((header_data)->encoded.bytes == ((void*)0) ? 0 : ((header_data )->encoded.len - (header_data)->encoded.pos))) {
1237 | nghttp3_qpack_nv nv;
1238 | uint8_t flags;
1239 |
1240 | if (header_len >= QPACK_MAX_HEADER_SIZE1048576) {
1241 | header_data->header_size_exceeded = true1;
1242 | break;
1243 | }
1244 |
1245 | ws_noisy("%p %p:%d decode decoder=%p sctx=%p", header_data->encoded.bytes,do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 1247, __func__, "%p %p:%d decode decoder=%p sctx=%p", header_data ->encoded.bytes, ((header_data)->encoded.bytes == ((void *)0) ? ((void*)0) : ((header_data)->encoded.pos == (header_data )->encoded.len) ? ((void*)0) : (header_data)->encoded.bytes + (header_data)->encoded.pos), ((header_data)->encoded .bytes == ((void*)0) ? 0 : ((header_data)->encoded.len - ( header_data)->encoded.pos)), decoder, sctx); } } while (0)
1246 | HEADER_BLOCK_ENC_ITER_PTR(header_data),do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 1247, __func__, "%p %p:%d decode decoder=%p sctx=%p", header_data ->encoded.bytes, ((header_data)->encoded.bytes == ((void *)0) ? ((void*)0) : ((header_data)->encoded.pos == (header_data )->encoded.len) ? ((void*)0) : (header_data)->encoded.bytes + (header_data)->encoded.pos), ((header_data)->encoded .bytes == ((void*)0) ? 0 : ((header_data)->encoded.len - ( header_data)->encoded.pos)), decoder, sctx); } } while (0)
1247 | HEADER_BLOCK_ENC_ITER_REMAINING(header_data), decoder, sctx)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 1247, __func__, "%p %p:%d decode decoder=%p sctx=%p", header_data ->encoded.bytes, ((header_data)->encoded.bytes == ((void *)0) ? ((void*)0) : ((header_data)->encoded.pos == (header_data )->encoded.len) ? ((void*)0) : (header_data)->encoded.bytes + (header_data)->encoded.pos), ((header_data)->encoded .bytes == ((void*)0) ? 0 : ((header_data)->encoded.len - ( header_data)->encoded.pos)), decoder, sctx); } } while (0);
1248 |
1249 | int32_t nread = (int32_t)nghttp3_qpack_decoder_read_request(decoder, sctx, &nv, &flags,
1250 | HEADER_BLOCK_ENC_ITER_PTR(header_data)((header_data)->encoded.bytes == ((void*)0) ? ((void*)0) : ((header_data)->encoded.pos == (header_data)->encoded. len) ? ((void*)0) : (header_data)->encoded.bytes + (header_data )->encoded.pos),
1251 | HEADER_BLOCK_ENC_ITER_REMAINING(header_data)((header_data)->encoded.bytes == ((void*)0) ? 0 : ((header_data )->encoded.len - (header_data)->encoded.pos)), 1);
1252 | /*
1253 | * Check for decoding errors.
1254 | */
1255 | if (nread < 0) {
1256 | header_data->state = HTTP3_HD_DECODER_ERROR0x2;
1257 | header_data->error = nread;
1258 | ws_debug("Early return nread=%d err=%s", nread, nghttp3_strerror(nread))do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 1258, __func__, "Early return nread=%d err=%s", nread, nghttp3_strerror (nread)); } } while (0);
1259 | break;
1260 | }
1261 |
1262 | /*
1263 | * Check whether the QPACK decoder is blocked on QPACK encoder stream.
1264 | */
1265 | if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED0x04u) {
1266 | uint64_t wicnt, ricnt;
1267 |
1268 | header_data->state = HTTP3_HD_DECODER_BLOCKED0x1;
1269 | ricnt = nghttp3_qpack_stream_context_get_ricnt(sctx);
1270 | wicnt = nghttp3_qpack_decoder_get_icnt(decoder);
1271 | ws_debug("Early return nread=%d blocked=%" PRIu8 " ricnt=%" PRIu64 " wicnt=%" PRIu64,do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 1272, __func__, "Early return nread=%d blocked=%" "hhu" " ricnt=%" "l" "u" " wicnt=%" "l" "u", nread, flags, ricnt, wicnt); } } while (0)
1272 | nread, flags, ricnt, wicnt)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 1272, __func__, "Early return nread=%d blocked=%" "hhu" " ricnt=%" "l" "u" " wicnt=%" "l" "u", nread, flags, ricnt, wicnt); } } while (0);
1273 | break;
1274 | }
1275 |
1276 | /*
1277 | * Check whether the decoder has emitted header data.
1278 | */
1279 | if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT0x01u) {
1280 | http3_header_field_t *out;
1281 |
1282 | ws_noisy("Emit nread=%d flags=%" PRIu8 "", nread, flags)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 1282, __func__, "Emit nread=%d flags=%" "hhu" "", nread, flags ); } } while (0);
1283 |
1284 | /* Create an output field and add it to the headers array */
1285 | out = wmem_new0(wmem_file_scope(), http3_header_field_t)((http3_header_field_t*)wmem_alloc0((wmem_file_scope()), sizeof (http3_header_field_t)));
1286 |
1287 | /* Populate the `encoded' portion. */
1288 | out->encoded.len = nread;
1289 | out->encoded.offset = header_data->encoded.pos;
1290 |
1291 | /* Populate the `decoded' portion. */
1292 | get_header_field_pstr(pinfo->pool, &nv, &out->decoded.bytes, &out->decoded.len);
1293 |
1294 | /* Add the decoded header field to the header data. */
1295 | if (header_data->header_fields == NULL((void*)0)) {
1296 | header_data->header_fields = wmem_array_new(wmem_file_scope(), sizeof(http3_header_field_t));
1297 | }
1298 | wmem_array_append(header_data->header_fields, out, 1);
1299 |
1300 | header_len += out->decoded.len;
1301 |
1302 | } else {
1303 | proto_tree_add_expert_format(tree, pinfo, &ei_http3_header_decoding_no_output, tvb, tvb_offset, 0,
1304 | "QPACK - nothing emitted decoder %p ctx %p flags %" PRIu8"hhu" " error %d (%s)",
1305 | decoder, sctx, flags, nread, nghttp3_strerror((int)nread));
1306 | }
1307 |
1308 | /*
1309 | * Check whether the QPACK decoder has finished.
1310 | */
1311 | if (nread == 0 || (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL0x02u)) {
1312 | break;
1313 | }
1314 |
1315 | HEADER_BLOCK_ENC_ITER_INC(header_data, nread)do { if ((header_data)) { (header_data)->encoded.pos += (nread ); ((void) (((header_data)->encoded.pos <= (header_data )->encoded.len) ? (void)0 : (proto_report_dissector_bug("%s:%u: failed assertion \"%s\"" , "epan/dissectors/packet-http3.c", 1315, "(header_data)->encoded.pos <= (header_data)->encoded.len" )))) ; } } while (0);
1316 | }
1317 | nghttp3_qpack_stream_context_del(sctx);
1318 |
1319 | if (stream_info->from_server) {
1320 | if (!http3_stream->response_header_data) {
1321 | http3_stream->response_header_data = wmem_list_new(wmem_file_scope());
1322 | }
1323 | wmem_list_append(http3_stream->response_header_data, header_data);
1324 | } else {
1325 | if (!http3_stream->request_header_data) {
1326 | http3_stream->request_header_data = wmem_list_new(wmem_file_scope());
1327 | }
1328 | wmem_list_append(http3_stream->request_header_data, header_data);
1329 | }
1330 | }
1331 |
1332 | /* Check if any expert information fields have to be added.
1333 | */
1334 | if (header_data->state == HTTP3_HD_DECODER_ERROR0x2) {
1335 | proto_tree_add_expert_format(tree, pinfo, &ei_http3_header_decoding_failed, tvb, tvb_offset, 0,
1336 | "QPACK decoding error %d (%s)",
1337 | header_data->error, nghttp3_strerror(header_data->error));
1338 | } else if (header_data->state == HTTP3_HD_DECODER_BLOCKED0x1) {
1339 | ti = proto_tree_add_boolean(tree, hf_http3_header_qpack_blocked, tvb, tvb_offset, 0, true1);
1340 | proto_item_set_generated(ti);
1341 | proto_tree_add_expert_format(tree, pinfo, &ei_http3_header_decoding_blocked, tvb, tvb_offset, 0,
1342 | "QPACK decoding blocked");
1343 | }
1344 |
1345 | if (header_data->encoded.pos < tvb_offset && header_data->encoded.len != 0) {
1346 | proto_tree_add_expert_format(tree, pinfo, &ei_http3_header_encoded_state, tvb, tvb_offset, 0,
1347 | "HTTP3 encoded headers - bytes %p pos %d len %d", header_data->encoded.bytes,
1348 | header_data->encoded.pos, header_data->encoded.len);
1349 | }
1350 |
1351 | if ((header_data->header_fields == NULL((void*)0)) || (wmem_array_get_count(header_data->header_fields) == 0)) {
1352 | return tvb_offset;
1353 | }
1354 |
1355 | /* Header data contains decoded header values; start building the protocol trees. */
1356 | header_tvb = tvb_new_composite();
1357 | header_len = 0;
1358 | hoffset = 0;
1359 | pseudo_headers = HTTP3_PSEUDO_HEADERS_INITIALIZER(http3_pseudo_header_fields_t){ .authority = ((void*)0), .method = ((void*)0), .path = ((void*)0), .protocol = ((void*)0), .reason_phrase = ((void*)0), .scheme = ((void*)0), .status = ((void*)0), };
1360 |
1361 | for (unsigned i = 0; i < wmem_array_get_count(header_data->header_fields); ++i) {
1362 | http3_header_field_t *in;
1363 | tvbuff_t *next_tvb;
1364 |
1365 | in = (http3_header_field_t *)wmem_array_index(header_data->header_fields, i);
1366 | header_len += in->decoded.len;
1367 |
1368 | /* Now setup the tvb buffer to have the new data */
1369 | next_tvb = tvb_new_child_real_data(tvb, (uint8_t*)in->decoded.bytes, in->decoded.len, in->decoded.len);
1370 | tvb_composite_append(header_tvb, next_tvb);
1371 | }
1372 |
1373 | tvb_composite_finalize(header_tvb);
1374 | add_new_data_source(pinfo, header_tvb, "Decompressed Header");
1375 |
1376 | ti = proto_tree_add_uint(tree, hf_http3_headers_decoded_length, header_tvb, hoffset, 1, header_len);
1377 | proto_item_set_generated(ti);
1378 | if (header_data->header_size_exceeded) {
1379 | expert_add_info(pinfo, ti, &ei_http3_header_size);
1380 | }
1381 |
1382 | ti = proto_tree_add_uint(tree, hf_http3_headers_count, header_tvb, hoffset, 1,
1383 | wmem_array_get_count(header_data->header_fields));
1384 | proto_item_set_generated(ti);
1385 |
1386 | wmem_strbuf_t *headers_buf = wmem_strbuf_create(pinfo->pool)wmem_strbuf_new(pinfo->pool, "");
1387 | wmem_strbuf_t *header_buf;
1388 |
1389 | for (unsigned i = 0; i < wmem_array_get_count(header_data->header_fields); ++i) {
1390 | http3_header_field_t *in;
1391 | proto_item *header;
1392 | proto_tree *header_tree;
1393 | uint32_t header_name_length;
1394 | const char *header_name;
1395 | uint32_t header_value_length;
1396 | const char *header_value;
1397 |
1398 | in = (http3_header_field_t *)wmem_array_index(header_data->header_fields, i);
1399 |
1400 | /* Populate tree with header name/value details. */
1401 | /* Add 'Header' subtree with description. */
1402 | header = proto_tree_add_item(tree, hf_http3_header, tvb, tvb_offset, in->encoded.len, ENC_NA0x00000000);
1403 |
1404 | header_tree = proto_item_add_subtree(header, ett_http3_headers);
1405 |
1406 | /* header value length */
1407 | proto_tree_add_item_ret_uint(header_tree, hf_http3_header_name_length, header_tvb, hoffset, 4,
1408 | ENC_BIG_ENDIAN0x00000000, &header_name_length);
1409 | hoffset += 4;
1410 |
1411 | /* Add header name. */
1412 | proto_tree_add_item_ret_string(header_tree, hf_http3_header_name, header_tvb, hoffset, header_name_length,
1413 | ENC_ASCII0x00000000 | ENC_NA0x00000000, pinfo->pool, (const uint8_t**)&header_name);
1414 | hoffset += header_name_length;
1415 |
1416 | /* header value length */
1417 | proto_tree_add_item_ret_uint(header_tree, hf_http3_header_value_length, header_tvb, hoffset, 4,
1418 | ENC_BIG_ENDIAN0x00000000, &header_value_length);
1419 | hoffset += 4;
1420 |
1421 | /* Add header value. */
1422 | proto_tree_add_item_ret_string(header_tree, hf_http3_header_value, header_tvb, hoffset, header_value_length,
1423 | ENC_ASCII0x00000000 | ENC_NA0x00000000, pinfo->pool, (const uint8_t**)&header_value);
1424 |
1425 | try_add_named_header_field(header_tree, pinfo, header_tvb, hoffset, header_value_length,
1426 | header_name, header_value);
1427 |
1428 | hoffset += header_value_length;
1429 |
1430 | /* Create a buffer of one header name and value per line to resemble
1431 | * text-based HTTP for adding to Follow Stream. The header_tvb already
1432 | * created has the string lengths and lacks newlines. */
1433 | header_buf = wmem_strbuf_new(pinfo->pool, header_name);
1434 | wmem_strbuf_append_printf(header_buf, ": %s", header_value);
1435 | proto_item_append_text(header, ": %s", wmem_strbuf_get_str(header_buf));
1436 | wmem_strbuf_append_printf(headers_buf, "%s\n", wmem_strbuf_finalize(header_buf));
1437 |
1438 | /* Track pseudo-header and header values stored persistently. */
1439 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
1440 | populate_http3_header_tracking(pinfo, http3_stream,
1441 | header_name, header_value);
1442 | }
1443 |
1444 | /* Add special values to the tree for certain pseudo-headers
1445 | * and headers, and collect the values for later processing,
1446 | * as some depend on each other. */
1447 | if (strcmp(header_name, HTTP3_HEADER_NAME_METHOD":method") == 0) {
1448 | pseudo_headers.method = header_value;
1449 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_PROTOCOL":protocol") == 0) {
1450 | pseudo_headers.protocol = header_value;
1451 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_PATH":path") == 0) {
1452 | pseudo_headers.path = header_value;
1453 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_AUTHORITY":authority") == 0) {
1454 | pseudo_headers.authority = header_value;
1455 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_SCHEME":scheme") == 0) {
1456 | pseudo_headers.scheme = header_value;
1457 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_STATUS":status") == 0) {
1458 | unsigned status_code;
1459 |
1460 | status_code = (unsigned)strtoul(header_value, NULL((void*)0), 10);
1461 | pseudo_headers.status = header_value;
1462 | pseudo_headers.reason_phrase = val_to_str_const(status_code, vals_http_status_code, "Unknown");
1463 | proto_item_append_text(header, " %s", pseudo_headers.reason_phrase);
1464 | proto_item_append_text(proto_item_get_parent(header), ", %s %s", pseudo_headers.status, pseudo_headers.reason_phrase);
1465 | } else if (strcmp(header_name, HTTP3_HEADER_NAME_TRANSFER_ENCODING"transfer-encoding") == 0) {
1466 | /* The Transfer-Encoding header field MUST NOT be used.
1467 | * https://www.rfc-editor.org/rfc/rfc9114.html#section-4.1-11 */
1468 | expert_add_info(pinfo, header, &ei_http3_header_transfer_encoding);
1469 | }
1470 |
1471 | tvb_offset += in->encoded.len;
1472 | }
1473 |
1474 | if (have_tap_listener(http3_follow_tap)) {
1475 | quic_follow_tap_data_t *follow_data = wmem_new0(pinfo->pool, quic_follow_tap_data_t)((quic_follow_tap_data_t*)wmem_alloc0((pinfo->pool), sizeof (quic_follow_tap_data_t)));
1476 |
1477 | wmem_strbuf_append(headers_buf, "\n");
1478 | follow_data->tvb = tvb_new_child_real_data(header_tvb,
1479 | (const uint8_t*)wmem_strbuf_get_str(headers_buf),
1480 | (unsigned)wmem_strbuf_get_len(headers_buf),
1481 | (unsigned)wmem_strbuf_get_len(headers_buf));
1482 | follow_data->stream_id = http3_stream->id;
1483 | follow_data->from_server = stream_info->from_server;
1484 |
1485 | tap_queue_packet(http3_follow_tap, pinfo, follow_data);
1486 | }
1487 |
1488 | /* We have finished constructing the tree for the header fields.
1489 | * Proceed to determine whether this is a variant of the CONNECT
1490 | * method and to update the info column display accordingly.
1491 | *
1492 | * We do this here, because while pseudo-headers must appear before
1493 | * regular headers, there is no guarantee of their internal ordering
1494 | * (although some implementations may assume that), and we want to
1495 | * add to COL_INFO in a particular order, and only can determine
1496 | * the CONNECT variant with both ":protocol" and ":method", etc.
1497 | */
1498 | if (pseudo_headers.method != NULL((void*)0)) {
1499 | const char *uri;
1500 |
1501 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
1502 | if (http3_stream->is_connect && pseudo_headers.protocol) {
1503 | http3_stream->protocol = wmem_strdup(wmem_file_scope(), pseudo_headers.protocol);
1504 | http3_stream->next_handle = http_upgrade_dissector(http3_stream->protocol);
1505 | http3_stream->upgrade_info = wmem_new0(wmem_file_scope(), http_upgrade_info_t)((http_upgrade_info_t*)wmem_alloc0((wmem_file_scope()), sizeof (http_upgrade_info_t)));
1506 | http3_stream->upgrade_info->server_port = pinfo->destport;
1507 | http3_stream->upgrade_info->http_version = 3;
1508 | http3_stream->upgrade_info->get_header_value = http3_get_header_value;
1509 | }
1510 | }
1511 |
1512 | /* HTTP/1.1 and HTTP/2 only add the method and path here */
1513 | uri = http3_get_request_full_uri(pinfo, http3_stream);
1514 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, ": ", "%s %s",
1515 | pseudo_headers.method, uri);
1516 | } else if (pseudo_headers.status != NULL((void*)0)) {
1517 | DISSECTOR_ASSERT(pseudo_headers.reason_phrase)((void) ((pseudo_headers.reason_phrase) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 1517, "pseudo_headers.reason_phrase")))); /* Must be filled together with `:status' */
1518 | /* append the status code and the reason phrase (for example, HEADERS: 200 OK) */
1519 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, ": ", "%s %s",
1520 | pseudo_headers.status, pseudo_headers.reason_phrase);
1521 | }
1522 |
1523 | return tvb_offset;
1524 | }
1525 |
1526 | #else /* HAVE_NGHTTP3 */
1527 | const char *
1528 | http3_get_header_value(packet_info *pinfo _U___attribute__((unused)), const char* name _U___attribute__((unused)), bool_Bool the_other_direction _U___attribute__((unused))) {
1529 | return NULL((void*)0);
1530 | }
1531 |
1532 | #endif /* HAVE_NGHTTP3 */
1533 |
1534 | static http3_session_info_t *
1535 | http3_session_new(void)
1536 | {
1537 | http3_session_info_t *http3_session;
1538 |
1539 | http3_session = wmem_new0(wmem_file_scope(), http3_session_info_t)((http3_session_info_t*)wmem_alloc0((wmem_file_scope()), sizeof (http3_session_info_t)));
1540 |
1541 | #ifdef HAVE_NGHTTP31
1542 | http3_initialize_qpack_decoders(http3_session);
1543 | #endif
1544 |
1545 | return http3_session;
1546 | }
1547 |
1548 | static http3_session_info_t *
1549 | http3_session_lookup_or_create(packet_info *pinfo)
1550 | {
1551 | http3_session_info_t *http3_session;
1552 |
1553 | /* First, try to look up the session by initial QUIC DCID */
1554 | quic_cid_t initial_dcid = {0};
1555 | if (quic_conn_data_get_conn_client_dcid_initial(pinfo, &initial_dcid)) {
1556 | /* Look up the session data in the conn map */
1557 | http3_session = (http3_session_info_t *)wmem_map_lookup(HTTP3_CONN_INFO_MAPhttp3_get_file_local_ctx()->conn_info_map, &initial_dcid);
1558 | if (http3_session == NULL((void*)0)) {
1559 | quic_cid_t *dcid_p = wmem_memdup(wmem_file_scope(), &initial_dcid, sizeof(initial_dcid));
1560 | http3_session = http3_session_new();
1561 | wmem_map_insert(HTTP3_CONN_INFO_MAPhttp3_get_file_local_ctx()->conn_info_map, dcid_p, http3_session);
1562 | }
1563 | } else {
1564 | /* Initial DCID can not be found, use the 5-tuple for lookup */
1565 | conversation_t *conversation = find_or_create_conversation(pinfo);
1566 | http3_session = (http3_session_info_t *)conversation_get_proto_data(conversation, proto_http3);
1567 |
1568 | if (http3_session == NULL((void*)0)) {
1569 | http3_session = http3_session_new();
1570 | conversation_add_proto_data(conversation, proto_http3, http3_session);
1571 | }
1572 | }
1573 |
1574 | return http3_session;
1575 | }
1576 |
1577 | #ifdef HAVE_NGHTTP31
1578 |
1579 | static conversation_t *
1580 | http3_find_inner_conversation(packet_info *pinfo, quic_stream_info *stream_info, http3_stream_info_t *http3_stream, void **ctx)
1581 | {
1582 | conversation_t *inner_conv = NULL((void*)0);
1583 |
1584 | if (stream_info != NULL((void*)0)) {
1585 | if (ctx) {
1586 | *ctx = pinfo->conv_elements;
1587 | }
1588 |
1589 | wmem_array_t *conversation_elements = wmem_array_new(pinfo->pool, sizeof(conversation_element_t));
1590 |
1591 | conversation_element_t h3_stream_addr = {
1592 | .type = CE_ADDRESS,
1593 | .addr_val = (pinfo->srcport < pinfo->destport) ? pinfo->src : pinfo->dst,
1594 | };
1595 | wmem_array_append_one(conversation_elements, h3_stream_addr)wmem_array_append((conversation_elements), &(h3_stream_addr ), 1);
1596 |
1597 | conversation_element_t h3_stream_port = {
1598 | .type = CE_PORT,
1599 | .port_val = (pinfo->srcport < pinfo->destport) ? pinfo->srcport : pinfo->destport,
1600 | };
1601 | wmem_array_append_one(conversation_elements, h3_stream_port)wmem_array_append((conversation_elements), &(h3_stream_port ), 1);
1602 |
1603 | conversation_element_t h3_stream_quic_stream = {
1604 | .type = CE_UINT64,
1605 | .uint64_val = http3_stream->id,
1606 | };
1607 | wmem_array_append_one(conversation_elements, h3_stream_quic_stream)wmem_array_append((conversation_elements), &(h3_stream_quic_stream ), 1);
1608 |
1609 | conversation_element_t h3_stream_last = {
1610 | .type = CE_CONVERSATION_TYPE,
1611 | .conversation_type_val = CONVERSATION_LOG,
1612 | };
1613 | wmem_array_append_one(conversation_elements, h3_stream_last)wmem_array_append((conversation_elements), &(h3_stream_last ), 1);
1614 |
1615 | pinfo->conv_elements = (conversation_element_t *)wmem_array_get_raw(conversation_elements);
1616 | inner_conv = find_conversation_pinfo(pinfo, 0);
1617 | if (!inner_conv) {
1618 | inner_conv = conversation_new_full(pinfo->fd->num, pinfo->conv_elements);
1619 | }
1620 | }
1621 |
1622 | return inner_conv;
1623 | }
1624 |
1625 | static void
1626 | http3_reset_inner_conversation(packet_info *pinfo, void *ctx)
1627 | {
1628 | if (ctx) {
1629 | struct conversation_element *conv_elements = (struct conversation_element *)ctx;
1630 | pinfo->conv_elements = conv_elements;
1631 | }
1632 | }
1633 |
1634 | enum body_decompression {
1635 | BODY_DECOMPRESSION_NONE,
1636 | BODY_DECOMPRESSION_ZLIB,
1637 | BODY_DECOMPRESSION_BROTLI,
1638 | BODY_DECOMPRESSION_ZSTD,
1639 | BODY_DECOMPRESSION_FAIL
1640 | };
1641 |
1642 | static enum body_decompression
1643 | get_body_decompression_info(packet_info *pinfo)
1644 | {
1645 | const char *content_encoding = http3_get_header_value(pinfo, HTTP3_HEADER_NAME_CONTENT_ENCODING"content-encoding", false0);
1646 | const char *status = http3_get_header_value(pinfo, HTTP3_HEADER_NAME_STATUS":status", false0);
1647 | if (content_encoding == NULL((void*)0)) {
1648 | return BODY_DECOMPRESSION_NONE;
1649 | }
1650 | if (!http3_decompress_body || g_strcmp0(status, HTTP3_HEADER_STATUS_PARTIAL_CONTENT"206") == 0) {
1651 | return BODY_DECOMPRESSION_FAIL;
1652 | }
1653 | #ifdef USE_ZLIB_OR_ZLIBNG
1654 | if (strncmp(content_encoding, "gzip", 4) == 0 || strncmp(content_encoding, "deflate", 7) == 0) {
1655 | return BODY_DECOMPRESSION_ZLIB;
1656 | }
1657 | #endif
1658 | #ifdef HAVE_BROTLI1
1659 | if (strncmp(content_encoding, "br", 2) == 0) {
1660 | return BODY_DECOMPRESSION_BROTLI;
1661 | }
1662 | #endif
1663 | #ifdef HAVE_ZSTD1
1664 | if (strncmp(content_encoding, "zstd", 4) == 0) {
1665 | return BODY_DECOMPRESSION_ZSTD;
1666 | }
1667 | #endif
1668 |
1669 | return BODY_DECOMPRESSION_FAIL;
1670 | }
1671 |
1672 | static void
1673 | dissect_http3_body_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *body_tree, http3_stream_info_t *http3_stream, bool_Bool decompression_success)
1674 | {
1675 | unsigned length = tvb_reported_length(tvb);
1676 | http_eo_t *eo_info;
1677 | const char *content_type = http3_get_header_value(pinfo, HTTP3_HEADER_NAME_CONTENT_TYPE"content-type", false0);
1678 |
1679 | proto_tree_add_item(body_tree, hf_http3_data, tvb, 0, length, ENC_NA0x00000000);
1680 |
1681 | if (have_tap_listener(http3_follow_tap)) {
1682 | quic_follow_tap_data_t *follow_data = wmem_new0(pinfo->pool, quic_follow_tap_data_t)((quic_follow_tap_data_t*)wmem_alloc0((pinfo->pool), sizeof (quic_follow_tap_data_t)));
1683 |
1684 | follow_data->tvb = tvb;
1685 | follow_data->stream_id = http3_stream->id;
1686 | follow_data->from_server = http3_stream->direction;
1687 |
1688 | tap_queue_packet(http3_follow_tap, pinfo, follow_data);
1689 | }
1690 |
1691 | if (have_tap_listener(http_eo_tap)) {
1692 | eo_info = wmem_new0(pinfo->pool, http_eo_t)((http_eo_t*)wmem_alloc0((pinfo->pool), sizeof(http_eo_t)) );
1693 |
1694 | eo_info->filename = http3_stream->pseudo_headers.path;
1695 | eo_info->hostname = http3_stream->pseudo_headers.authority;
1696 | eo_info->content_type = content_type;
1697 | eo_info->payload = tvb;
1698 |
1699 | tap_queue_packet(http_eo_tap, pinfo, eo_info);
1700 | }
1701 |
1702 | /* If we couldn't, or wouldn't, decompress the data, stop here. */
1703 | if (!decompression_success)
1704 | return;
1705 |
1706 | if (content_type != NULL((void*)0)) {
1707 | const char *semicolon = ws_strchrnul(content_type, ';');
1708 | char *media_type = wmem_ascii_strdown(pinfo->pool, content_type, semicolon - content_type);
1709 | char *media_type_parameters = NULL((void*)0);
1710 | while (*semicolon && *semicolon == ';' && g_ascii_isspace(*semicolon)((g_ascii_table[(guchar) (*semicolon)] & G_ASCII_SPACE) != 0)) {
1711 | ++semicolon;
1712 | }
1713 | if (*semicolon) {
1714 | media_type_parameters = wmem_strdup(pinfo->pool, semicolon);
1715 | }
1716 | media_content_info_t media_type_metadata = { MEDIA_CONTAINER_HTTP_OTHERS, media_type_parameters, NULL((void*)0), NULL((void*)0)};
1717 | if (!dissector_try_string_with_data(media_type_dissector_table, media_type,
1718 | tvb, pinfo, proto_tree_get_root(body_tree), true1, &media_type_metadata)) {
1719 |
1720 | const char *saved_match_string = pinfo->match_string;
1721 | pinfo->match_string = media_type;
1722 | call_dissector_with_data(media_handle, tvb, pinfo, proto_tree_get_root(body_tree), &media_type_metadata);
1723 | pinfo->match_string = saved_match_string;
1724 | }
1725 | }
1726 | }
1727 |
1728 | static void
1729 | dissect_http3_data_full_body(tvbuff_t *tvb, packet_info *pinfo, proto_tree *http3_tree, http3_stream_info_t *http3_stream)
1730 | {
1731 | unsigned datalen = tvb_reported_length(tvb);
1732 | proto_tree *body_tree = http3_tree;
1733 | tvbuff_t *next_tvb = tvb;
1734 |
1735 | const char *content_encoding = http3_get_header_value(pinfo, HTTP3_HEADER_NAME_CONTENT_ENCODING"content-encoding", false0);
1736 | enum body_decompression decompression = get_body_decompression_info(pinfo);
1737 | bool_Bool decompression_success = true1;
1738 |
1739 | if (decompression != BODY_DECOMPRESSION_NONE) {
1740 | proto_item *compressed_ti = NULL((void*)0);
1741 |
1742 | tvbuff_t *decompressed_tvb = NULL((void*)0);
1743 | switch (decompression) {
1744 | case BODY_DECOMPRESSION_ZLIB:
1745 | decompressed_tvb = tvb_child_uncompress_zlib(tvb, tvb, 0, datalen);
1746 | break;
1747 | case BODY_DECOMPRESSION_BROTLI:
1748 | decompressed_tvb = tvb_child_uncompress_brotli(tvb, tvb, 0, datalen);
1749 | break;
1750 | case BODY_DECOMPRESSION_ZSTD:
1751 | decompressed_tvb = tvb_child_uncompress_zstd(tvb, tvb, 0, datalen);
1752 | break;
1753 | default:
1754 | break;
1755 | }
1756 |
1757 | compressed_ti = proto_tree_add_none_format(http3_tree,
1758 | hf_http3_encoded_entity, tvb, 0, datalen,
1759 | "Content-encoded entity body (%s): %u bytes",
1760 | content_encoding == NULL((void*)0) ? "unknown" : content_encoding, datalen);
1761 |
1762 | if (decompressed_tvb) {
1763 | unsigned decompressed_length = tvb_reported_length(decompressed_tvb);
1764 | add_new_data_source(pinfo, decompressed_tvb, "Decompressed entity body");
1765 |
1766 | proto_item_append_text(compressed_ti, " -> %u bytes", decompressed_length);
1767 | body_tree = proto_item_add_subtree(compressed_ti, ett_http3_encoded_entity);
1768 | next_tvb = decompressed_tvb;
1769 | datalen = decompressed_length;
| Value stored to 'datalen' is never read
1770 | } else {
1771 | expert_add_info(pinfo, compressed_ti, &ei_http3_body_decompression_failed);
1772 | decompression_success = false0;
1773 | }
1774 | }
1775 |
1776 | dissect_http3_body_data(next_tvb, pinfo, body_tree, http3_stream, decompression_success);
1777 | }
1778 |
1779 | static void
1780 | dissect_http3_data_partial_body(tvbuff_t *tvb, packet_info *pinfo, proto_tree *http3_tree, quic_stream_info *stream_info, http3_stream_info_t *http3_stream)
1781 | {
1782 | unsigned length;
1783 | void *saved_ctx = NULL((void*)0);
1784 |
1785 | if (http3_stream->is_connect) {
1786 | /* Part of a tunneled CONNECT method. */
1787 | proto_item_append_text(http3_tree, " (tunneled data)");
1788 | } else {
1789 | /* Not part of CONNECT, frame that should be reassembled later. */
1790 | proto_item_append_text(http3_tree, " (partial entity body)");
1791 | }
1792 |
1793 | length = tvb_reported_length(tvb);
1794 | /* Adding this seems redundant with http3.frame_payload. */
1795 | proto_tree_add_item(http3_tree, hf_http3_data, tvb, 0, length, ENC_NA0x00000000);
1796 |
1797 | if (http3_stream->next_handle) {
1798 | /* Extended CONNECT */
1799 | /* inner_conv = */ http3_find_inner_conversation(pinfo, stream_info, http3_stream, &saved_ctx);
1800 | http3_stream->upgrade_info->from_server = http3_stream->direction;
1801 | call_dissector_only(http3_stream->next_handle, tvb, pinfo, proto_tree_get_root(http3_tree), http3_stream->upgrade_info);
1802 | http3_reset_inner_conversation(pinfo, saved_ctx);
1803 | }
1804 | }
1805 |
1806 | static bool_Bool
1807 | should_attempt_to_reassemble_data_content(http3_stream_info_t *http3_stream)
1808 | {
1809 | /* If this data frame is part of a CONNECT tunnel, don't try to reassemble.
1810 | * XXX - This is what HTTP/2 does, but perhaps CONNECT data should also
1811 | * use the streaming reassembly mode, once that is implemented.
1812 | */
1813 | if (http3_stream->is_connect) {
1814 | return false0;
1815 | }
1816 |
1817 | return true1;
1818 | }
1819 |
1820 | static tvbuff_t*
1821 | reassemble_http3_data_into_full_content(tvbuff_t *tvb, packet_info *pinfo, proto_tree *http3_tree, unsigned offset, quic_stream_info *stream_info, http3_stream_info_t *http3_stream, bool_Bool fin)
1822 | {
1823 | if (!should_attempt_to_reassemble_data_content(http3_stream)) {
1824 | return NULL((void*)0);
1825 | }
1826 |
1827 | fragment_head *head = NULL((void*)0);
1828 | unsigned remaining = tvb_captured_length_remaining(tvb, offset);
1829 | unsigned content_length;
1830 | bool_Bool content_length_set = false0;
1831 |
1832 | /* We can re-use the QUIC reassembly functions; we only have one body
1833 | * reassembly per request/response (since we only reassemble at FIN
1834 | * or when reaching the content-length), so the QUIC stream_info
1835 | * has all the necessary information to make the reassembly unique. */
1836 |
1837 | /* Are we starting the body defragmentation? */
1838 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited) && !fragment_get(&http3_body_reassembly_table, pinfo, 0, stream_info)) {
1839 | /* If so, do we know the content length */
1840 | const char *content_length_str = http3_get_header_value(pinfo, HTTP3_HEADER_NAME_CONTENT_LENGTH"content-length", false0);
1841 | if (content_length_str && ws_strtou(content_length_str, NULL((void*)0), &content_length)) {
1842 | content_length_set = true1;
1843 | /* Is this DATA frame the entire content-length? */
1844 | if (remaining == content_length) {
1845 | /* Yes; there are no more DATA frames. If FIN isn't set, that
1846 | * is because a later QUIC frame with no DATA will have FIN.
1847 | * Let's go ahead and desegment now. The reassembly head isn't
1848 | * created, so we can't use fragment_set_tot_len, which will
1849 | * handle this in the other cases.
1850 | *
1851 | * XXX - Would it be simpler to have something like
1852 | * fragment_start_seq_check?
1853 | */
1854 | fin = true1;
1855 | }
1856 | }
1857 | }
1858 |
1859 | /* We might want a fragment_add_next instead of fragment_add_check_next so
1860 | * that we can get reassembly errors when the segment is too long instead
1861 | * of creating a new reassembly. */
1862 | head = fragment_add_check_next(&http3_body_reassembly_table, tvb, offset, pinfo, 0, stream_info, remaining, !fin);
1863 | if (content_length_set) {
1864 | fragment_set_tot_len(&http3_body_reassembly_table, pinfo, 0, stream_info, content_length);
1865 | }
1866 |
1867 | if (head) {
1868 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
1869 | /* XXX - Remember that we dissected via DATA frames so we don't need
1870 | * to dissect at FIN in a non-DATA frame, e.g. HEADERS or GREASE. */
1871 | }
1872 | /* The Info column information is less useful when dissected in the
1873 | * same capture file frame, even in a different HTTP/3 DATA frame. */
1874 | if (pinfo->num != head->reassembled_in) {
1875 | col_append_frame_number(pinfo, COL_INFO, " [HTTP/3 reassembled in #%u]",
1876 | head->reassembled_in);
1877 | }
1878 | /* This should work now (layer numbers are more stable), but check
1879 | * to see if we need to do what HTTP/2 does. */
1880 | return process_reassembled_data(tvb, offset, pinfo, "Reassembled body",
1881 | head, &http3_body_fragment_items, NULL((void*)0), http3_tree);
1882 |
1883 | #if 0
1884 | proto_tree_add_uint(http3_tree, hf_http3_body_reassembled_in, tvb, 0,
1885 | 0, head->reassembled_in);
1886 | #endif
1887 | }
1888 |
1889 | return NULL((void*)0);
1890 | }
1891 |
1892 | static int
1893 | dissect_http3_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *http3_tree, unsigned offset _U___attribute__((unused)),
1894 | quic_stream_info *stream_info, http3_stream_info_t *http3_stream, bool_Bool fin)
1895 | {
1896 | /* Padding is not defined in HTTP/3 DATA Frames
1897 | * https://www.rfc-editor.org/rfc/rfc9114.html#name-comparison-of-http-2-and-ht
1898 | */
1899 |
1900 | /* XXX - HTTP/2 supports fake headers for a stream here */
1901 |
1902 | /* "Because some messages are large or unbounded, endpoints SHOULD begin
1903 | * processing partial HTTP messages once enough of the message has been
1904 | * received to make progress."
1905 | * https://www.rfc-editor.org/rfc/rfc9114.html#section-4.1-14
1906 | * Not all possible media type subdissectors support defragmentation via
1907 | * setting desegment_offset and desegment_len in pinfo, and the necessary
1908 | * hooks are not in this dissector. For now, just reassemble everything
1909 | * into the full content.
1910 | *
1911 | * XXX - Cf. what the HTTP/2 dissector does for the streaming reassembly
1912 | * mode.
1913 | */
1914 |
1915 | tvbuff_t *data_tvb = reassemble_http3_data_into_full_content(tvb, pinfo, http3_tree, offset, stream_info, http3_stream, fin);
1916 | if (data_tvb) {
1917 | dissect_http3_data_full_body(data_tvb, pinfo, http3_tree, http3_stream);
1918 | } else {
1919 | dissect_http3_data_partial_body(tvb, pinfo, http3_tree, stream_info, http3_stream);
1920 | }
1921 |
1922 | return tvb_reported_length(tvb);
1923 | }
1924 | #else
1925 | static int
1926 | dissect_http3_data(tvbuff_t *tvb, packet_info *pinfo _U___attribute__((unused)), proto_tree *http3_tree, unsigned offset _U___attribute__((unused)),
1927 | quic_stream_info *stream_info _U___attribute__((unused)), http3_stream_info_t *http3_stream _U___attribute__((unused)), bool_Bool fin _U___attribute__((unused)))
1928 | {
1929 | unsigned length = tvb_reported_length(tvb);
1930 | /* Adding this seems redundant with http3.frame_payload. */
1931 | proto_tree_add_item(http3_tree, hf_http3_data, tvb, 0, length, ENC_NA0x00000000);
1932 |
1933 | return length;
1934 | }
1935 | #endif /* HAVE_NGHTTP3 */
1936 |
1937 | static unsigned
1938 | #ifdef HAVE_NGHTTP31
1939 | dissect_http3_headers(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned tvb_offset, unsigned offset,
1940 | quic_stream_info *stream_info, http3_stream_info_t *http3_stream)
1941 | #else
1942 | dissect_http3_headers(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U___attribute__((unused)), unsigned tvb_offset _U___attribute__((unused)), unsigned offset _U___attribute__((unused)),
1943 | quic_stream_info *stream_info _U___attribute__((unused)), http3_stream_info_t *http3_stream)
1944 | #endif
1945 | {
1946 | if (http3_stream->direction == FROM_CLIENT_TO_SERVER) {
1947 | if (http3_stream->request_frame_num == 0) {
1948 | http3_stream->request_frame_num = pinfo->num;
1949 | http3_stream->request_ts = pinfo->abs_ts;
1950 | }
1951 | } else {
1952 | if (http3_stream->response_frame_num == 0) {
1953 | http3_stream->response_frame_num = pinfo->num;
1954 | }
1955 | }
1956 | #ifdef HAVE_NGHTTP31
1957 | decode_http3_headers(tvb, pinfo, tree, tvb_offset, offset, stream_info, http3_stream);
1958 | #else
1959 | if (have_tap_listener(http3_follow_tap)) {
1960 | quic_follow_tap_data_t *follow_data = wmem_new0(pinfo->pool, quic_follow_tap_data_t)((quic_follow_tap_data_t*)wmem_alloc0((pinfo->pool), sizeof (quic_follow_tap_data_t)));
1961 |
1962 | follow_data->tvb = tvb;
1963 | follow_data->stream_id = http3_stream->id;
1964 | follow_data->from_server = stream_info->from_server;
1965 |
1966 | tap_queue_packet(http3_follow_tap, pinfo, follow_data);
1967 | }
1968 | #endif
1969 | return tvb_reported_length(tvb);
1970 | }
1971 |
1972 | /* Settings */
1973 | static int
1974 | dissect_http3_settings(tvbuff_t *tvb, packet_info *pinfo, proto_tree *http3_tree, unsigned offset)
1975 | {
1976 | uint64_t settingsid, value;
1977 | unsigned lenvar;
1978 | proto_item *ti_settings, *pi;
1979 | proto_tree *settings_tree;
1980 |
1981 | while (tvb_reported_length_remaining(tvb, offset) > 0) {
1982 | ti_settings = proto_tree_add_item(http3_tree, hf_http3_settings, tvb, offset, 2, ENC_NA0x00000000);
1983 | settings_tree = proto_item_add_subtree(ti_settings, ett_http3_settings);
1984 | pi = proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_identifier, tvb, offset, -1,
1985 | ENC_VARINT_QUIC0x00000004, &settingsid, &lenvar);
1986 | /* Check if it is a GREASE Settings ID */
1987 | if (http3_is_reserved_code(settingsid)) {
1988 | proto_item_set_text(pi, "Settings Identifier: Reserved (%#" PRIx64"l" "x" ")", settingsid);
1989 | proto_item_append_text(ti_settings, " - Reserved (GREASE)");
1990 | } else {
1991 | proto_item_append_text(ti_settings, " - %s",
1992 | val64_to_str_wmem(pinfo->pool, settingsid, http3_settings_vals, "Unknown (%#" PRIx64"l" "x" ")"));
1993 | }
1994 |
1995 | offset += lenvar;
1996 | proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_value, tvb, offset, -1, ENC_VARINT_QUIC0x00000004, NULL((void*)0),
1997 | &lenvar);
1998 |
1999 | switch (settingsid) {
2000 | case HTTP3_QPACK_MAX_TABLE_CAPACITY0x01:
2001 | proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_qpack_max_table_capacity, tvb, offset, -1,
2002 | ENC_VARINT_QUIC0x00000004, &value, &lenvar);
2003 | proto_item_append_text(ti_settings, ": %" PRIu64"l" "u", value);
2004 | break;
2005 | case HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE0x06:
2006 | proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_max_field_section_size, tvb, offset, -1,
2007 | ENC_VARINT_QUIC0x00000004, &value, &lenvar);
2008 | proto_item_append_text(ti_settings, ": %" PRIu64"l" "u", value);
2009 | break;
2010 | case HTTP3_QPACK_BLOCKED_STREAMS0x07:
2011 | proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_qpack_blocked_streams, tvb, offset, -1,
2012 | ENC_VARINT_QUIC0x00000004, &value, &lenvar);
2013 | proto_item_append_text(ti_settings, ": %" PRIu64"l" "u", value);
2014 | break;
2015 | case HTTP3_EXTENDED_CONNECT0x08:
2016 | proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_extended_connect, tvb, offset, -1,
2017 | ENC_VARINT_QUIC0x00000004, &value, &lenvar);
2018 | proto_item_append_text(ti_settings, ": %" PRIu64"l" "u", value);
2019 | break;
2020 | case HTTP3_WEBTRANSPORT0x2b603742:
2021 | proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_webtransport, tvb, offset, -1,
2022 | ENC_VARINT_QUIC0x00000004, &value, &lenvar);
2023 | proto_item_append_text(ti_settings, ": %" PRIu64"l" "u", value);
2024 | break;
2025 | case HTTP3_H3_DATAGRAM0x33:
2026 | proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_h3_datagram, tvb, offset, -1,
2027 | ENC_VARINT_QUIC0x00000004, &value, &lenvar);
2028 | proto_item_append_text(ti_settings, ": %" PRIu64"l" "u", value);
2029 | break;
2030 | case HTTP3_H3_DATAGRAM_DRAFT040xffd277:
2031 | proto_tree_add_item_ret_varint(settings_tree, hf_http3_settings_h3_datagram_draft04, tvb, offset, -1,
2032 | ENC_VARINT_QUIC0x00000004, &value, &lenvar);
2033 | proto_item_append_text(ti_settings, ": %" PRIu64"l" "u", value);
2034 | break;
2035 | default:
2036 | /* No Default */
2037 | break;
2038 | }
2039 | offset += lenvar;
2040 | }
2041 |
2042 | return offset;
2043 | }
2044 |
2045 | /**
2046 | * Priority Update
2047 | */
2048 | static int
2049 | dissect_http3_priority_update(tvbuff_t *tvb, packet_info *pinfo _U___attribute__((unused)), proto_tree *http3_tree, unsigned offset,
2050 | http3_stream_info_t *http3_stream)
2051 | {
2052 | uint64_t element_id;
2053 | unsigned priority_field_value_len;
2054 | unsigned lenvar;
2055 |
2056 | proto_tree_add_item_ret_varint(http3_tree, hf_http3_priority_update_element_id, tvb, offset, -1, ENC_VARINT_QUIC0x00000004,
2057 | &element_id, &lenvar);
2058 | offset += lenvar;
2059 | priority_field_value_len = tvb_reported_length_remaining(tvb, offset);
2060 |
2061 | proto_tree_add_item(http3_tree, hf_http3_priority_update_field_value, tvb, offset, priority_field_value_len,
2062 | ENC_ASCII0x00000000);
2063 |
2064 | if (have_tap_listener(http3_follow_tap)) {
2065 | quic_follow_tap_data_t *follow_data = wmem_new0(pinfo->pool, quic_follow_tap_data_t)((quic_follow_tap_data_t*)wmem_alloc0((pinfo->pool), sizeof (quic_follow_tap_data_t)));
2066 |
2067 | wmem_strbuf_t *priority_buf = wmem_strbuf_new(pinfo->pool, "priority: ");
2068 | tvbuff_t *priority_tvb = tvb_new_composite();
2069 | tvb_composite_append(priority_tvb,
2070 | tvb_new_child_real_data(tvb,
2071 | (const uint8_t*)wmem_strbuf_get_str(priority_buf),
2072 | (unsigned)wmem_strbuf_get_len(priority_buf),
2073 | (unsigned)wmem_strbuf_get_len(priority_buf)));
2074 | tvb_composite_append(priority_tvb, tvb_new_subset_length(tvb, offset, priority_field_value_len));
2075 | priority_buf = wmem_strbuf_create(pinfo->pool)wmem_strbuf_new(pinfo->pool, "");
2076 | wmem_strbuf_append_printf(priority_buf, " [Prioritized Element ID %" PRIu64"l" "u" "]\n", element_id);
2077 | tvb_composite_append(priority_tvb,
2078 | tvb_new_child_real_data(tvb,
2079 | (const uint8_t*)wmem_strbuf_get_str(priority_buf),
2080 | (unsigned)wmem_strbuf_get_len(priority_buf),
2081 | (unsigned)wmem_strbuf_get_len(priority_buf)));
2082 | tvb_composite_finalize(priority_tvb);
2083 | follow_data->tvb = priority_tvb;
2084 | follow_data->stream_id = http3_stream->id;
2085 | follow_data->from_server = http3_stream->direction;
2086 |
2087 | tap_queue_packet(http3_follow_tap, pinfo, follow_data);
2088 | }
2089 |
2090 | offset += priority_field_value_len;
2091 |
2092 | return offset;
2093 | }
2094 |
2095 | static int
2096 | dissect_http3_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, quic_stream_info *stream_info, http3_stream_info_t *http3_stream)
2097 | {
2098 | uint64_t frame_type, frame_length;
2099 | unsigned type_length_size, lenvar, payload_length, total_length;
2100 | proto_item *ti_ft, *ti_ft_type;
2101 | proto_tree *ft_tree;
2102 | const char *ft_display_name;
2103 | bool_Bool fin = false0;
2104 |
2105 | ti_ft = proto_tree_add_item(tree, hf_http3_frame, tvb, offset, -1, ENC_NA0x00000000);
2106 | ft_tree = proto_item_add_subtree(ti_ft, ett_http3_frame);
2107 |
2108 | ti_ft_type = proto_tree_add_item_ret_varint(ft_tree, hf_http3_frame_type, tvb, offset, -1, ENC_VARINT_QUIC0x00000004, &frame_type,
2109 | &lenvar);
2110 | offset += lenvar;
2111 | type_length_size = lenvar;
2112 | if (http3_is_reserved_code(frame_type)) {
2113 | proto_item_set_text(ti_ft_type, "Type: Reserved (%#" PRIx64"l" "x" ")", frame_type);
2114 | ft_display_name = "Reserved (GREASE)";
2115 | } else {
2116 | ft_display_name = val64_to_str_wmem(pinfo->pool, frame_type, http3_frame_types, "Unknown (%#" PRIx64"l" "x" ")");
2117 | col_append_sep_str(pinfo->cinfo, COL_INFO, ", ", ft_display_name);
2118 | }
2119 | proto_tree_add_item_ret_varint(ft_tree, hf_http3_frame_length, tvb, offset, -1, ENC_VARINT_QUIC0x00000004, &frame_length,
2120 | &lenvar);
2121 | proto_item_set_text(ti_ft, "%s len=%" PRId64"l" "d", ft_display_name, frame_length);
2122 | offset += lenvar;
2123 | type_length_size += lenvar;
2124 |
2125 | if (ckd_add(&total_length, type_length_size, frame_length)__builtin_add_overflow((type_length_size), (frame_length), (& total_length))) {
2126 | // There is no way for us to correctly handle these sizes. Most likely
2127 | // it is garbage.
2128 | return INT32_MAX(2147483647);
2129 | }
2130 |
2131 | proto_item_set_len(ti_ft, total_length);
2132 | payload_length = (unsigned)frame_length;
2133 | if (payload_length == 0) {
2134 | return offset;
2135 | }
2136 |
2137 | if (stream_info->fin && tvb_reported_length_remaining(tvb, offset) == frame_length) {
2138 | // If the QUIC stream is at FIN, then finish reassembly iff this
2139 | // is the last HTTP/3 frame within the QUIC STREAM frame.
2140 | fin = true1;
2141 | }
2142 | proto_tree_add_item(ft_tree, hf_http3_frame_payload, tvb, offset, payload_length, ENC_NA0x00000000);
2143 |
2144 | switch (frame_type) {
2145 | case HTTP3_DATA0x0: { /* TODO: dissect Data Frame */
2146 | tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, payload_length);
2147 | dissect_http3_data(next_tvb, pinfo, ft_tree, 0, stream_info, http3_stream, fin);
2148 | fin = false0;
2149 | } break;
2150 | case HTTP3_HEADERS0x1: {
2151 | tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, payload_length);
2152 | dissect_http3_headers(next_tvb, pinfo, ft_tree, 0, offset, stream_info, http3_stream);
2153 | } break;
2154 | case HTTP3_CANCEL_PUSH0x3: /* TODO: dissect Cancel_Push Frame */
2155 | break;
2156 | case HTTP3_SETTINGS0x4: { /* Settings Frame */
2157 | tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, payload_length);
2158 | dissect_http3_settings(next_tvb, pinfo, ft_tree, 0);
2159 | } break;
2160 | case HTTP3_PUSH_PROMISE0x5: /* TODO: dissect Push_Promise_Frame */
2161 | break;
2162 | case HTTP3_GOAWAY0x7: /* TODO: dissect Goaway Frame */
2163 | break;
2164 | case HTTP3_MAX_PUSH_ID0xD: /* TODO: dissect Max_Push_ID Frame */
2165 | break;
2166 | case HTTP3_PRIORITY_UPDATE_REQUEST_STREAM0xF0700:
2167 | /* FALLTHROUGH */
2168 | case HTTP3_PRIORITY_UPDATE_PUSH_STREAM0xF0701: { /* Priority_Update Frame */
2169 | tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, payload_length);
2170 | dissect_http3_priority_update(next_tvb, pinfo, ft_tree, 0, http3_stream);
2171 | } break;
2172 | default: /* TODO: add expert advice (remember to treat GREASE differently) */
2173 | break;
2174 | }
2175 |
2176 | #ifdef HAVE_NGHTTP31
2177 | if (fin) {
2178 | fragment_head *head = NULL((void*)0);
2179 | head = fragment_end_seq_next(&http3_body_reassembly_table, pinfo, 0, stream_info);
2180 |
2181 | if (head) {
2182 | tvbuff_t *reassembled_data = process_reassembled_data(tvb, offset, pinfo, "Reassembled body",
2183 | head, &http3_body_fragment_items, NULL((void*)0), ft_tree);
2184 | if (reassembled_data)
2185 | dissect_http3_data_full_body(reassembled_data, pinfo, ft_tree, http3_stream);
2186 | }
2187 | }
2188 | #endif
2189 |
2190 | offset += payload_length;
2191 | return offset;
2192 | }
2193 |
2194 | static void
2195 | report_unknown_stream_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
2196 | quic_stream_info *stream_info, http3_stream_info_t *http3_stream)
2197 | {
2198 | /*
2199 | * https://www.rfc-editor.org/rfc/rfc9114.html#name-unidirectional-streams
2200 | *
2201 | * "If the stream header indicates a stream type which is not supported by
2202 | * the recipient, the remainder of the stream cannot be consumed as the
2203 | * semantics are unknown."
2204 | */
2205 | proto_tree_add_expert_format(tree, pinfo, &ei_http3_unknown_stream_type, tvb, offset, 0,
2206 | "Unknown stream type %#" PRIx64"l" "x" " on Stream ID %#" PRIx64"l" "x",
2207 | http3_stream->uni_stream_type, stream_info->stream_id);
2208 | }
2209 |
2210 | /**
2211 | * https://www.rfc-editor.org/rfc/rfc7541#section-5.1
2212 | * via
2213 | * https://www.rfc-editor.org/rfc/rfc9204.html#name-prefixed-integers
2214 | *
2215 | * Read a QPACK varint value, return number of consumed bytes, including the prefix byte.
2216 | *
2217 | * Optionally return the value of the one-bit flag that precedes the QPACK prefixed integer.
2218 | *
2219 | * Such flag is is interpreted differently, depending on the context:
2220 | * - If the prefixed integer represents length of a string literal, the flag value
2221 | * indicates that the following string literal is encoded using Huffman code.
2222 | * See https://www.rfc-editor.org/rfc/rfc7541#section-5.2 for details.
2223 | * - If the prefixed integer represents a name index, the flag value indicates
2224 | * that the following name index belongs to the static/dynamic table.
2225 | * See https://www.rfc-editor.org/rfc/rfc9204.html#name-insert-with-name-reference
2226 | * for details.
2227 | */
2228 | #define HTTP3_QPACK_MAX_SHIFT62 62
2229 | #define HTTP3_QPACK_MAX_INT((1ull << 62) - 1) ((1ull << HTTP3_QPACK_MAX_SHIFT62) - 1)
2230 |
2231 | static unsigned
2232 | read_qpack_prefixed_integer(tvbuff_t *tvb, unsigned offset, unsigned prefix,
2233 | uint64_t *out_result, bool_Bool *out_flag)
2234 | {
2235 | /*
2236 | * This can throw a ReportedBoundError; in fact, we count on that
2237 | * currently in order to detect QPACK fields split across packets.
2238 | */
2239 | uint64_t k = (uint8_t)((1 << prefix) - 1);
2240 | uint64_t n = 0;
2241 | uint64_t add = 0;
2242 | uint64_t shift = 0;
2243 | uint8_t byte;
2244 | unsigned start_offset = offset;
2245 |
2246 | byte = tvb_get_uint8(tvb, offset);
2247 |
2248 | if (out_flag) {
2249 | *out_flag = byte & (1 << prefix);
2250 | }
2251 |
2252 | if ((byte & k) != k) {
2253 | *out_result = byte & k;
2254 | return 1;
2255 | }
2256 |
2257 | n = k;
2258 |
2259 | for (++offset; tvb_captured_length_remaining(tvb, offset); ++offset, shift += 7) {
2260 | byte = tvb_get_uint8(tvb, offset);
2261 | add = byte & 0x7f;
2262 | if (shift > HTTP3_QPACK_MAX_SHIFT62) {
2263 | return 0;
2264 | }
2265 | if ((HTTP3_QPACK_MAX_INT((1ull << 62) - 1) >> shift) < add) {
2266 | return 0;
2267 | }
2268 | add <<= shift;
2269 | if (HTTP3_QPACK_MAX_INT((1ull << 62) - 1) - add < n) {
2270 | return 0;
2271 | }
2272 |
2273 | n += add;
2274 |
2275 | if ((byte & (1 << 7)) == 0) {
2276 | break;
2277 | }
2278 | }
2279 |
2280 | *out_result = n;
2281 |
2282 | /* Otherwise, consume extra byte and mark the fin output param */
2283 | return offset - start_offset + 1;
2284 | }
2285 |
2286 | static int
2287 | dissect_http3_qpack_encoder_stream(tvbuff_t *tvb, packet_info *pinfo _U___attribute__((unused)), proto_tree *tree,
2288 | int start_offset, http3_stream_info_t *http3_stream, int *picnt)
2289 | {
2290 | unsigned end_offset; /* Sentinel offset past the buffer. */
2291 | tvbuff_t *decoded_tvb; /* TVB with the result of decoding the Huffman-encoding strings */
2292 | volatile bool_Bool can_continue; /* Flag to indicate that we can parse the next QPACK instruction */
2293 | volatile int icnt; /* Number of dissected QPACK instructions */
2294 | volatile unsigned offset; /* Current parsing offset, relative to the start of the tvb */
2295 |
2296 | can_continue = true1;
2297 | icnt = 0;
2298 | offset = start_offset;
2299 | end_offset = start_offset + tvb_captured_length_remaining(tvb, start_offset);
2300 | wmem_strbuf_t *follow_buf = wmem_strbuf_create(pinfo->pool)wmem_strbuf_new(pinfo->pool, "");
2301 | wmem_strbuf_t *instr_buf;
2302 |
2303 | while (offset < end_offset && can_continue) {
2304 | int inst_offset; /* Starting offset of the currently parsed instruction in the tvb */
2305 | int inst_len; /* Total length of the instruction */
2306 | unsigned varint_len;
2307 |
2308 | proto_item *opcode_ti = NULL((void*)0), *huffman_ti;
2309 | proto_tree *opcode_tree;
2310 |
2311 | inst_offset = offset;
2312 |
2313 | 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) {
2314 | uint8_t opcode; /* The instruction opcode */
2315 |
2316 | opcode = tvb_get_uint8(tvb, inst_offset) & QPACK_OPCODE_MASK0xE0;
2317 |
2318 | ws_noisy("Decoding opcode=%" PRIu8 " decoded=%d start=%d current=%d end=%d",do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 2319, __func__, "Decoding opcode=%" "hhu" " decoded=%d start=%d current=%d end=%d" , opcode, (offset - start_offset), start_offset, offset, end_offset ); } } while (0)
2319 | opcode, (offset - start_offset), start_offset, offset, end_offset)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 2319, __func__, "Decoding opcode=%" "hhu" " decoded=%d start=%d current=%d end=%d" , opcode, (offset - start_offset), start_offset, offset, end_offset ); } } while (0);
2320 |
2321 | if (opcode & QPACK_OPCODE_INSERT_INDEXED0x80) {
2322 | unsigned name_idx_len = 0;
2323 | uint64_t name_idx = 0;
2324 | unsigned val_offset = 0;
2325 | uint64_t val_len = 0;
2326 | bool_Bool val_huffman = false0;
2327 | const uint8_t *val_str = NULL((void*)0);
2328 |
2329 | /* https://datatracker.ietf.org/doc/html/rfc9204#name-insert-with-name-reference
2330 | *
2331 | * 0 1 2 3 4 5 6 7
2332 | * +---+---+---+---+---+---+---+---+
2333 | * | 1 | T | Name Index (6+) |
2334 | * +---+---+-----------------------+
2335 | * | H | Value Length (7+) |
2336 | * +---+---------------------------+
2337 | * | Value String (Length bytes) |
2338 | * +-------------------------------+
2339 | */
2340 |
2341 | /* Read the 6-encoded name index len */
2342 | name_idx_len = read_qpack_prefixed_integer(tvb, offset, 6, &name_idx, NULL((void*)0));
2343 | if (name_idx_len == 0) {
2344 | THROW(ScsiBoundsError)except_throw(1, (7), ((void*)0));
2345 | }
2346 | offset += name_idx_len;
2347 |
2348 | /* Read the 7-encoded value len and set the value offset for subsequent dissection */
2349 | varint_len = read_qpack_prefixed_integer(tvb, offset, 7, &val_len, &val_huffman);
2350 | if (varint_len == 0) {
2351 | THROW(ScsiBoundsError)except_throw(1, (7), ((void*)0));
2352 | }
2353 | /* XXX - If val_len > UINT32_MAX, fail with expert info? */
2354 | offset += varint_len;
2355 | val_offset = offset;
2356 | offset += (uint32_t)val_len;
2357 |
2358 | /* Update the instruction length */
2359 | inst_len = offset - inst_offset;
2360 |
2361 | /* Add the protocol tree items */
2362 | opcode_ti = proto_tree_add_item(tree, hf_http3_qpack_encoder_opcode_insert_indexed,
2363 | tvb, inst_offset,inst_len, ENC_NA0x00000000);
2364 | opcode_tree = proto_item_add_subtree(opcode_ti, ett_http3_qpack_opcode);
2365 | proto_tree_add_uint64(opcode_tree, hf_http3_qpack_encoder_opcode_insert_indexed_ref,
2366 | tvb, inst_offset, inst_len, name_idx);
2367 |
2368 | if (val_huffman) {
2369 | huffman_ti = proto_tree_add_item(opcode_tree, hf_http3_qpack_encoder_opcode_insert_indexed_hval,
2370 | tvb, val_offset, (uint32_t)val_len, ENC_NA0x00000000);
2371 | decoded_tvb = tvb_child_uncompress_hpack_huff(tvb, val_offset, (unsigned)val_len);
2372 | if (decoded_tvb) {
2373 | add_new_data_source(pinfo, decoded_tvb, "Decoded QPACK Value");
2374 | proto_tree_add_item_ret_string(opcode_tree, hf_http3_qpack_encoder_opcode_insert_indexed_val,
2375 | decoded_tvb, 0, tvb_captured_length(decoded_tvb), ENC_NA0x00000000, pinfo->pool, &val_str);
2376 | } else {
2377 | expert_add_info(pinfo, huffman_ti, &ei_http3_huffman_failed);
2378 | val_str = (const uint8_t*)"???";
2379 | }
2380 | } else {
2381 | proto_tree_add_item_ret_string(opcode_tree, hf_http3_qpack_encoder_opcode_insert_indexed_val,
2382 | tvb, val_offset, (uint32_t)val_len, ENC_NA0x00000000, pinfo->pool, &val_str);
2383 | }
2384 | instr_buf = wmem_strbuf_new(pinfo->pool, "INSERT_INDEXED");
2385 | wmem_strbuf_append_printf(instr_buf, "[%" PRIu64"l" "u" "=%s]", name_idx, val_str);
2386 | proto_item_set_text(opcode_ti, "%s", wmem_strbuf_get_str(instr_buf));
2387 | wmem_strbuf_append_printf(follow_buf, "%s\n", wmem_strbuf_finalize(instr_buf));
2388 | } else if (opcode & QPACK_OPCODE_INSERT0x40) {
2389 | unsigned name_offset = 0;
2390 | uint64_t name_len = 0;
2391 | bool_Bool name_huffman = false0;
2392 | const uint8_t *name_str = NULL((void*)0);
2393 | bool_Bool val_huffman = false0;
2394 | unsigned val_offset = 0;
2395 | uint64_t val_len = 0;
2396 | const uint8_t *val_str = NULL((void*)0);
2397 |
2398 |
2399 | /* https://datatracker.ietf.org/doc/html/rfc9204#name-insert-with-literal-name
2400 | *
2401 | * 0 1 2 3 4 5 6 7
2402 | * +---+---+---+---+---+---+---+---+
2403 | * | 0 | 1 | H | Name Length (5+) |
2404 | * +---+---+---+-------------------+
2405 | * | Name String (Length bytes) |
2406 | * +---+---------------------------+
2407 | * | H | Value Length (7+) |
2408 | * +---+---------------------------+
2409 | * | Value String (Length bytes) |
2410 | * +-------------------------------+
2411 | */
2412 |
2413 | /* Read the 5-encoded name length and set the name offset for subsequent dissection */
2414 | varint_len = read_qpack_prefixed_integer(tvb, offset, 5, &name_len, &name_huffman);
2415 | if (varint_len == 0) {
2416 | THROW(ScsiBoundsError)except_throw(1, (7), ((void*)0));
2417 | }
2418 | /* XXX - If name_len > UINT32_MAX, fail with expert info? */
2419 | offset += varint_len;
2420 | name_offset = offset;
2421 | offset += (uint32_t)name_len;
2422 |
2423 | /* Read the 7-encoded value length and set the value offset for subsequent dissection */
2424 | varint_len = read_qpack_prefixed_integer(tvb, offset, 7, &val_len, &val_huffman);
2425 | if (varint_len == 0) {
2426 | THROW(ScsiBoundsError)except_throw(1, (7), ((void*)0));
2427 | }
2428 | /* XXX - If val_len > UINT32_MAX, fail with expert info? */
2429 | offset += varint_len;
2430 | val_offset = offset;
2431 | offset += (uint32_t)val_len;
2432 |
2433 | /* Update the instruction length */
2434 | inst_len = offset - inst_offset;
2435 |
2436 | /* Add the protocol tree items */
2437 | opcode_ti = proto_tree_add_item(tree, hf_http3_qpack_encoder_opcode_insert,
2438 | tvb, inst_offset, inst_len, ENC_NA0x00000000);
2439 | opcode_tree = proto_item_add_subtree(opcode_ti, ett_http3_qpack_opcode);
2440 |
2441 | if (name_huffman) {
2442 | huffman_ti = proto_tree_add_item(opcode_tree, hf_http3_qpack_encoder_opcode_insert_hname,
2443 | tvb, name_offset, (uint32_t)name_len, ENC_NA0x00000000);
2444 | decoded_tvb = tvb_child_uncompress_hpack_huff(tvb, name_offset, (unsigned)name_len);
2445 | if (decoded_tvb) {
2446 | add_new_data_source(pinfo, decoded_tvb, "Decoded QPACK Name");
2447 | proto_tree_add_item_ret_string(opcode_tree, hf_http3_qpack_encoder_opcode_insert_name,
2448 | decoded_tvb, 0, tvb_captured_length(decoded_tvb), ENC_NA0x00000000, pinfo->pool, &name_str);
2449 | } else {
2450 | expert_add_info(pinfo, huffman_ti, &ei_http3_huffman_failed);
2451 | name_str = (const uint8_t*)"???";
2452 | }
2453 | } else {
2454 | proto_tree_add_item_ret_string(opcode_tree, hf_http3_qpack_encoder_opcode_insert_name,
2455 | tvb, name_offset,(uint32_t)name_len, ENC_NA0x00000000, pinfo->pool, &name_str);
2456 | }
2457 |
2458 | if (val_huffman) {
2459 | huffman_ti = proto_tree_add_item(opcode_tree, hf_http3_qpack_encoder_opcode_insert_hval,
2460 | tvb, val_offset,(uint32_t)val_len, ENC_NA0x00000000);
2461 | decoded_tvb = tvb_child_uncompress_hpack_huff(tvb, val_offset, (unsigned)val_len);
2462 | if (decoded_tvb) {
2463 | add_new_data_source(pinfo, decoded_tvb, "Decoded QPACK Value");
2464 | proto_tree_add_item_ret_string(opcode_tree, hf_http3_qpack_encoder_opcode_insert_val,
2465 | decoded_tvb, 0, tvb_captured_length(decoded_tvb), ENC_NA0x00000000, pinfo->pool, &val_str);
2466 | } else {
2467 | expert_add_info(pinfo, huffman_ti, &ei_http3_huffman_failed);
2468 | val_str = (const uint8_t*)"???";
2469 | }
2470 | } else {
2471 | proto_tree_add_item_ret_string(opcode_tree, hf_http3_qpack_encoder_opcode_insert_val,
2472 | tvb, val_offset,(uint32_t)val_len, ENC_NA0x00000000, pinfo->pool, &val_str);
2473 | }
2474 |
2475 | instr_buf = wmem_strbuf_new(pinfo->pool, "INSERT");
2476 | wmem_strbuf_append_printf(instr_buf, "[%s=%s]", name_str, val_str);
2477 | proto_item_set_text(opcode_ti, "%s", wmem_strbuf_get_str(instr_buf));
2478 | wmem_strbuf_append_printf(follow_buf, "%s\n", wmem_strbuf_finalize(instr_buf));
2479 | } else if (opcode & QPACK_OPCODE_SET_DTABLE_CAP0x20) {
2480 | uint64_t dynamic_capacity = 0;
2481 |
2482 | /* https://datatracker.ietf.org/doc/html/rfc9204#name-set-dynamic-table-capacity
2483 | *
2484 | * 0 1 2 3 4 5 6 7
2485 | * +---+---+---+---+---+---+---+---+
2486 | * | 0 | 0 | 1 | Capacity (5+) |
2487 | * +---+---+---+-------------------+
2488 | */
2489 |
2490 | /* Read the 5-encoded table capacity */
2491 | varint_len = read_qpack_prefixed_integer(tvb, offset, 5, &dynamic_capacity, NULL((void*)0));
2492 | if (varint_len == 0) {
2493 | THROW(ScsiBoundsError)except_throw(1, (7), ((void*)0));
2494 | }
2495 | offset += varint_len;
2496 |
2497 | /* Update the instruction length */
2498 | inst_len = offset - inst_offset;
2499 |
2500 | /* Add the protocol tree items */
2501 | opcode_ti = proto_tree_add_item(tree, hf_http3_qpack_encoder_opcode_dtable_cap,
2502 | tvb, inst_offset,inst_len, ENC_NA0x00000000);
2503 | opcode_tree = proto_item_add_subtree(opcode_ti, ett_http3_qpack_opcode);
2504 | proto_tree_add_uint64(opcode_tree, hf_http3_qpack_encoder_opcode_dtable_cap_val,
2505 | tvb, inst_offset, inst_len,dynamic_capacity);
2506 |
2507 | instr_buf = wmem_strbuf_new(pinfo->pool, "DTABLE_CAP");
2508 | wmem_strbuf_append_printf(instr_buf, "[%" PRIu64"l" "u" "]", dynamic_capacity);
2509 | proto_item_set_text(opcode_ti, "%s", wmem_strbuf_get_str(instr_buf));
2510 | wmem_strbuf_append_printf(follow_buf, "%s\n", wmem_strbuf_finalize(instr_buf));
2511 | } else if (opcode == QPACK_OPCODE_DUPLICATE0x00) {
2512 | uint64_t duplicate_of = 0;
2513 |
2514 | /* https://datatracker.ietf.org/doc/html/rfc9204#name-duplicate
2515 | *
2516 | * 0 1 2 3 4 5 6 7
2517 | * +---+---+---+---+---+---+---+---+
2518 | * | 0 | 0 | 0 | Index (5+) |
2519 | * +---+---+---+-------------------+
2520 | */
2521 |
2522 | /* Read the 5-encoded index of a duplicate instruction */
2523 | varint_len = read_qpack_prefixed_integer(tvb, offset, 5, &duplicate_of, NULL((void*)0));
2524 | if (varint_len == 0) {
2525 | THROW(ScsiBoundsError)except_throw(1, (7), ((void*)0));
2526 | }
2527 | offset += varint_len;
2528 |
2529 | /* Update the instruction length */
2530 | inst_len = offset - inst_offset;
2531 |
2532 | /* Add the protocol tree items */
2533 | proto_tree_add_item(tree, hf_http3_qpack_encoder_opcode_duplicate,
2534 | tvb, inst_offset, inst_len, ENC_NA0x00000000);
2535 |
2536 | instr_buf = wmem_strbuf_new(pinfo->pool, "DUPLICATE");
2537 | wmem_strbuf_append_printf(instr_buf, "[%" PRIu64"l" "u" "]", duplicate_of);
2538 | proto_item_set_text(opcode_ti, "%s", wmem_strbuf_get_str(instr_buf));
2539 | wmem_strbuf_append_printf(follow_buf, "%s\n", wmem_strbuf_finalize(instr_buf));
2540 | } else {
2541 | ws_debug("Opcode=%" PRIu8 ": UNKNOWN", opcode)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 2541, __func__, "Opcode=%" "hhu" ": UNKNOWN", opcode); } } while (0);
2542 | can_continue = false0;
2543 | }
2544 | /* Increment the instruction count */
2545 | icnt ++;
2546 | }
2547 | CATCH(ScsiBoundsError)if (except_state == 0 && exc != 0 && exc-> except_id.except_code == (7) && (except_state |= 1)) {
2548 | /* This is obviously not SCSI, but we use a bounds type error
2549 | * which the main API won't throw. This too large integer is
2550 | * possibly a QUIC reassembly error or the payload not actually
2551 | * being HTTP/3. */
2552 | proto_tree_add_expert(tree, pinfo, &ei_http3_prefix_int_failed, tvb, offset, 1);
2553 | /* Above won't throw an exception because we would have thrown
2554 | * an error retrieving the first octet of the prefixed integer. */
2555 | offset = end_offset;
2556 | can_continue = false0;
2557 | }
2558 | CATCH(ReportedBoundsError)if (except_state == 0 && exc != 0 && exc-> except_id.except_code == (3) && (except_state |= 1)) {
2559 | /* We could not parse the last instruction, hence update `decoded' accordingly. */
2560 | ws_debug("%u: Could not parse last instruction, rolling back parsing offset from %d to %d",do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 2561, __func__, "%u: Could not parse last instruction, rolling back parsing offset from %d to %d" , pinfo->num, offset, inst_offset); } } while (0)
2561 | pinfo->num, offset, inst_offset)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 2561, __func__, "%u: Could not parse last instruction, rolling back parsing offset from %d to %d" , pinfo->num, offset, inst_offset); } } while (0);
2562 | offset = inst_offset;
2563 | can_continue = false0;
2564 | }
2565 | ENDTRYif(!(except_state&1) && exc != 0) except_rethrow( exc); except_free(except_ch.except_obj.except_dyndata); except_pop (); };};
2566 | }
2567 |
2568 | if (icnt && have_tap_listener(http3_follow_tap)) {
2569 | quic_follow_tap_data_t *follow_data = wmem_new0(pinfo->pool, quic_follow_tap_data_t)((quic_follow_tap_data_t*)wmem_alloc0((pinfo->pool), sizeof (quic_follow_tap_data_t)));
2570 |
2571 | follow_data->tvb = tvb_new_child_real_data(tvb,
2572 | (const uint8_t*)wmem_strbuf_get_str(follow_buf),
2573 | (unsigned)wmem_strbuf_get_len(follow_buf),
2574 | (unsigned)wmem_strbuf_get_len(follow_buf));
2575 | follow_data->stream_id = http3_stream->id;
2576 | follow_data->from_server = http3_stream->direction;
2577 |
2578 | tap_queue_packet(http3_follow_tap, pinfo, follow_data);
2579 | }
2580 |
2581 | *picnt = icnt;
2582 | return offset - start_offset;
2583 | }
2584 |
2585 | static int
2586 | dissect_http3_qpack_enc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
2587 | quic_stream_info *stream_info, http3_stream_info_t *http3_stream)
2588 | {
2589 | int remaining;
2590 | int remaining_captured;
2591 | int retval;
2592 |
2593 | remaining_captured = tvb_captured_length_remaining(tvb, offset);
2594 | remaining = tvb_reported_length_remaining(tvb, offset);
2595 | retval = remaining;
2596 |
2597 | DISSECTOR_ASSERT(remaining_captured == remaining)((void) ((remaining_captured == remaining) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 2597, "remaining_captured == remaining"))));
2598 |
2599 | if (remaining > 0) {
2600 | http3_session_info_t *http3_session;
2601 | proto_item *qpack_update;
2602 | proto_tree *qpack_update_tree;
2603 | int decoded;
2604 | int icnt;
2605 |
2606 | http3_session = http3_session_lookup_or_create(pinfo);
2607 | DISSECTOR_ASSERT(http3_session)((void) ((http3_session) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 2607, "http3_session"))));
2608 |
2609 | /*
2610 | * Add a QPACK encoder tree item.
2611 | */
2612 | qpack_update = proto_tree_add_item(tree, hf_http3_qpack_encoder, tvb, offset, remaining, ENC_NA0x00000000);
2613 | qpack_update_tree = proto_item_add_subtree(qpack_update, ett_http3_qpack_update);
2614 | decoded = dissect_http3_qpack_encoder_stream(tvb, pinfo, qpack_update_tree, offset,
2615 | http3_stream, &icnt);
2616 |
2617 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
2618 | ws_debug("decode encoder stream: Wireshark decoded=%u of %u", decoded, remaining)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 2618, __func__, "decode encoder stream: Wireshark decoded=%u of %u" , decoded, remaining); } } while (0);
2619 | }
2620 | if (decoded < remaining) {
2621 | pinfo->desegment_offset = offset + decoded;
2622 | pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT0x0fffffff;
2623 | }
2624 |
2625 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "QPACK ENC[%d]", icnt);
2626 |
2627 | #ifdef HAVE_NGHTTP31
2628 | http3_stream_dir packet_direction = http3_packet_get_direction(stream_info);
2629 | nghttp3_qpack_decoder *decoder = http3_session->qpack_decoder[packet_direction];
2630 | proto_item *ti = NULL((void*)0);
2631 | http3_qpack_encoder_state_t *encoder_state = http3_get_qpack_encoder_state(pinfo, tvb, offset);
2632 |
2633 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
2634 | /*
2635 | * Since we are now defragmenting, pass only the number of bytes
2636 | * decoded to the nghttp3_qpack_decoder. Otherwise, we'll end up
2637 | * sending the same bytes to the decoder again when the packet
2638 | * is defragmented.
2639 | */
2640 | uint8_t *qpack_buf = (uint8_t *)tvb_memdup(pinfo->pool, tvb, offset, decoded);
2641 | int qpack_buf_len = decoded;
2642 |
2643 | /*
2644 | * Get the instr count prior to processing the data.
2645 | */
2646 | uint64_t icnt_before = nghttp3_qpack_decoder_get_icnt(decoder);
2647 |
2648 | encoder_state->nread = nghttp3_qpack_decoder_read_encoder(decoder, qpack_buf, qpack_buf_len);
2649 | encoder_state->icnt = nghttp3_qpack_decoder_get_icnt(decoder);
2650 | encoder_state->icnt_inc = (uint32_t)(encoder_state->icnt - icnt_before);
2651 |
2652 | ws_debug("decode encoder stream: decoder=%p nread=%td new insertions=%u total insertions=%" PRIu64,do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 2653, __func__, "decode encoder stream: decoder=%p nread=%td new insertions=%u total insertions=%" "l" "u", decoder, encoder_state->nread, encoder_state-> icnt_inc, encoder_state->icnt); } } while (0)
2653 | decoder, encoder_state->nread, encoder_state->icnt_inc, encoder_state->icnt)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 2653, __func__, "decode encoder stream: decoder=%p nread=%td new insertions=%u total insertions=%" "l" "u", decoder, encoder_state->nread, encoder_state-> icnt_inc, encoder_state->icnt); } } while (0);
2654 | }
2655 |
2656 | /* nghttp3_qpack_decoder_read_encoder() returns a nghttp3_ssize
2657 | * (ptrdiff_t), negative in the case of errors, but nghttp3_strerror()
2658 | * accepts int instead.
2659 | */
2660 | if (encoder_state->nread < 0) {
2661 | quic_cid_t quic_cid = {.len = 0};
2662 | bool_Bool initial_cid_found = quic_conn_data_get_conn_client_dcid_initial(pinfo, &quic_cid);
2663 | proto_tree_add_expert_format(tree, pinfo, &ei_http3_qpack_failed, tvb, offset, 0,
2664 | "QPACK decoder %p DCID %s [found=%d] error %d (%s)",
2665 | decoder, cid_to_string(&quic_cid, pinfo->pool), initial_cid_found,
2666 | (int)encoder_state->nread, nghttp3_strerror((int)encoder_state->nread));
2667 | }
2668 |
2669 | proto_item_set_text(qpack_update, "QPACK encoder stream; %d instructions (%" PRIu64"l" "u" " total)",
2670 | encoder_state->icnt_inc, encoder_state->icnt);
2671 | ti = proto_tree_add_uint(qpack_update_tree, hf_http3_qpack_encoder_icnt_inc, tvb, offset, 0,
2672 | encoder_state->icnt_inc);
2673 | proto_item_set_generated(ti);
2674 | ti = proto_tree_add_uint64(qpack_update_tree, hf_http3_qpack_encoder_icnt, tvb, offset, 0,
2675 | encoder_state->icnt);
2676 | proto_item_set_generated(ti);
2677 | #else
2678 | (void)stream_info;
2679 | (void)qpack_update;
2680 | (void)decoded;
2681 | #endif /* HAVE_NGHTTP3 */
2682 | }
2683 |
2684 | return retval;
2685 | }
2686 |
2687 | static int
2688 | dissect_http3_qpack_decoder_stream(tvbuff_t *tvb, packet_info *pinfo _U___attribute__((unused)), proto_tree *tree,
2689 | int start_offset, http3_stream_info_t *http3_stream, int *picnt)
2690 | {
2691 | unsigned end_offset; /* Sentinel offset past the buffer. */
2692 | volatile bool_Bool can_continue; /* Flag to indicate that we can parse the next QPACK instruction */
2693 | volatile int icnt; /* Number of dissected QPACK instructions */
2694 | volatile unsigned offset; /* Current parsing offset, relative to the start of the tvb */
2695 |
2696 | can_continue = true1;
2697 | icnt = 0;
2698 | offset = start_offset;
2699 | end_offset = start_offset + tvb_captured_length_remaining(tvb, start_offset);
2700 |
2701 | wmem_strbuf_t *follow_buf = wmem_strbuf_create(pinfo->pool)wmem_strbuf_new(pinfo->pool, "");
2702 | wmem_strbuf_t *instr_buf;
2703 |
2704 | while (offset < end_offset && can_continue) {
2705 | int inst_offset; /* Starting offset of the currently parsed instruction in the tvb */
2706 | int inst_len; /* Total length of the instruction */
2707 | proto_item *opcode_ti;
2708 | proto_tree *opcode_tree;
2709 |
2710 | inst_offset = offset;
2711 |
2712 | 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) {
2713 | uint8_t opcode; /* The instruction opcode */
2714 | unsigned varint_len;
2715 |
2716 | opcode = tvb_get_uint8(tvb, inst_offset) & QPACK_OPCODE_MASK0xE0;
2717 |
2718 | ws_noisy("Decoding opcode=%" PRIu8 " decoded=%d start=%d current=%d end=%d",do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 2719, __func__, "Decoding opcode=%" "hhu" " decoded=%d start=%d current=%d end=%d" , opcode, (offset - start_offset), start_offset, offset, end_offset ); } } while (0)
2719 | opcode, (offset - start_offset), start_offset, offset, end_offset)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 2719, __func__, "Decoding opcode=%" "hhu" " decoded=%d start=%d current=%d end=%d" , opcode, (offset - start_offset), start_offset, offset, end_offset ); } } while (0);
2720 |
2721 | if (opcode & QPACK_OPCODE_SECTION_ACK0x80) {
2722 | uint64_t stream_id = 0;
2723 |
2724 | /* https://datatracker.ietf.org/doc/html/rfc9204#section-4.4.1
2725 | *
2726 | * 0 1 2 3 4 5 6 7
2727 | * +---+---+---+---+---+---+---+---+
2728 | * | 1 | Stream ID (7+) |
2729 | * +---+---------------------------+
2730 | */
2731 |
2732 | /* Read the 7-encoded name stream ID */
2733 | varint_len = read_qpack_prefixed_integer(tvb, offset, 7, &stream_id, NULL((void*)0));
2734 | if (varint_len == 0) {
2735 | offset = end_offset;
2736 | break;
2737 | }
2738 | offset += varint_len;
2739 |
2740 | /* Update the instruction length */
2741 | inst_len = offset - inst_offset;
2742 |
2743 | /* Add the protocol tree items */
2744 | opcode_ti = proto_tree_add_item(tree, hf_http3_qpack_decoder_opcode_section_ack,
2745 | tvb, inst_offset, inst_len, ENC_NA0x00000000);
2746 | opcode_tree = proto_item_add_subtree(opcode_ti, ett_http3_qpack_opcode);
2747 | proto_tree_add_uint64(opcode_tree, hf_http3_qpack_decoder_opcode_section_ack_stream_id,
2748 | tvb, inst_offset, inst_len, stream_id);
2749 |
2750 | instr_buf = wmem_strbuf_new(pinfo->pool, "SECTION_ACK");
2751 | wmem_strbuf_append_printf(instr_buf, "[id=%" PRIu64"l" "u" "]", stream_id);
2752 | proto_item_set_text(opcode_ti, "%s", wmem_strbuf_get_str(instr_buf));
2753 | wmem_strbuf_append_printf(follow_buf, "%s\n", wmem_strbuf_finalize(instr_buf));
2754 | } else if (opcode & QPACK_OPCODE_STREAM_CANCEL0x40) {
2755 | uint64_t stream_id = 0;
2756 |
2757 | /* https://datatracker.ietf.org/doc/html/rfc9204#section-4.4.2
2758 | *
2759 | * 0 1 2 3 4 5 6 7
2760 | * +---+---+---+---+---+---+---+---+
2761 | * | 0 | 1 | Stream ID (6+) |
2762 | * +---+---+---+-------------------+
2763 | */
2764 |
2765 | /* Read the 6-encoded name stream ID */
2766 | varint_len = read_qpack_prefixed_integer(tvb, offset, 6, &stream_id, NULL((void*)0));
2767 | if (varint_len == 0) {
2768 | offset = end_offset;
2769 | break;
2770 | }
2771 | offset += varint_len;
2772 |
2773 | /* Update the instruction length */
2774 | inst_len = offset - inst_offset;
2775 |
2776 | /* Add the protocol tree items */
2777 | opcode_ti = proto_tree_add_item(tree, hf_http3_qpack_decoder_opcode_cancel_stream,
2778 | tvb, inst_offset, inst_len, ENC_NA0x00000000);
2779 | opcode_tree = proto_item_add_subtree(opcode_ti, ett_http3_qpack_opcode);
2780 | proto_tree_add_uint64(opcode_tree, hf_http3_qpack_decoder_opcode_cancel_stream_id,
2781 | tvb, inst_offset, inst_len,stream_id);
2782 |
2783 | instr_buf = wmem_strbuf_new(pinfo->pool, "STREAM_CANCEL");
2784 | wmem_strbuf_append_printf(instr_buf, "[id=%" PRIu64"l" "u" "]", stream_id);
2785 | proto_item_set_text(opcode_ti, "%s", wmem_strbuf_get_str(instr_buf));
2786 | wmem_strbuf_append_printf(follow_buf, "%s\n", wmem_strbuf_finalize(instr_buf));
2787 | } else if (opcode == QPACK_OPCODE_ICNT_INCREMENT0x00) {
2788 | uint64_t icnt_inc = 0;
2789 |
2790 | /* https://datatracker.ietf.org/doc/html/rfc9204#section-4.4.3
2791 | *
2792 | * 0 1 2 3 4 5 6 7
2793 | * +---+---+---+---+---+---+---+---+
2794 | * | 0 | 0 | Increment (6+) |
2795 | * +---+---+-----------------------+
2796 | */
2797 |
2798 | /* Read the 6-encoded instruction count increment */
2799 | varint_len = read_qpack_prefixed_integer(tvb, offset, 6, &icnt_inc, NULL((void*)0));
2800 | if (varint_len == 0) {
2801 | offset = end_offset;
2802 | break;
2803 | }
2804 | offset += varint_len;
2805 |
2806 | /* Update the instruction length */
2807 | inst_len = offset - inst_offset;
2808 | /* Add the protocol tree items */
2809 |
2810 | opcode_ti = proto_tree_add_item(tree, hf_http3_qpack_decoder_opcode_icnt_increment,
2811 | tvb, inst_offset, inst_len, ENC_NA0x00000000);
2812 | opcode_tree = proto_item_add_subtree(opcode_ti, ett_http3_qpack_opcode);
2813 | proto_tree_add_uint64(opcode_tree, hf_http3_qpack_decoder_opcode_icnt_increment_value,
2814 | tvb, inst_offset, inst_len,icnt_inc);
2815 |
2816 | instr_buf = wmem_strbuf_new(pinfo->pool, "ICNT_INC");
2817 | wmem_strbuf_append_printf(instr_buf, "[%" PRIu64"l" "u" "]", icnt_inc);
2818 | proto_item_set_text(opcode_ti, "%s", wmem_strbuf_get_str(instr_buf));
2819 | wmem_strbuf_append_printf(follow_buf, "%s\n", wmem_strbuf_finalize(instr_buf));
2820 | } else {
2821 | ws_debug("Opcode=%" PRIu8 ": UNKNOWN", opcode)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 2821, __func__, "Opcode=%" "hhu" ": UNKNOWN", opcode); } } while (0);
2822 | can_continue = false0;
2823 | }
2824 | /* Increment the instruction count */
2825 | icnt ++;
2826 | }
2827 | CATCH(ReportedBoundsError)if (except_state == 0 && exc != 0 && exc-> except_id.except_code == (3) && (except_state |= 1)) {
2828 | /* We could not parse the last instruction, hence roll back `parsing_offset' . */
2829 | ws_noisy("Could not parse last instruction, rolling back parsing offset from %d to %d",do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 2830, __func__, "Could not parse last instruction, rolling back parsing offset from %d to %d" , offset, inst_offset); } } while (0)
2830 | offset, inst_offset)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_NOISY, "epan/dissectors/packet-http3.c" , 2830, __func__, "Could not parse last instruction, rolling back parsing offset from %d to %d" , offset, inst_offset); } } while (0);
2831 | offset = inst_offset;
2832 | can_continue = false0;
2833 | }
2834 | ENDTRYif(!(except_state&1) && exc != 0) except_rethrow( exc); except_free(except_ch.except_obj.except_dyndata); except_pop (); };};
2835 | }
2836 |
2837 | if (icnt && have_tap_listener(http3_follow_tap)) {
2838 | quic_follow_tap_data_t *follow_data = wmem_new0(pinfo->pool, quic_follow_tap_data_t)((quic_follow_tap_data_t*)wmem_alloc0((pinfo->pool), sizeof (quic_follow_tap_data_t)));
2839 |
2840 | follow_data->tvb = tvb_new_child_real_data(tvb,
2841 | (const uint8_t*)wmem_strbuf_get_str(follow_buf),
2842 | (unsigned)wmem_strbuf_get_len(follow_buf),
2843 | (unsigned)wmem_strbuf_get_len(follow_buf));
2844 | follow_data->stream_id = http3_stream->id;
2845 | follow_data->from_server = http3_stream->direction;
2846 |
2847 | tap_queue_packet(http3_follow_tap, pinfo, follow_data);
2848 | }
2849 |
2850 | *picnt = icnt;
2851 | return offset - start_offset;
2852 | }
2853 |
2854 | static int
2855 | dissect_http3_qpack_dec(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
2856 | quic_stream_info *stream_info _U___attribute__((unused)), http3_stream_info_t *http3_stream)
2857 | {
2858 | int remaining;
2859 | int remaining_captured;
2860 | int retval;
2861 |
2862 | remaining_captured = tvb_captured_length_remaining(tvb, offset);
2863 | remaining = tvb_reported_length_remaining(tvb, offset);
2864 | retval = remaining;
2865 |
2866 | DISSECTOR_ASSERT(remaining_captured == remaining)((void) ((remaining_captured == remaining) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 2866, "remaining_captured == remaining"))));
2867 |
2868 | if (remaining > 0) {
2869 | http3_session_info_t *http3_session;
2870 | proto_item *qpack_update;
2871 | proto_tree *qpack_update_tree;
2872 | int decoded;
2873 | int icnt;
2874 |
2875 | http3_session = http3_session_lookup_or_create(pinfo);
2876 | DISSECTOR_ASSERT(http3_session)((void) ((http3_session) ? (void)0 : (proto_report_dissector_bug ("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-http3.c" , 2876, "http3_session"))));
2877 |
2878 | /*
2879 | * Add a QPACK encoder tree item.
2880 | */
2881 | qpack_update = proto_tree_add_item(tree, hf_http3_qpack_decoder, tvb, offset, remaining, ENC_NA0x00000000);
2882 | qpack_update_tree = proto_item_add_subtree(qpack_update, ett_http3_qpack_update);
2883 | decoded = dissect_http3_qpack_decoder_stream(tvb, pinfo, qpack_update_tree, offset, http3_stream, &icnt);
2884 |
2885 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "QPACK DEC[%d]", icnt);
2886 |
2887 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
2888 | ws_debug("decode decoder stream: Wireshark decoded=%u of %u", decoded, remaining)do { if (1) { ws_log_full("HTTP3", LOG_LEVEL_DEBUG, "epan/dissectors/packet-http3.c" , 2888, __func__, "decode decoder stream: Wireshark decoded=%u of %u" , decoded, remaining); } } while (0);
2889 | }
2890 | if (decoded < remaining) {
2891 | pinfo->desegment_offset = offset + decoded;
2892 | pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT0x0fffffff;
2893 | }
2894 | retval = decoded;
2895 | }
2896 |
2897 | return retval;
2898 | }
2899 |
2900 | static int
2901 | dissect_http3_client_bidi_stream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
2902 | quic_stream_info *stream_info, http3_stream_info_t *http3_stream)
2903 | {
2904 | proto_item *ti_stream, *ti_stream_id;
2905 | proto_tree *stream_tree;
2906 |
2907 | ti_stream = proto_tree_add_item(tree, hf_http3_stream_bidi, tvb, offset, 1, ENC_NA0x00000000);
2908 | stream_tree = proto_item_add_subtree(ti_stream, ett_http3_stream_bidi);
2909 |
2910 | ti_stream_id = proto_tree_add_uint64(stream_tree, hf_http3_stream_id, tvb, offset, 0, http3_stream->id);
2911 | proto_item_set_generated(ti_stream_id);
2912 |
2913 | while (tvb_reported_length_remaining(tvb, offset)) {
2914 | if (!http3_check_frame_size(tvb, pinfo, offset)) {
2915 | offset = tvb_captured_length(tvb);
2916 | break;
2917 | }
2918 | offset = dissect_http3_frame(tvb, pinfo, stream_tree, offset, stream_info, http3_stream);
2919 | }
2920 |
2921 | const char *uri = http3_get_request_full_uri(pinfo, http3_stream);
2922 | if (uri) {
2923 | proto_item *ti_url = proto_tree_add_string(stream_tree, hf_http3_header_request_full_uri, tvb, 0, 0, uri);
2924 | proto_item_set_url(ti_url);
2925 | proto_item_set_generated(ti_url);
2926 | }
2927 |
2928 | if (http3_stream->direction == FROM_CLIENT_TO_SERVER) {
2929 | if (http3_stream->response_frame_num != 0) {
2930 | proto_item_set_generated(proto_tree_add_uint(stream_tree, hf_http3_response_in, tvb, 0, 0, http3_stream->response_frame_num));
2931 | }
2932 | } else {
2933 | if (http3_stream->request_frame_num != 0) {
2934 | if (!nstime_is_unset(&http3_stream->request_ts)) {
2935 | nstime_t delta;
2936 |
2937 | nstime_delta(&delta, &pinfo->abs_ts, &http3_stream->request_ts);
2938 | proto_item_set_generated(proto_tree_add_time(stream_tree, hf_http3_time, tvb, 0, 0, &delta));
2939 | }
2940 | proto_item_set_generated(proto_tree_add_uint(stream_tree, hf_http3_request_in, tvb, 0, 0, http3_stream->request_frame_num));
2941 | }
2942 | }
2943 |
2944 | return offset;
2945 | }
2946 |
2947 | static int
2948 | dissect_http3_uni_stream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, quic_stream_info *stream_info,
2949 | http3_stream_info_t *http3_stream)
2950 | {
2951 | uint64_t stream_type;
2952 | unsigned lenvar;
2953 | proto_item *ti_stream, *ti_stream_id, *ti_stream_type;
2954 | proto_tree *stream_tree;
2955 | const char *stream_display_name;
2956 |
2957 | ti_stream = proto_tree_add_item(tree, hf_http3_stream_uni, tvb, offset, -1, ENC_NA0x00000000);
2958 | stream_tree = proto_item_add_subtree(ti_stream, ett_http3_stream_uni);
2959 |
2960 | ti_stream_id = proto_tree_add_uint64(stream_tree, hf_http3_stream_id, tvb, offset, 0, http3_stream->id);
2961 | proto_item_set_generated(ti_stream_id);
2962 |
2963 | if (stream_info->offset == 0) {
2964 | ti_stream_type = proto_tree_add_item_ret_varint(stream_tree, hf_http3_stream_uni_type, tvb, offset, -1, ENC_VARINT_QUIC0x00000004, &stream_type,
2965 | &lenvar);
2966 | offset += lenvar;
2967 | http3_stream->uni_stream_type = stream_type;
2968 | if (http3_is_reserved_code(stream_type)) {
2969 | // Reserved to exercise requirement that unknown types are ignored.
2970 | proto_item_set_text(ti_stream_type, "Stream Type: Reserved (%#" PRIx64"l" "x" ")", stream_type);
2971 | stream_display_name = "Reserved (GREASE)";
2972 | }
2973 | else {
2974 | stream_display_name = val64_to_str_wmem(pinfo->pool, stream_type, http3_stream_types, "Unknown (%#" PRIx64"l" "x" ")");
2975 | }
2976 | proto_item_set_text(ti_stream, "UNI STREAM: %s off=%" PRIu64"l" "u", stream_display_name, stream_info->stream_offset);
2977 | } else {
2978 | stream_type = http3_stream->uni_stream_type;
2979 | /*ti_stream_type = proto_tree_add_item(stream_tree, hf_http3_stream_uni_type, tvb, offset, -1, ENC_BIG_ENDIAN);*/
2980 | }
2981 |
2982 | switch (stream_type) {
2983 | case HTTP3_STREAM_TYPE_CONTROL:
2984 | while (tvb_reported_length_remaining(tvb, offset)) {
2985 | if (!http3_check_frame_size(tvb, pinfo, offset)) {
2986 | return tvb_captured_length(tvb);
2987 | }
2988 | offset = dissect_http3_frame(tvb, pinfo, stream_tree, offset, stream_info, http3_stream);
2989 | }
2990 | break;
2991 | case HTTP3_STREAM_TYPE_PUSH:
2992 | // The remaining data of this stream consists of HTTP/3 frames.
2993 | if (stream_info->offset == 0) {
2994 | proto_tree_add_item_ret_varint(stream_tree, hf_http3_push_id, tvb, offset, -1, ENC_VARINT_QUIC0x00000004, NULL((void*)0), &lenvar);
2995 | offset += lenvar;
2996 | }
2997 | break;
2998 | case HTTP3_STREAM_TYPE_QPACK_ENCODER:
2999 | offset = dissect_http3_qpack_enc(tvb, pinfo, stream_tree, offset, stream_info, http3_stream);
3000 | break;
3001 | case HTTP3_STREAM_TYPE_QPACK_DECODER:
3002 | offset = dissect_http3_qpack_dec(tvb, pinfo, stream_tree, offset, stream_info, http3_stream);
3003 | break;
3004 | case HTTP3_STREAM_TYPE_WEBTRANSPORT:
3005 | // TODO
3006 | offset = tvb_captured_length(tvb);
3007 | break;
3008 | default:
3009 | // Unknown or reserved stream type, consume everything.
3010 | if (!http3_is_reserved_code(stream_type)) {
3011 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) {
3012 | http3_stream->broken_from_offset = stream_info->offset + offset;
3013 | }
3014 | report_unknown_stream_type(tvb, pinfo, stream_tree, offset, stream_info, http3_stream);
3015 | }
3016 | offset = tvb_captured_length(tvb);
3017 | break;
3018 | }
3019 |
3020 | return offset;
3021 | }
3022 |
3023 | static proto_tree *
3024 | start_http3_tree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
3025 | proto_item * ti;
3026 |
3027 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "HTTP3");
3028 | // Only clear the columns if this is the first HTTP/3 STREAM in the packet.
3029 | if (proto_get_layer_num(pinfo, proto_http3) == 1) {
3030 | col_clear(pinfo->cinfo, COL_INFO);
3031 | }
3032 |
3033 | ti = proto_tree_add_item(tree, proto_http3, tvb, 0, -1, ENC_NA0x00000000);
3034 | return proto_item_add_subtree(ti, ett_http3);
3035 | }
3036 |
3037 | static int
3038 | dissect_http3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
3039 | {
3040 | quic_stream_info * stream_info = (quic_stream_info *)data;
3041 | proto_tree * http3_tree;
3042 | int offset = 0;
3043 | http3_stream_info_t *http3_stream;
3044 | http3_session_info_t *http3_session;
3045 | bool_Bool from_server;
3046 |
3047 | if (!stream_info) {
3048 | return 0;
3049 | }
3050 |
3051 | /* XXX - Handle implementations where QUIC signals FIN in a QUIC frame that
3052 | * has no HTTP/3 frames, and we don't get a Content-Length header either?
3053 | * Unlike HTTP/1.1, there might be nothing for QUIC itself to reassemble,
3054 | * since each HTTP/3 frame has a TPKT-like header with its own length.
3055 | * So tvb might be length 0 here. We also might want to handle the case
3056 | * where FIN is set but the length is too short for a frame. In that
3057 | * case, it is more likely that more DATA will be forthcoming, or would
3058 | * the QUIC stream just abruptly stop like a RESET_STREAM? For that matter,
3059 | * should we try to reassemble any leftover data on a RESET_STREAM? */
3060 |
3061 | switch (QUIC_STREAM_TYPE(stream_info->stream_id)((stream_info->stream_id) & 3U)) {
3062 | case QUIC_STREAM_CLIENT_BIDI0:
3063 | /* Used for HTTP requests and responses. */
3064 | if (!http3_check_frame_size(tvb, pinfo, offset)) {
3065 | return tvb_captured_length(tvb);
3066 | }
3067 | break;
3068 | case QUIC_STREAM_SERVER_BIDI1:
3069 | /* "HTTP/3 does not use server-initiated bidirectional streams,
3070 | * though an extension could define a use for these streams." */
3071 | break;
3072 | case QUIC_STREAM_CLIENT_UNI2:
3073 | case QUIC_STREAM_SERVER_UNI3:
3074 | break;
3075 | }
3076 |
3077 | http3_tree = start_http3_tree(tvb, pinfo, tree);
3078 |
3079 | /* We need a single HTTP/3 stream for both directions */
3080 | from_server = stream_info->from_server;
3081 | if (QUIC_STREAM_TYPE(stream_info->stream_id)((stream_info->stream_id) & 3U) == QUIC_STREAM_CLIENT_BIDI0 && from_server) {
3082 | stream_info->from_server = false0;
3083 | }
3084 | http3_stream = (http3_stream_info_t *)quic_stream_get_proto_data(pinfo, stream_info);
3085 | if (!http3_stream) {
3086 | http3_stream = wmem_new0(wmem_file_scope(), http3_stream_info_t)((http3_stream_info_t*)wmem_alloc0((wmem_file_scope()), sizeof (http3_stream_info_t)));
3087 | nstime_set_unset(&http3_stream->request_ts);
3088 | quic_stream_add_proto_data(pinfo, stream_info, http3_stream);
3089 | http3_stream->id = stream_info->stream_id;
3090 | }
3091 | if (QUIC_STREAM_TYPE(stream_info->stream_id)((stream_info->stream_id) & 3U) == QUIC_STREAM_CLIENT_BIDI0 && from_server) {
3092 | stream_info->from_server = true1;
3093 | }
3094 |
3095 | http3_session = http3_session_lookup_or_create(pinfo);
3096 | http3_session->current_stream = http3_stream;
3097 | http3_stream->direction = http3_packet_get_direction(stream_info);
3098 |
3099 | uint64_t *stream_id = wmem_new(pinfo->pool, uint64_t)((uint64_t*)wmem_alloc((pinfo->pool), sizeof(uint64_t)));
3100 | *stream_id = http3_stream->id;
3101 | p_add_proto_data(pinfo->pool, pinfo, hf_http3_stream_id, 0, stream_id);
3102 |
3103 | // If a STREAM has unknown data, everything afterwards cannot be dissected.
3104 | if (http3_stream->broken_from_offset && http3_stream->broken_from_offset <= stream_info->offset + offset) {
3105 | report_unknown_stream_type(tvb, pinfo, tree, offset, stream_info, http3_stream);
3106 | return tvb_captured_length(tvb);
3107 | }
3108 |
3109 | switch (QUIC_STREAM_TYPE(stream_info->stream_id)((stream_info->stream_id) & 3U)) {
3110 | case QUIC_STREAM_CLIENT_BIDI0:
3111 | /* Used for HTTP requests and responses. */
3112 | dissect_http3_client_bidi_stream(tvb, pinfo, http3_tree, offset, stream_info, http3_stream);
3113 | break;
3114 |
3115 | case QUIC_STREAM_SERVER_BIDI1:
3116 | /* "HTTP/3 does not use server-initiated bidirectional streams,
3117 | * though an extension could define a use for these streams." */
3118 | // XXX expert info?
3119 | return tvb_captured_length(tvb);
3120 |
3121 | case QUIC_STREAM_CLIENT_UNI2:
3122 | case QUIC_STREAM_SERVER_UNI3:
3123 | dissect_http3_uni_stream(tvb, pinfo, http3_tree, offset, stream_info, http3_stream);
3124 | break;
3125 | }
3126 |
3127 | return tvb_captured_length(tvb);
3128 | }
3129 |
3130 | static int
3131 | dissect_http3_datagram(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
3132 | quic_datagram_info * datagram_info = (quic_datagram_info *)data;
3133 | uint64_t request_stream_id;
3134 | proto_item * ti;
3135 | proto_tree * http3_tree, * datragram_tree, * stream_id_tree;
3136 | uint32_t lenvar;
3137 | int offset = 0;
3138 |
3139 | if (!datagram_info) {
3140 | return 0;
3141 | }
3142 |
3143 | http3_tree = start_http3_tree(tvb, pinfo, tree);
3144 | col_append_sep_str(pinfo->cinfo, COL_INFO, ", ", "DATAGRAM");
3145 |
3146 | ti = proto_tree_add_item(http3_tree, hf_http3_datagram, tvb, offset, -1, ENC_NA0x00000000);
3147 | datragram_tree = proto_item_add_subtree(ti, ett_http3_datagram);
3148 |
3149 | ti = proto_tree_add_item_ret_varint(datragram_tree, hf_http3_datagram_quarter_stream_id, tvb, offset, -1, ENC_VARINT_QUIC0x00000004, &request_stream_id, &lenvar);
3150 | stream_id_tree = proto_item_add_subtree(ti, ett_http3_datagram_stream_id);
3151 |
3152 | if (request_stream_id > ((1ULL << 60) - 1)) {
3153 | proto_tree_add_expert_format(stream_id_tree, pinfo, &ei_http3_datagram_invalid_stream_id, tvb, offset, lenvar,
3154 | "Quarter Stream ID is too big");
3155 | return tvb_captured_length(tvb);
3156 | }
3157 |
3158 | request_stream_id = request_stream_id * 4;
3159 | ti = proto_tree_add_uint64(stream_id_tree, hf_http3_datagram_request_stream_id, tvb, offset, lenvar, request_stream_id);
3160 | proto_item_set_generated(ti);
3161 | offset += lenvar;
3162 |
3163 | proto_tree_add_item(datragram_tree, hf_http3_datagram_payload, tvb, offset, -1, ENC_NA0x00000000);
3164 |
3165 | return tvb_captured_length(tvb);
3166 | }
3167 |
3168 | #ifdef HAVE_NGHTTP31
3169 | static void
3170 | register_static_headers(void)
3171 | {
3172 | header_fields_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL((void*)0));
3173 |
3174 | /*
3175 | * Here hf[x].hfinfo.name is a header method which is used as key
3176 | * for matching ids while processing http3 packets.
3177 | */
3178 | static hf_register_info hf[] = {
3179 | { &hf_http3_headers_authority,
3180 | { ":authority", "http3.headers.authority",
3181 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3182 | "Authority portion of the target URI", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3183 | },
3184 | { &hf_http3_headers_status,
3185 | { ":status", "http3.headers.status",
3186 | FT_UINT16, BASE_DEC, VALS(vals_http_status_code)((0 ? (const struct _value_string*)0 : ((vals_http_status_code )))), 0x0,
3187 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3188 | },
3189 | { &hf_http3_headers_path,
3190 | { ":path", "http3.headers.path",
3191 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3192 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3193 | },
3194 | { &hf_http3_headers_protocol,
3195 | { ":protocol", "http3.headers.protocol",
3196 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3197 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3198 | },
3199 | { &hf_http3_headers_method,
3200 | { ":method", "http3.headers.method",
3201 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3202 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3203 | },
3204 | { &hf_http3_headers_scheme,
3205 | { ":scheme", "http3.headers.scheme",
3206 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3207 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3208 | },
3209 | { &hf_http3_headers_accept,
3210 | { "accept", "http3.headers.accept",
3211 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3212 | "Media types that are acceptable to the user agent", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3213 | },
3214 | { &hf_http3_headers_accept_charset,
3215 | { "accept-charset", "http3.headers.accept_charset",
3216 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3217 | "Acceptable charsets in textual responses for the user agent", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3218 | },
3219 | { &hf_http3_headers_accept_encoding,
3220 | { "accept-encoding", "http3.headers.accept_encoding",
3221 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3222 | "Acceptable content codings (like compression) in responses for the user agent", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3223 | },
3224 | { &hf_http3_headers_accept_language,
3225 | { "accept-language", "http3.headers.accept_language",
3226 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3227 | "Preferred natural languages for the user agent", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3228 | },
3229 | { &hf_http3_headers_accept_ranges,
3230 | { "accept-ranges", "http3.headers.accept_ranges",
3231 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3232 | "Bytes range which server may use for partial data transfer", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3233 | },
3234 | { &hf_http3_headers_access_control_allow_origin,
3235 | { "access-control-allow-origin", "http3.headers.access_control_allow_origin",
3236 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3237 | "Origin control for cross-origin resource sharing", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3238 | },
3239 | { &hf_http3_headers_age,
3240 | { "age", "http3.headers.age",
3241 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3242 | "Time in seconds which was spent for transferring data through proxy", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3243 | },
3244 | { &hf_http3_headers_allow,
3245 | { "allow", "http3.headers.allow",
3246 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3247 | "List of allowed methods for request", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3248 | },
3249 | { &hf_http3_headers_authorization,
3250 | { "authorization", "http3.headers.authorization",
3251 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3252 | "Credentials for a server-side authorization", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3253 | },
3254 | { &hf_http3_headers_cache_control,
3255 | { "cache-control", "http3.headers.cache_control",
3256 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3257 | "Request or response directives for a cache control", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3258 | },
3259 | { &hf_http3_headers_content_disposition,
3260 | { "content-disposition", "http3.headers.content_disposition",
3261 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3262 | "Indicates that response will be displayed as page or downloaded with dialog box", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3263 | },
3264 | { &hf_http3_headers_content_encoding,
3265 | { "content-encoding", "http3.headers.content_encoding",
3266 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3267 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3268 | },
3269 | { &hf_http3_headers_content_language,
3270 | { "content-language", "http3.headers.content_language",
3271 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3272 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3273 | },
3274 | { &hf_http3_headers_content_length,
3275 | { "content-length", "http3.headers.content_length",
3276 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3277 | "Size of body in bytes", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3278 | },
3279 | { &hf_http3_headers_content_location,
3280 | { "content-location", "http3.headers.content_location",
3281 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3282 | "Alternative URL for a response data", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3283 | },
3284 | { &hf_http3_headers_content_range,
3285 | { "content-range", "http3.headers.content_range",
3286 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3287 | "Range of bytes which was sent by server for partial data transfer", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3288 | },
3289 | { &hf_http3_headers_content_type,
3290 | { "content-type", "http3.headers.content_type",
3291 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3292 | "MIME type of response", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3293 | },
3294 | { &hf_http3_headers_cookie,
3295 | { "cookie", "http3.headers.cookie",
3296 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3297 | "Stored cookies", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3298 | },
3299 | { &hf_http3_headers_date,
3300 | { "date", "http3.headers.date",
3301 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3302 | "Date and time at which the data was originated", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3303 | },
3304 | { &hf_http3_headers_etag,
3305 | { "etag", "http3.headers.etag",
3306 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3307 | "Directive for version indication of resource", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3308 | },
3309 | { &hf_http3_headers_expect,
3310 | { "expect", "http3.headers.expect",
3311 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3312 | "Expectations that need to be fulfilled for correct request", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3313 | },
3314 | { &hf_http3_headers_expires,
3315 | { "expires", "http3.headers.expires",
3316 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3317 | "Data after which resource will be stale", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3318 | },
3319 | { &hf_http3_headers_from,
3320 | { "from", "http3.headers.from",
3321 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3322 | "Email of a person who responsible for a requesting data", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3323 | },
3324 | { &hf_http3_headers_if_match,
3325 | { "if-match", "http3.headers.if_match",
3326 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3327 | "Mechanism for requesting data matched by a list of ETags", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3328 | },
3329 | { &hf_http3_headers_if_modified_since,
3330 | { "if-modified-since", "http3.headers.if_modified_since",
3331 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3332 | "Resource will be sent with status code 200 if it was modified otherwise with status code 304", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3333 | },
3334 | { &hf_http3_headers_if_none_match,
3335 | { "if-none-match", "http3.headers.if_none_match",
3336 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3337 | "Mechanism for requesting data not matched by a list of ETags", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3338 | },
3339 | { &hf_http3_headers_if_range,
3340 | { "if-range", "http3.headers.if_range",
3341 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3342 | "Mechanism for a range request which is used to check if a resource was modified", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3343 | },
3344 | { &hf_http3_headers_if_unmodified_since,
3345 | { "if-unmodified-since", "http3.headers.if_unmodified_since",
3346 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3347 | "Resource will be processed if it was not modified otherwise 412 error will be returned", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3348 | },
3349 | { &hf_http3_headers_last_modified,
3350 | { "last-modified", "http3.headers.last_modified",
3351 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3352 | "Date and time at which the origin server believes the resource was last modified", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3353 | },
3354 | { &hf_http3_headers_link,
3355 | { "link", "http3.headers.link",
3356 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3357 | "Mechanism for indicating that resource will be preloaded", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3358 | },
3359 | { &hf_http3_headers_location,
3360 | { "location", "http3.headers.location",
3361 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3362 | "Mechanism for indicating that client will be redirected", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3363 | },
3364 | { &hf_http3_headers_max_forwards,
3365 | { "max-forwards", "http3.headers.max_forwards",
3366 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3367 | "Mechanism for limiting the number of proxies", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3368 | },
3369 | { &hf_http3_headers_proxy_authenticate,
3370 | { "proxy-authenticate", "http3.headers.proxy_authenticate",
3371 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3372 | "Authentication method that should be used to gain access to a resource behind a proxy server", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3373 | },
3374 | { &hf_http3_headers_proxy_authorization,
3375 | { "proxy-authorization", "http3.headers.proxy_authorization",
3376 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3377 | "Credentials for a proxy-side authorization", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3378 | },
3379 | { &hf_http3_headers_range,
3380 | { "range", "http3.headers.range",
3381 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3382 | "Range of resource bytes that server should return", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3383 | },
3384 | { &hf_http3_headers_referer,
3385 | { "referer", "http3.headers.referer",
3386 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3387 | "Address of the previous web page", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3388 | },
3389 | { &hf_http3_headers_refresh,
3390 | { "refresh", "http3.headers.refresh",
3391 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3392 | "Time in seconds after which client will be redirected by given url", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3393 | },
3394 | { &hf_http3_headers_retry_after,
3395 | { "retry-after", "http3.headers.retry_after",
3396 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3397 | "Mechanism to indicate when resource expected to be available", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3398 | },
3399 | { &hf_http3_headers_server,
3400 | { "server", "http3.headers.server",
3401 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3402 | "Information about server software", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3403 | },
3404 | { &hf_http3_headers_set_cookie,
3405 | { "set-cookie", "http3.headers.set_cookie",
3406 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3407 | "Send a cookie to the client", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3408 | },
3409 | { &hf_http3_headers_strict_transport_security,
3410 | { "strict-transport-security", "http3.headers.strict_transport_security",
3411 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3412 | "HSTS indicates that resource should be accessed only using HTTPS", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3413 | },
3414 | { &hf_http3_headers_user_agent,
3415 | { "user-agent", "http3.headers.user_agent",
3416 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3417 | "Information about client software", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3418 | },
3419 | { &hf_http3_headers_vary,
3420 | { "vary", "http3.headers.vary",
3421 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3422 | "Mechanism for selecting which header will be used for content negotiation algorithm", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3423 | },
3424 | { &hf_http3_headers_via,
3425 | { "via", "http3.headers.via",
3426 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3427 | "Additional information for loop detection and protocol capabilities in proxy requests", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3428 | },
3429 | { &hf_http3_headers_www_authenticate,
3430 | { "www-authenticate", "http3.headers.www_authenticate",
3431 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3432 | "Authentication method that should be used to gain access to a resource", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3433 | }
3434 | };
3435 |
3436 | char *header_name;
3437 | for (unsigned i = 0; i < G_N_ELEMENTS(hf)(sizeof (hf) / sizeof ((hf)[0])); ++i) {
3438 | header_name = g_strdup(hf[i].hfinfo.name)g_strdup_inline (hf[i].hfinfo.name);
3439 |
3440 | g_hash_table_insert(header_fields_hash, header_name, &hf[i].hfinfo.id);
3441 | }
3442 | proto_register_field_array(proto_http3, hf, G_N_ELEMENTS(hf)(sizeof (hf) / sizeof ((hf)[0])));
3443 | }
3444 | #endif /* HAVE_NGHTTP3 */
3445 |
3446 | void
3447 | proto_register_http3(void)
3448 | {
3449 | expert_module_t *expert_http3;
3450 | module_t *module_http3 _U___attribute__((unused));
3451 |
3452 | static hf_register_info hf[] = {
3453 | { &hf_http3_stream_uni,
3454 | { "Uni Stream", "http3.stream.uni",
3455 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
3456 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3457 | },
3458 | { &hf_http3_stream_uni_type,
3459 | { "Uni Stream Type", "http3.stream_uni_type",
3460 | FT_UINT64, BASE_HEX|BASE_VAL64_STRING0x00000400, VALS64(http3_stream_types)((0 ? (const struct _val64_string*)0 : ((http3_stream_types)) )), 0x0,
3461 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3462 | },
3463 | { &hf_http3_stream_bidi,
3464 | { "Request Stream", "http3.stream",
3465 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
3466 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3467 | },
3468 | { &hf_http3_stream_id,
3469 | { "Stream ID", "http3.stream.id",
3470 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3471 | "62-bit value identical to the QUIC Stream ID", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3472 | },
3473 | { &hf_http3_push_id,
3474 | { "Push ID", "http3.push_id",
3475 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3476 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3477 | },
3478 | { &hf_http3_frame,
3479 | { "Frame", "http3.frame",
3480 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
3481 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3482 | },
3483 | { &hf_http3_frame_type,
3484 | { "Type", "http3.frame_type",
3485 | FT_UINT64, BASE_HEX|BASE_VAL64_STRING0x00000400, VALS64(http3_frame_types)((0 ? (const struct _val64_string*)0 : ((http3_frame_types))) ), 0x0,
3486 | "Frame Type", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3487 | },
3488 | { &hf_http3_frame_length,
3489 | { "Length", "http3.frame_length",
3490 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3491 | "Length of the Frame Payload", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3492 | },
3493 | { &hf_http3_frame_payload,
3494 | { "Frame Payload", "http3.frame_payload",
3495 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3496 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3497 | },
3498 |
3499 | /* Generated Fields */
3500 | { &hf_http3_time,
3501 | { "Time since request", "http3.time",
3502 | FT_RELATIVE_TIME, BASE_NONE, NULL((void*)0), 0,
3503 | "Time since the request was sent", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3504 | },
3505 | { &hf_http3_request_in,
3506 | { "Request in frame", "http3.request_in",
3507 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST)((gpointer) (glong) (FT_FRAMENUM_REQUEST)), 0x0,
3508 | "This frame is part of a response to a HTTP/3 request that began in this frame", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3509 | },
3510 | { &hf_http3_response_in,
3511 | { "Response in frame", "http3.response_in",
3512 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE)((gpointer) (glong) (FT_FRAMENUM_RESPONSE)), 0x0,
3513 | "This frame is part of a request responded in the frame with this number", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3514 | },
3515 |
3516 | /* Data */
3517 | { &hf_http3_data,
3518 | { "Data", "http3.data",
3519 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3520 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3521 | },
3522 | { &hf_http3_encoded_entity,
3523 | { "Content-encoded entity body", "http3.body.content_encoded",
3524 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
3525 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3526 | },
3527 | /* Body fragments */
3528 | { &hf_http3_body_fragments,
3529 | { "Body fragments", "http3.body.fragments",
3530 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
3531 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3532 | },
3533 | { &hf_http3_body_fragment,
3534 | { "Body fragment", "http3.body.fragment",
3535 | FT_FRAMENUM, BASE_NONE, NULL((void*)0), 0x0,
3536 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3537 | },
3538 | { &hf_http3_body_fragment_overlap,
3539 | { "Body fragment overlap", "http3.body.fragment.overlap",
3540 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0,
3541 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3542 | },
3543 | { &hf_http3_body_fragment_overlap_conflicts,
3544 | { "Body fragment overlapping with conflicting data", "http3.body.fragment.overlap.conflicts",
3545 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0,
3546 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3547 | },
3548 | { &hf_http3_body_fragment_multiple_tails,
3549 | { "Body has multiple tail fragments", "http3.body.fragment.multiple_tails",
3550 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0,
3551 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3552 | },
3553 | { &hf_http3_body_fragment_too_long_fragment,
3554 | { "Body fragment too long", "http3.body.fragment.too_long_fragment",
3555 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0,
3556 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3557 | },
3558 | { &hf_http3_body_fragment_error,
3559 | { "Body defragment error", "http3.body.fragment.error",
3560 | FT_FRAMENUM, BASE_NONE, NULL((void*)0), 0x0,
3561 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3562 | },
3563 | { &hf_http3_body_fragment_count,
3564 | { "Body fragment count", "http3.body.fragment.count",
3565 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3566 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3567 | },
3568 | { &hf_http3_body_reassembled_in,
3569 | { "Reassembled body in frame", "http3.body.reassembled.in",
3570 | FT_FRAMENUM, BASE_NONE, NULL((void*)0), 0x0,
3571 | "Reassembled body in frame number", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3572 | },
3573 | { &hf_http3_body_reassembled_length,
3574 | { "Reassembled body length", "http3.body.reassembled.length",
3575 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3576 | "Reassembled body in frame number", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3577 | },
3578 | { &hf_http3_body_reassembled_data,
3579 | { "Reassembled body data", "http3.body.reassembled.data",
3580 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3581 | "Reassembled body data for multisegment PDU spanning across DATAs", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3582 | },
3583 |
3584 | /* Headers */
3585 | { &hf_http3_headers_count,
3586 | { "Headers Count", "http3.headers.count",
3587 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3588 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3589 | },
3590 | { &hf_http3_header,
3591 | { "Header", "http3.headers.header",
3592 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
3593 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3594 | },
3595 | { &hf_http3_headers_decoded_length,
3596 | { "Decoded Headers Length", "http3.headers.decoded_length",
3597 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3598 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3599 | },
3600 | { &hf_http3_header_name_length,
3601 | { "Name Length", "http3.headers.header.name.length",
3602 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3603 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3604 | },
3605 | { &hf_http3_header_name,
3606 | { "Name", "http3.header.header.name",
3607 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3608 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3609 | },
3610 | { &hf_http3_header_value_length,
3611 | { "Value Length", "http3.headers.header.value.length",
3612 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3613 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3614 | },
3615 | { &hf_http3_header_value,
3616 | { "Value", "http3.headers.header.value",
3617 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3618 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3619 | },
3620 | { &hf_http3_header_request_full_uri,
3621 | { "Full request URI", "http3.request.full_uri",
3622 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3623 | "The full requested URI (including host name)", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3624 | },
3625 |
3626 | /* QPACK */
3627 | { &hf_http3_header_qpack_blocked,
3628 | { "HEADERS head-of-line-blocked on QPACK encoder stream", "http3.header.qpack.blocked",
3629 | FT_BOOLEAN, BASE_NONE, NULL((void*)0), 0x0,
3630 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3631 | },
3632 | { &hf_http3_header_qpack_blocked_stream_rcint,
3633 | { "Required instruction count", "http3.header.qpack.blocked.rcint",
3634 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3635 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3636 | },
3637 | { &hf_http3_header_qpack_blocked_decoder_wicnt,
3638 | { "Available instruction count", "http3.header.qpack.blocked.wcint",
3639 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3640 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3641 | },
3642 | { &hf_http3_qpack_encoder,
3643 | { "QPACK encoder", "http3.qpack.encoder",
3644 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3645 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3646 | },
3647 | { &hf_http3_qpack_encoder_icnt,
3648 | { "QPACK encoder instruction count", "http3.qpack.encoder.icnt",
3649 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3650 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3651 | },
3652 | { &hf_http3_qpack_encoder_icnt_inc,
3653 | { "QPACK encoder instruction count increment", "http3.qpack.encoder.icnt.inc",
3654 | FT_UINT32, BASE_DEC, NULL((void*)0), 0x0,
3655 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3656 | },
3657 | { &hf_http3_qpack_encoder_opcode_insert_indexed,
3658 | { "Insert with Name Reference", "http3.qpack.encoder.opcode.insert_indexed",
3659 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3660 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3661 | },
3662 | { &hf_http3_qpack_encoder_opcode_insert_indexed_ref,
3663 | { "Name Reference", "http3.qpack.encoder.opcode.insert_indexed.ref",
3664 | FT_UINT64, BASE_HEX, NULL((void*)0), 0x0,
3665 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3666 | },
3667 | { &hf_http3_qpack_encoder_opcode_insert_indexed_val,
3668 | { "Value", "http3.qpack.encoder.opcode.insert_indexed.val",
3669 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3670 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3671 | },
3672 | { &hf_http3_qpack_encoder_opcode_insert_indexed_hval,
3673 | { "Value (Huffman)", "http3.qpack.encoder.opcode.insert_indexed.hval",
3674 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3675 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3676 | },
3677 | { &hf_http3_qpack_encoder_opcode_insert,
3678 | { "Insert with Literal Name", "http3.qpack.encoder.opcode.insert",
3679 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3680 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3681 | },
3682 | { &hf_http3_qpack_encoder_opcode_insert_name,
3683 | { "Literal Name", "http3.qpack.encoder.opcode.insert.name",
3684 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3685 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3686 | },
3687 | { &hf_http3_qpack_encoder_opcode_insert_hname,
3688 | { "Literal Name (Huffman)", "http3.qpack.encoder.opcode.insert.hname",
3689 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3690 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3691 | },
3692 | { &hf_http3_qpack_encoder_opcode_insert_val,
3693 | { "Value", "http3.qpack.encoder.opcode.insert.val",
3694 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3695 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3696 | },
3697 | { &hf_http3_qpack_encoder_opcode_insert_hval,
3698 | { "Value (Huffman)", "http3.qpack.encoder.opcode.insert.hval",
3699 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3700 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3701 | },
3702 | { &hf_http3_qpack_encoder_opcode_duplicate,
3703 | { "Duplicate", "http3.qpack.encoder.opcode.duplicate",
3704 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3705 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3706 | },
3707 | { &hf_http3_qpack_encoder_opcode_dtable_cap,
3708 | { "Set Dynamic Table Capacity", "http3.qpack.encoder.opcode.dtable_cap",
3709 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3710 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3711 | },
3712 | { &hf_http3_qpack_encoder_opcode_dtable_cap_val,
3713 | { "Capacity", "http3.qpack.encoder.opcode.dtable_cap.val",
3714 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3715 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3716 | },
3717 | { &hf_http3_qpack_decoder,
3718 | { "QPACK decoder", "http3.qpack.decoder",
3719 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3720 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3721 | },
3722 | { &hf_http3_qpack_decoder_opcode_section_ack,
3723 | { "Section ACK", "http3.qpack.decoder.opcode.section_ack",
3724 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3725 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3726 | },
3727 | { &hf_http3_qpack_decoder_opcode_section_ack_stream_id,
3728 | { "Section ACK stream ID", "http3.qpack.decoder.opcode.section_ack.stream_id",
3729 | FT_UINT64, BASE_HEX, NULL((void*)0), 0x0,
3730 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3731 | },
3732 | { &hf_http3_qpack_decoder_opcode_cancel_stream,
3733 | { "Cancel stream", "http3.qpack.decoder.opcode.cancel_stream",
3734 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3735 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3736 | },
3737 | { &hf_http3_qpack_decoder_opcode_cancel_stream_id,
3738 | { "Cancel stream stream ID", "http3.qpack.decoder.opcode.cancel_stream.id",
3739 | FT_UINT64, BASE_HEX, NULL((void*)0), 0x0,
3740 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3741 | },
3742 | { &hf_http3_qpack_decoder_opcode_icnt_increment,
3743 | { "Instruction count increment", "http3.qpack.decoder.opcode.icnt_inc",
3744 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3745 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3746 | },
3747 | { &hf_http3_qpack_decoder_opcode_icnt_increment_value,
3748 | { "Instruction count increment value", "http3.qpack.decoder.opcode.icnt_inc.val",
3749 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3750 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3751 | },
3752 |
3753 | /* Settings */
3754 | { &hf_http3_settings,
3755 | { "Settings", "http3.settings",
3756 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
3757 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3758 | },
3759 | { &hf_http3_settings_identifier,
3760 | { "Settings Identifier", "http3.settings.id",
3761 | FT_UINT64, BASE_HEX|BASE_VAL64_STRING0x00000400, VALS64(http3_settings_vals)((0 ? (const struct _val64_string*)0 : ((http3_settings_vals) ))), 0x0,
3762 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3763 | },
3764 | { &hf_http3_settings_value,
3765 | { "Settings Value", "http3.settings.value",
3766 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3767 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3768 | },
3769 | { &hf_http3_settings_qpack_max_table_capacity,
3770 | { "Max Table Capacity", "http3.settings.qpack.max_table_capacity",
3771 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3772 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3773 | },
3774 | { &hf_http3_settings_max_field_section_size,
3775 | { "Max header list size", "http3.settings.max_field_section_size",
3776 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3777 | "The default value is unlimited.", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3778 | },
3779 | { &hf_http3_settings_qpack_blocked_streams,
3780 | { "Blocked Streams", "http3.settings.qpack.blocked_streams",
3781 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3782 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3783 | },
3784 | { &hf_http3_settings_extended_connect,
3785 | { "Extended CONNECT", "http3.settings.extended_connect",
3786 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3787 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3788 | },
3789 | { &hf_http3_settings_webtransport,
3790 | { "WebTransport", "http3.settings.webtransport",
3791 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3792 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3793 | },
3794 | { &hf_http3_settings_h3_datagram,
3795 | { "H3 DATAGRAM", "http3.settings.h3_datagram",
3796 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3797 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3798 | },
3799 | { &hf_http3_settings_h3_datagram_draft04,
3800 | { "H3 DATAGRAM Draft04", "http3.settings.h3_datagram_draft04",
3801 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3802 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3803 | },
3804 |
3805 | /* Priority Update */
3806 | { &hf_http3_priority_update_element_id,
3807 | { "Priority Update Element ID", "http3.priority_update_element_id",
3808 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3809 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3810 | },
3811 | { &hf_http3_priority_update_field_value,
3812 | { "Priority Update Field Value", "http3.priority_update_field_value",
3813 | FT_STRING, BASE_NONE, NULL((void*)0), 0x0,
3814 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3815 | },
3816 |
3817 | /* Datagram */
3818 | { &hf_http3_datagram,
3819 | { "Datagram", "http3.datagram",
3820 | FT_NONE, BASE_NONE, NULL((void*)0), 0x0,
3821 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3822 | },
3823 | { &hf_http3_datagram_quarter_stream_id,
3824 | { "Quarter Stream ID", "http3.datagram.quarter_stream_id",
3825 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3826 | "Request stream id divided by 4", HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3827 | },
3828 | { &hf_http3_datagram_request_stream_id,
3829 | { "Associated Request Stream ID", "http3.datagram.request_stream_id",
3830 | FT_UINT64, BASE_DEC, NULL((void*)0), 0x0,
3831 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3832 | },
3833 | { &hf_http3_datagram_payload,
3834 | { "Datagram Payload", "http3.datagram.payload",
3835 | FT_BYTES, BASE_NONE, NULL((void*)0), 0x0,
3836 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }
3837 | },
3838 | };
3839 |
3840 | static int *ett[] = {&ett_http3,
3841 | &ett_http3_stream_uni,
3842 | &ett_http3_stream_bidi,
3843 | &ett_http3_frame,
3844 | &ett_http3_body_fragment,
3845 | &ett_http3_body_fragments,
3846 | &ett_http3_encoded_entity,
3847 | &ett_http3_settings,
3848 | &ett_http3_headers,
3849 | &ett_http3_headers_qpack_blocked,
3850 | &ett_http3_qpack_update,
3851 | &ett_http3_qpack_opcode,
3852 | &ett_http3_datagram,
3853 | &ett_http3_datagram_stream_id};
3854 |
3855 | static ei_register_info ei[] = {
3856 | { &ei_http3_unknown_stream_type,
3857 | { "http3.unknown_stream_type", PI_UNDECODED0x05000000, PI_WARN0x00600000,
3858 | "An unknown stream type was encountered", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }
3859 | },
3860 | { &ei_http3_qpack_failed,
3861 | { "http3.qpack_enc_failed", PI_UNDECODED0x05000000, PI_NOTE0x00400000,
3862 | "Error decoding QPACK buffer", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }
3863 | },
3864 | { &ei_http3_prefix_int_failed,
3865 | { "http3.prefix_int.failed", PI_UNDECODED0x05000000, PI_WARN0x00600000,
3866 | "Error decoding prefixed integer (too big)", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }
3867 | },
3868 | { &ei_http3_huffman_failed,
3869 | { "http3.huffman.failed", PI_UNDECODED0x05000000, PI_WARN0x00600000,
3870 | "Error in Huffman decoding", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }
3871 | },
3872 | { &ei_http3_header_encoded_state ,
3873 | { "http3.expert.header.encoded_state", PI_DEBUG0x08000000, PI_NOTE0x00400000,
3874 | "HTTP3 header encoded block", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }
3875 | },
3876 | { &ei_http3_header_decoding_failed ,
3877 | { "http3.expert.header_decoding.failed", PI_UNDECODED0x05000000, PI_NOTE0x00400000,
3878 | "Failed to decode HTTP3 header name/value", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }
3879 | },
3880 | { &ei_http3_header_decoding_blocked,
3881 | { "http3.expert.header_decoding.blocked", PI_UNDECODED0x05000000, PI_NOTE0x00400000,
3882 | "Failed to decode HTTP3 header name/value (blocked on QPACK)", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}}}
3883 | },
3884 | { &ei_http3_header_decoding_no_output,
3885 | { "http3.expert.header_decoding.no_output", PI_UNDECODED0x05000000, PI_NOTE0x00400000,
3886 | "Failed to decode HTTP3 header name/value (QPACK decoder no emission)", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}}}
3887 | },
3888 | /* Stopping due to excessive headers is possibly PI_SECURITY
3889 | * (decompression bomb or other dangerous implemention). */
3890 | { &ei_http3_header_size,
3891 | { "http3.expert.header_decoding.header_size_exceeded", PI_UNDECODED0x05000000, PI_WARN0x00600000,
3892 | "QPACK decompression stopped after " G_STRINGIFY(QPACK_MAX_HEADER_SIZE)"1048576" " bytes", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}}}
3893 | },
3894 | { &ei_http3_header_transfer_encoding,
3895 | { "http3.header.transfer_encoding", PI_PROTOCOL0x09000000, PI_WARN0x00600000,
3896 | "The Transfer-Encoding header field MUST NOT be used in HTTP/3", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}}}
3897 | },
3898 | { &ei_http3_body_decompression_failed,
3899 | { "http3.body.content_encoded.failed", PI_UNDECODED0x05000000, PI_WARN0x00600000,
3900 | "Unable to decompress content-encoded entity", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}}}
3901 | },
3902 | { &ei_http3_datagram_invalid_stream_id,
3903 | { "http3.expert.datagram.invalid_stream_id", PI_UNDECODED0x05000000, PI_WARN0x00600000,
3904 | "Failed to decode HTTP3 datagram stream id", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}}}
3905 | },
3906 | };
3907 |
3908 | proto_http3 = proto_register_protocol("Hypertext Transfer Protocol Version 3", "HTTP3", "http3");
3909 |
3910 | proto_register_field_array(proto_http3, hf, array_length(hf)(sizeof (hf) / sizeof (hf)[0]));
3911 | proto_register_subtree_array(ett, array_length(ett)(sizeof (ett) / sizeof (ett)[0]));
3912 |
3913 | module_http3 = prefs_register_protocol(proto_http3, NULL((void*)0));
3914 |
3915 | expert_http3 = expert_register_protocol(proto_http3);
3916 | expert_register_field_array(expert_http3, ei, array_length(ei)(sizeof (ei) / sizeof (ei)[0]));
3917 |
3918 | http3_handle = register_dissector("http3", dissect_http3, proto_http3);
3919 | http3_datagram_handle = register_dissector("http3.datagram", dissect_http3_datagram, proto_http3);
3920 | #ifdef HAVE_NGHTTP31
3921 | /* Fill hash table with static headers */
3922 | register_static_headers();
3923 |
3924 | #if defined(HAVE_ZLIB1) || defined(HAVE_ZLIBNG) || defined(HAVE_BROTLI1) || defined(HAVE_ZSTD1)
3925 | prefs_register_bool_preference(module_http3, "decompress_body",
3926 | "Decompress entity bodies",
3927 | "Whether to decompress entity bodies that are compressed "
3928 | "using \"Content-Encoding: \"",
3929 | &http3_decompress_body);
3930 | #else
3931 | prefs_register_obsolete_preference(module_http3, "decompress_body");
3932 | #endif
3933 |
3934 | reassembly_table_register(&http3_body_reassembly_table,
3935 | &quic_reassembly_table_functions);
3936 | #endif
3937 |
3938 | http3_follow_tap = register_tap("http3_follow");
3939 |
3940 | /* Just use the QUIC functions for now, since the IDs are the same.
3941 | * This may change once QUIC multipath is supported. */
3942 | register_follow_stream(proto_http3, "http3_follow", quic_follow_conv_filter, quic_follow_index_filter, udp_follow_address_filter, udp_port_to_display, follow_quic_tap_listener, get_quic_connections_count, quic_get_sub_stream_id);
3943 | }
3944 |
3945 | void
3946 | proto_reg_handoff_http3(void)
3947 | {
3948 | #ifdef HAVE_NGHTTP31
3949 | media_type_dissector_table = find_dissector_table("media_type");
3950 | media_handle = find_dissector_add_dependency("media", proto_http3);
3951 |
3952 | register_eo_t *http_eo = get_eo_by_name("http");
3953 | if (http_eo) {
3954 | http_eo_tap = find_tap_id(get_eo_tap_listener_name(http_eo));
3955 | }
3956 | #endif
3957 |
3958 | dissector_add_string("quic.proto", "h3", http3_handle);
3959 | dissector_add_string("quic.proto.datagram", "h3", http3_datagram_handle);
3960 | }
3961 |
3962 | /**
3963 | * Implementation of helper functions.
3964 | */
3965 | static http3_file_local_ctx *g_http3_file_local_ctx;
3966 |
3967 | static unsigned
3968 | http3_conn_info_hash(const void *key)
3969 | {
3970 | uint8_t bkey[QUIC_MAX_CID_LENGTH20];
3971 | const quic_cid_t *v;
3972 | unsigned h = 0;
3973 |
3974 | if (key) {
3975 | v = (const quic_cid_t *)key;
3976 | memset(&bkey[0], 0, QUIC_MAX_CID_LENGTH20);
3977 | memcpy(&bkey[0], &v->cid[0], MIN(v->len, QUIC_MAX_CID_LENGTH)(((v->len) < (20)) ? (v->len) : (20)));
3978 | h = wmem_strong_hash(&bkey[0], QUIC_MAX_CID_LENGTH20);
3979 | }
3980 | return h;
3981 | }
3982 |
3983 | static gboolean
3984 | http3_conn_info_equal(const void *lhs, const void *rhs)
3985 | {
3986 | const quic_cid_t *a = (const quic_cid_t *)lhs;
3987 | const quic_cid_t *b = (const quic_cid_t *)rhs;
3988 | size_t alen = a->len;
3989 | size_t blen = b->len;
3990 |
3991 | return alen == blen && memcmp(&a->cid[0], &b->cid[0], alen) == 0;
3992 | }
3993 |
3994 | #ifdef HAVE_NGHTTP31
3995 | /* Due to QPACK compression, we may get lots of relatively large
3996 | header decoded_header_fields (e.g., 4KiB). Allocating each of them requires lots
3997 | of memory. The maximum compression is achieved in QPACK by
3998 | referencing header field stored in dynamic table by one or two
3999 | bytes. We reduce memory usage by caching header field in this
4000 | wmem_map_t to reuse its memory region when we see the same header
4001 | field next time. */
4002 |
4003 | static size_t
4004 | http3_hdrcache_length(const void *vv)
4005 | {
4006 | const uint8_t *v = (const uint8_t *)vv;
4007 | uint32_t namelen, valuelen;
4008 |
4009 | namelen = pntohu32(v);
4010 | valuelen = pntohu32(v + sizeof(namelen) + namelen);
4011 |
4012 | return namelen + sizeof(namelen) + valuelen + sizeof(valuelen);
4013 | }
4014 |
4015 | static unsigned
4016 | http3_hdrcache_hash(const void *key)
4017 | {
4018 | return wmem_strong_hash((const uint8_t *)key, http3_hdrcache_length(key));
4019 | }
4020 |
4021 | static gboolean
4022 | http3_hdrcache_equal(const void *lhs, const void *rhs)
4023 | {
4024 | const uint8_t *a = (const uint8_t *)lhs;
4025 | const uint8_t *b = (const uint8_t *)rhs;
4026 | size_t alen = http3_hdrcache_length(a);
4027 | size_t blen = http3_hdrcache_length(b);
4028 |
4029 | return alen == blen && memcmp(a, b, alen) == 0;
4030 | }
4031 | #endif
4032 |
4033 | /* Deallocation callback */
4034 | static bool_Bool
4035 | http3_file_local_ctx_del_cb(wmem_allocator_t *allocator _U___attribute__((unused)), wmem_cb_event_t event _U___attribute__((unused)), void *user_data _U___attribute__((unused)))
4036 | {
4037 | g_http3_file_local_ctx = NULL((void*)0);
4038 | return false0;
4039 | }
4040 |
4041 | static http3_file_local_ctx *
4042 | http3_get_file_local_ctx(void)
4043 | {
4044 | if (g_http3_file_local_ctx == NULL((void*)0)) {
4045 | /*
4046 | * The file-local context hasn't been initialized yet
4047 | * for the current file.
4048 | */
4049 | g_http3_file_local_ctx = wmem_new(wmem_file_scope(), http3_file_local_ctx)((http3_file_local_ctx*)wmem_alloc((wmem_file_scope()), sizeof (http3_file_local_ctx)));
4050 | g_http3_file_local_ctx->conn_info_map =
4051 | wmem_map_new(wmem_file_scope(), http3_conn_info_hash, http3_conn_info_equal);
4052 | #ifdef HAVE_NGHTTP31
4053 | g_http3_file_local_ctx->hdr_cache_map =
4054 | wmem_map_new(wmem_file_scope(), http3_hdrcache_hash, http3_hdrcache_equal);
4055 | #endif
4056 | wmem_register_callback(wmem_file_scope(), http3_file_local_ctx_del_cb, NULL((void*)0));
4057 | }
4058 |
4059 | return g_http3_file_local_ctx;
4060 | }
4061 |
4062 | /*
4063 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
4064 | *
4065 | * Local variables:
4066 | * c-basic-offset: 4
4067 | * tab-width: 8
4068 | * indent-tabs-mode: nil
4069 | * End:
4070 | *
4071 | * vi: set shiftwidth=4 tabstop=8 expandtab:
4072 | * :indentSize=4:tabSize=8:noTabs=true:
4073 | */