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

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
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
59void proto_reg_handoff_http3(void);
60void proto_register_http3(void);
61
62static dissector_handle_t http3_handle;
63static dissector_handle_t http3_datagram_handle;
64
65static int http3_follow_tap;
66
67#ifdef HAVE_NGHTTP31
68static reassembly_table http3_body_reassembly_table;
69
70static dissector_table_t media_type_dissector_table;
71
72static dissector_handle_t media_handle;
73
74static 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)
80static bool_Bool http3_decompress_body = true1;
81#else
82static 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
90static int proto_http3;
91static int hf_http3_stream_uni;
92static int hf_http3_stream_uni_type;
93static int hf_http3_stream_bidi;
94static int hf_http3_stream_id;
95static int hf_http3_push_id;
96static int hf_http3_frame;
97static int hf_http3_frame_type;
98static int hf_http3_frame_length;
99static int hf_http3_frame_payload;
100
101static int hf_http3_time;
102static int hf_http3_request_in;
103static int hf_http3_response_in;
104
105static int hf_http3_data;
106static int hf_http3_encoded_entity;
107static int hf_http3_body_fragments;
108static int hf_http3_body_fragment;
109static int hf_http3_body_fragment_overlap;
110static int hf_http3_body_fragment_overlap_conflicts;
111static int hf_http3_body_fragment_multiple_tails;
112static int hf_http3_body_fragment_too_long_fragment;
113static int hf_http3_body_fragment_error;
114static int hf_http3_body_fragment_count;
115static int hf_http3_body_reassembled_in;
116static int hf_http3_body_reassembled_length;
117static int hf_http3_body_reassembled_data;
118
119static int hf_http3_headers_count;
120static int hf_http3_header;
121static int hf_http3_headers_decoded_length;
122static int hf_http3_header_name_length;
123static int hf_http3_header_name;
124static int hf_http3_header_value_length;
125static int hf_http3_header_value;
126static int hf_http3_header_request_full_uri;
127
128static int hf_http3_header_qpack_blocked;
129static int hf_http3_header_qpack_blocked_stream_rcint;
130static int hf_http3_header_qpack_blocked_decoder_wicnt;
131
132#ifdef HAVE_NGHTTP31
133/* Static HTTP3 headers */
134static int hf_http3_headers_status;
135static int hf_http3_headers_path;
136static int hf_http3_headers_protocol;
137static int hf_http3_headers_method;
138static int hf_http3_headers_scheme;
139static int hf_http3_headers_accept;
140static int hf_http3_headers_accept_charset;
141static int hf_http3_headers_accept_encoding;
142static int hf_http3_headers_accept_language;
143static int hf_http3_headers_accept_ranges;
144static int hf_http3_headers_access_control_allow_origin;
145static int hf_http3_headers_age;
146static int hf_http3_headers_allow;
147static int hf_http3_headers_authorization;
148static int hf_http3_headers_authority;
149static int hf_http3_headers_cache_control;
150static int hf_http3_headers_content_disposition;
151static int hf_http3_headers_content_encoding;
152static int hf_http3_headers_content_language;
153static int hf_http3_headers_content_length;
154static int hf_http3_headers_content_location;
155static int hf_http3_headers_content_range;
156static int hf_http3_headers_content_type;
157static int hf_http3_headers_cookie;
158static int hf_http3_headers_date;
159static int hf_http3_headers_etag;
160static int hf_http3_headers_expect;
161static int hf_http3_headers_expires;
162static int hf_http3_headers_from;
163static int hf_http3_headers_if_match;
164static int hf_http3_headers_if_modified_since;
165static int hf_http3_headers_if_none_match;
166static int hf_http3_headers_if_range;
167static int hf_http3_headers_if_unmodified_since;
168static int hf_http3_headers_last_modified;
169static int hf_http3_headers_link;
170static int hf_http3_headers_location;
171static int hf_http3_headers_max_forwards;
172static int hf_http3_headers_proxy_authenticate;
173static int hf_http3_headers_proxy_authorization;
174static int hf_http3_headers_range;
175static int hf_http3_headers_referer;
176static int hf_http3_headers_refresh;
177static int hf_http3_headers_retry_after;
178static int hf_http3_headers_server;
179static int hf_http3_headers_set_cookie;
180static int hf_http3_headers_strict_transport_security;
181static int hf_http3_headers_user_agent;
182static int hf_http3_headers_vary;
183static int hf_http3_headers_via;
184static int hf_http3_headers_www_authenticate;
185#endif
186
187static int hf_http3_qpack_encoder;
188static int hf_http3_qpack_encoder_icnt;
189static int hf_http3_qpack_encoder_icnt_inc;
190static int hf_http3_qpack_encoder_opcode_insert_indexed;
191static int hf_http3_qpack_encoder_opcode_insert_indexed_ref;
192static int hf_http3_qpack_encoder_opcode_insert_indexed_val;
193static int hf_http3_qpack_encoder_opcode_insert_indexed_hval;
194static int hf_http3_qpack_encoder_opcode_insert;
195static int hf_http3_qpack_encoder_opcode_insert_name;
196static int hf_http3_qpack_encoder_opcode_insert_hname;
197static int hf_http3_qpack_encoder_opcode_insert_val;
198static int hf_http3_qpack_encoder_opcode_insert_hval;
199static int hf_http3_qpack_encoder_opcode_duplicate;
200static int hf_http3_qpack_encoder_opcode_dtable_cap;
201static int hf_http3_qpack_encoder_opcode_dtable_cap_val;
202
203static int hf_http3_qpack_decoder;
204static int hf_http3_qpack_decoder_opcode_section_ack;
205static int hf_http3_qpack_decoder_opcode_section_ack_stream_id;
206static int hf_http3_qpack_decoder_opcode_cancel_stream;
207static int hf_http3_qpack_decoder_opcode_cancel_stream_id;
208static int hf_http3_qpack_decoder_opcode_icnt_increment;
209static int hf_http3_qpack_decoder_opcode_icnt_increment_value;
210
211static int hf_http3_settings;
212static int hf_http3_settings_identifier;
213static int hf_http3_settings_value;
214static int hf_http3_settings_qpack_max_table_capacity;
215static int hf_http3_settings_max_field_section_size;
216static int hf_http3_settings_qpack_blocked_streams;
217static int hf_http3_settings_extended_connect;
218static int hf_http3_settings_webtransport;
219static int hf_http3_settings_h3_datagram;
220static int hf_http3_settings_h3_datagram_draft04;
221static int hf_http3_priority_update_element_id;
222static int hf_http3_priority_update_field_value;
223
224static int hf_http3_datagram;
225static int hf_http3_datagram_quarter_stream_id;
226static int hf_http3_datagram_request_stream_id;
227static int hf_http3_datagram_payload;
228
229static expert_field ei_http3_qpack_failed;
230static expert_field ei_http3_prefix_int_failed;
231static expert_field ei_http3_huffman_failed;
232/* HTTP3 dissection EIs */
233static expert_field ei_http3_unknown_stream_type;
234/* Encoded data EIs */
235static expert_field ei_http3_header_encoded_state;
236/* HTTP3 header decoding EIs */
237static expert_field ei_http3_header_decoding_failed;
238static expert_field ei_http3_header_decoding_blocked;
239static expert_field ei_http3_header_decoding_no_output;
240static expert_field ei_http3_header_size;
241static expert_field ei_http3_header_transfer_encoding;
242/* HTTP3 body decoding EIs */
243static expert_field ei_http3_body_decompression_failed;
244/* HTTP3 datagram prefix EIs */
245static expert_field ei_http3_datagram_invalid_stream_id;
246
247/* Initialize the subtree pointers */
248static int ett_http3;
249static int ett_http3_stream_uni;
250static int ett_http3_stream_bidi;
251static int ett_http3_frame;
252static int ett_http3_body_fragment;
253static int ett_http3_body_fragments;
254static int ett_http3_encoded_entity;
255static int ett_http3_settings;
256static int ett_http3_headers;
257static int ett_http3_headers_qpack_blocked;
258static int ett_http3_qpack_update;
259static int ett_http3_qpack_opcode;
260static int ett_http3_datagram;
261static int ett_http3_datagram_stream_id;
262
263#ifdef HAVE_NGHTTP31
264static 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 */
310enum 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
323static 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
350static 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
383static 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
414typedef 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 */
442typedef 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}
461typedef 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
487typedef void *qpack_decoder_t;
488typedef void *qpack_decoder_ctx_t;
489typedef 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 */
498static 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 */
551typedef 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 */
567typedef 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 */
600typedef 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 */
633typedef 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 */
648typedef 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 */
663static 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 */
675static inline bool_Bool
676http3_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 */
684static bool_Bool
685try_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 */
705static int
706get_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 */
733static bool_Bool
734http3_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
747static inline http3_stream_dir
748http3_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
755uint64_t*
756http3_get_stream_id(packet_info *pinfo)
757{
758 return p_get_proto_data(pinfo->pool, pinfo, hf_http3_stream_id, 0);
759}
760
761static const char*
762http3_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 */
795static bool_Bool
796qpack_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 */
805static void *
806http3_nghttp3_malloc(size_t size, void *user_data _U___attribute__((unused)))
807{
808 return wmem_alloc0(wmem_file_scope(), size);
809}
810
811static void
812http3_nghttp3_free(void *ptr, void *user_data _U___attribute__((unused)))
813{
814 wmem_free(wmem_file_scope(), ptr);
815}
816
817static void *
818http3_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
823static void *
824http3_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
829static 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
836static nghttp3_mem *
837qpack_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 */
848static void
849http3_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
860static GHashTable *header_fields_hash;
861
862static const char *
863cid_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
873static http3_header_data_t *
874http3_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
928static http3_qpack_encoder_state_t *
929http3_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
983static proto_item *
984try_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
1020static void
1021get_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
1071static const char*
1072http3_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
1116static void
1117populate_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
1177static int
1178decode_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 */
1527const char *
1528http3_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
1534static http3_session_info_t *
1535http3_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
1548static http3_session_info_t *
1549http3_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
1579static conversation_t *
1580http3_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
1625static void
1626http3_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
1634enum body_decompression {
1635 BODY_DECOMPRESSION_NONE,
1636 BODY_DECOMPRESSION_ZLIB,
1637 BODY_DECOMPRESSION_BROTLI,
1638 BODY_DECOMPRESSION_ZSTD,
1639 BODY_DECOMPRESSION_FAIL
1640};
1641
1642static enum body_decompression
1643get_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
1672static void
1673dissect_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
1728static void
1729dissect_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
1779static void
1780dissect_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
1806static bool_Bool
1807should_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
1820static tvbuff_t*
1821reassemble_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
1892static int
1893dissect_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
1925static int
1926dissect_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
1937static unsigned
1938#ifdef HAVE_NGHTTP31
1939dissect_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
1942dissect_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 */
1973static int
1974dissect_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 */
2048static int
2049dissect_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
2095static int
2096dissect_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
2194static void
2195report_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
2231static unsigned
2232read_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
2286static int
2287dissect_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
2585static int
2586dissect_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
2687static int
2688dissect_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
2854static int
2855dissect_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
2900static int
2901dissect_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
2947static int
2948dissect_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
3023static proto_tree *
3024start_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
3037static int
3038dissect_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
3130static int
3131dissect_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
3169static void
3170register_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
3446void
3447proto_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
3945void
3946proto_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 */
3965static http3_file_local_ctx *g_http3_file_local_ctx;
3966
3967static unsigned
3968http3_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
3983static gboolean
3984http3_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
4003static size_t
4004http3_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
4015static unsigned
4016http3_hdrcache_hash(const void *key)
4017{
4018 return wmem_strong_hash((const uint8_t *)key, http3_hdrcache_length(key));
4019}
4020
4021static gboolean
4022http3_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 */
4034static bool_Bool
4035http3_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
4041static http3_file_local_ctx *
4042http3_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 */