2 * Routines for HTTP/3 dissection
3 * Copyright 2019, Peter Wu <peter@lekensteyn.nl>
4 * Copyright 2023, Omer Shapira <oesh@github.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 * https://tools.ietf.org/html/draft-ietf-quic-http-29
15 * https://tools.ietf.org/html/draft-ietf-quic-qpack-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.
25 #define WS_LOG_DOMAIN "HTTP3"
27 #include <epan/packet.h>
28 #include <epan/expert.h>
29 #include <epan/exceptions.h>
30 #include <epan/proto_data.h>
31 #include <epan/to_str.h>
34 #include <wsutil/pint.h>
36 #include <epan/conversation_table.h>
37 #include <epan/dissectors/packet-http.h> /* for getting status reason-phrase */
39 #include "packet-quic.h"
40 #include "packet-tls-utils.h"
41 #include "wsutil/wmem/wmem_user_cb.h"
43 #include <epan/decode_as.h>
44 #include <epan/reassemble.h>
48 #include <nghttp3/nghttp3.h>
51 void proto_reg_handoff_http3(void);
52 void proto_register_http3(void);
54 static dissector_handle_t http3_handle
;
56 #define PROTO_DATA_KEY_HEADER 0
57 #define PROTO_DATA_KEY_QPACK 1
59 static int proto_http3
;
60 static int hf_http3_stream_uni
;
61 static int hf_http3_stream_uni_type
;
62 static int hf_http3_stream_bidi
;
63 static int hf_http3_push_id
;
64 static int hf_http3_frame
;
65 static int hf_http3_frame_type
;
66 static int hf_http3_frame_length
;
67 static int hf_http3_frame_payload
;
69 static int hf_http3_data
;
71 //static int hf_http3_headers;
72 static int hf_http3_headers_count
;
73 static int hf_http3_header
;
74 static int hf_http3_header_length
;
75 static int hf_http3_header_name_length
;
76 static int hf_http3_header_name
;
77 static int hf_http3_header_value_length
;
78 static int hf_http3_header_value
;
79 static int hf_http3_header_request_full_uri
;
81 static int hf_http3_header_qpack_blocked
;
82 static int hf_http3_header_qpack_blocked_stream_rcint
;
83 static int hf_http3_header_qpack_blocked_decoder_wicnt
;
84 //static int hf_http3_header_qpack_fatal;
87 /* Static HTTP3 headers */
88 static int hf_http3_headers_status
;
89 static int hf_http3_headers_path
;
90 static int hf_http3_headers_method
;
91 static int hf_http3_headers_scheme
;
92 static int hf_http3_headers_accept
;
93 static int hf_http3_headers_accept_charset
;
94 static int hf_http3_headers_accept_encoding
;
95 static int hf_http3_headers_accept_language
;
96 static int hf_http3_headers_accept_ranges
;
97 static int hf_http3_headers_access_control_allow_origin
;
98 static int hf_http3_headers_age
;
99 static int hf_http3_headers_allow
;
100 static int hf_http3_headers_authorization
;
101 static int hf_http3_headers_authority
;
102 static int hf_http3_headers_cache_control
;
103 static int hf_http3_headers_content_disposition
;
104 static int hf_http3_headers_content_encoding
;
105 static int hf_http3_headers_content_language
;
106 static int hf_http3_headers_content_length
;
107 static int hf_http3_headers_content_location
;
108 static int hf_http3_headers_content_range
;
109 static int hf_http3_headers_content_type
;
110 static int hf_http3_headers_cookie
;
111 static int hf_http3_headers_date
;
112 static int hf_http3_headers_etag
;
113 static int hf_http3_headers_expect
;
114 static int hf_http3_headers_expires
;
115 static int hf_http3_headers_from
;
116 static int hf_http3_headers_if_match
;
117 static int hf_http3_headers_if_modified_since
;
118 static int hf_http3_headers_if_none_match
;
119 static int hf_http3_headers_if_range
;
120 static int hf_http3_headers_if_unmodified_since
;
121 static int hf_http3_headers_last_modified
;
122 static int hf_http3_headers_link
;
123 static int hf_http3_headers_location
;
124 static int hf_http3_headers_max_forwards
;
125 static int hf_http3_headers_proxy_authenticate
;
126 static int hf_http3_headers_proxy_authorization
;
127 static int hf_http3_headers_range
;
128 static int hf_http3_headers_referer
;
129 static int hf_http3_headers_refresh
;
130 static int hf_http3_headers_retry_after
;
131 static int hf_http3_headers_server
;
132 static int hf_http3_headers_set_cookie
;
133 static int hf_http3_headers_strict_transport_security
;
134 static int hf_http3_headers_user_agent
;
135 static int hf_http3_headers_vary
;
136 static int hf_http3_headers_via
;
137 static int hf_http3_headers_www_authenticate
;
140 //static int hf_http3_qpack;
141 static int hf_http3_qpack_encoder
;
142 //static int hf_http3_qpack_encoder_length;
143 static int hf_http3_qpack_encoder_icnt
;
144 static int hf_http3_qpack_encoder_icnt_inc
;
145 //static int hf_http3_qpack_encoder_opcode;
146 static int hf_http3_qpack_encoder_opcode_insert_indexed
;
147 static int hf_http3_qpack_encoder_opcode_insert_indexed_ref
;
148 static int hf_http3_qpack_encoder_opcode_insert_indexed_val
;
149 static int hf_http3_qpack_encoder_opcode_insert_indexed_hval
;
150 static int hf_http3_qpack_encoder_opcode_insert
;
151 static int hf_http3_qpack_encoder_opcode_insert_name
;
152 static int hf_http3_qpack_encoder_opcode_insert_hname
;
153 static int hf_http3_qpack_encoder_opcode_insert_val
;
154 static int hf_http3_qpack_encoder_opcode_insert_hval
;
155 static int hf_http3_qpack_encoder_opcode_duplicate
;
156 //static int hf_http3_qpack_encoder_opcode_duplicate_val;
157 static int hf_http3_qpack_encoder_opcode_dtable_cap
;
158 static int hf_http3_qpack_encoder_opcode_dtable_cap_val
;
160 static int hf_http3_settings
;
161 static int hf_http3_settings_identifier
;
162 static int hf_http3_settings_value
;
163 static int hf_http3_settings_qpack_max_table_capacity
;
164 static int hf_http3_settings_max_field_section_size
;
165 static int hf_http3_settings_qpack_blocked_streams
;
166 static int hf_http3_settings_extended_connect
;
167 static int hf_http3_settings_webtransport
;
168 static int hf_http3_settings_h3_datagram
;
169 static int hf_http3_settings_h3_datagram_draft04
;
170 static int hf_http3_priority_update_element_id
;
171 static int hf_http3_priority_update_field_value
;
173 /* QPACK dissection EIs */
174 //static expert_field ei_http3_qpack_enc_update;
175 static expert_field ei_http3_qpack_failed
;
176 /* HTTP3 dissection EIs */
177 static expert_field ei_http3_unknown_stream_type
;
178 //static expert_field ei_http3_data_not_decoded;
179 /* Encoded data EIs */
180 static expert_field ei_http3_header_encoded_state
;
181 /* HTTP3 header decoding EIs */
182 static expert_field ei_http3_header_decoding_failed
;
183 static expert_field ei_http3_header_decoding_blocked
;
184 static expert_field ei_http3_header_decoding_no_output
;
186 /* Initialize the subtree pointers */
187 static int ett_http3
;
188 static int ett_http3_stream_uni
;
189 static int ett_http3_stream_bidi
;
190 static int ett_http3_frame
;
191 static int ett_http3_settings
;
192 static int ett_http3_headers
;
193 static int ett_http3_headers_qpack_blocked
;
194 static int ett_http3_qpack_update
;
195 static int ett_http3_qpack_opcode
;
198 * HTTP3 header constants.
199 * The below constants are used for dissecting the
200 * code. This is not an exahustive list.
202 #define HTTP3_HEADER_NAME_CONTENT_ENCODING "content-encoding"
203 #define HTTP3_HEADER_NAME_CONTENT_TYPE "content-type"
204 #define HTTP3_HEADER_NAME_TRANSFER_ENCODING "transfer-encoding"
205 #define HTTP3_HEADER_NAME_AUTHORITY ":authority"
206 #define HTTP3_HEADER_NAME_METHOD ":method"
207 #define HTTP3_HEADER_NAME_PATH ":path"
208 #define HTTP3_HEADER_NAME_SCHEME ":scheme"
209 #define HTTP3_HEADER_NAME_STATUS ":status"
211 #define HTTP3_HEADER_METHOD_CONNECT "CONNECT"
212 #define HTTP3_HEADER_STATUS_PARTIAL_CONTENT "206"
214 #define HTTP3_HEADER_UNKNOWN "<unknown>"
217 * Unidirectional stream types.
218 * https://tools.ietf.org/html/draft-ietf-quic-http-29#section-6.2
219 * https://tools.ietf.org/html/draft-ietf-quic-qpack-16#section-4.2
221 enum http3_stream_type
{
222 HTTP3_STREAM_TYPE_CONTROL
,
223 HTTP3_STREAM_TYPE_PUSH
,
224 HTTP3_STREAM_TYPE_QPACK_ENCODER
,
225 HTTP3_STREAM_TYPE_QPACK_DECODER
,
226 HTTP3_STREAM_TYPE_WEBTRANSPORT
= 0x54, // draft-ietf-webtrans-http3-03
230 * Unidirectional stream types (62-bit code space).
231 * https://tools.ietf.org/html/draft-ietf-quic-http-29#section-11.2.4
234 static const val64_string http3_stream_types
[] = {
235 /* 0x00 - 0x3f Assigned via Standards Action or IESG Approval. */
236 { 0x00, "Control Stream" },
237 { 0x01, "Push Stream" },
238 { 0x02, "QPACK Encoder Stream" },
239 { 0x03, "QPACK Decoder Stream" },
240 { 0x54, "WebTransport Stream" },
241 /* 0x40 - 0x3FFFFFFFFFFFFFFF Assigned via Specification Required policy */
247 * Frame type codes (62-bit code space).
248 * https://tools.ietf.org/html/draft-ietf-quic-http-29#section-11.2.1
250 #define HTTP3_DATA 0x0
251 #define HTTP3_HEADERS 0x1
252 #define HTTP3_CANCEL_PUSH 0x3
253 #define HTTP3_SETTINGS 0x4
254 #define HTTP3_PUSH_PROMISE 0x5
255 #define HTTP3_GOAWAY 0x7
256 #define HTTP3_MAX_PUSH_ID 0xD
257 #define HTTP3_WEBTRANSPORT_BISTREAM 0x41
258 #define HTTP3_PRIORITY_UPDATE_REQUEST_STREAM 0xF0700
259 #define HTTP3_PRIORITY_UPDATE_PUSH_STREAM 0xF0701
261 static const val64_string http3_frame_types
[] = {
262 /* 0x00 - 0x3f Assigned via Standards Action or IESG Approval. */
263 { HTTP3_DATA
, "DATA" },
264 { HTTP3_HEADERS
, "HEADERS" },
265 { 0x02, "Reserved" }, // "PRIORITY" in draft-22 and before
266 { HTTP3_CANCEL_PUSH
, "CANCEL_PUSH" },
267 { HTTP3_SETTINGS
, "SETTINGS" },
268 { HTTP3_PUSH_PROMISE
, "PUSH_PROMISE" },
269 { 0x06, "Reserved" },
270 { HTTP3_GOAWAY
, "GOAWAY" },
271 { 0x08, "Reserved" },
272 { 0x09, "Reserved" },
273 { HTTP3_MAX_PUSH_ID
, "MAX_PUSH_ID" },
274 { 0x0e, "Reserved" }, // "DUPLICATE_PUSH" in draft-26 and before
275 { HTTP3_WEBTRANSPORT_BISTREAM
, "WEBTRANSPORT_BISTREAM" }, // draft-ietf-webtrans-http3-03
276 { HTTP3_PRIORITY_UPDATE_REQUEST_STREAM
, "PRIORITY_UPDATE" }, // RFC 9218
277 { HTTP3_PRIORITY_UPDATE_PUSH_STREAM
, "PRIORITY_UPDATE" }, // RFC 9218
278 /* 0x40 - 0x3FFFFFFFFFFFFFFF Assigned via Specification Required policy */
283 * Settings parameter type codes (62-bit code space).
284 * https://tools.ietf.org/html/draft-ietf-quic-http-29#name-http-2-settings-parameters
286 #define HTTP3_QPACK_MAX_TABLE_CAPACITY 0x01
287 #define HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE 0x06
288 #define HTTP3_QPACK_BLOCKED_STREAMS 0x07
289 #define HTTP3_EXTENDED_CONNECT 0x08 /* https://datatracker.ietf.org/doc/draft-ietf-httpbis-h3-websockets */
290 #define HTTP3_H3_DATAGRAM 0x33 // rfc9297
291 #define HTTP3_H3_DATAGRAM_DRAFT04 0xffd277 // draft-ietf-masque-h3-datagram-04
292 #define HTTP3_WEBTRANSPORT 0x2b603742 // draft-ietf-webtrans-http3-03
294 static const val64_string http3_settings_vals
[] = {
295 { HTTP3_QPACK_MAX_TABLE_CAPACITY
, "Max Table Capacity" },
296 { HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE
, "Max Field Section Size" },
297 { HTTP3_QPACK_BLOCKED_STREAMS
, "Blocked Streams" },
298 { HTTP3_EXTENDED_CONNECT
, "Extended CONNECT" },
299 { HTTP3_WEBTRANSPORT
, "Enable WebTransport" },
300 { HTTP3_H3_DATAGRAM
, "Enable Datagram" },
301 { HTTP3_H3_DATAGRAM_DRAFT04
, "Enable Datagram Draft04" },
306 * QPACK encoder stream opcodes.
308 #define QPACK_OPCODE_MASK 0xE0
309 #define QPACK_OPCODE_INSERT_INDEXED 0x80
310 #define QPACK_OPCODE_INSERT 0x40
311 #define QPACK_OPCODE_SET_DTABLE_CAP 0x20
312 #define QPACK_OPCODE_DUPLICATE 0x00
314 #define QPACK_HUFFMAN_5_STRING 0x20
315 #define QPACK_HUFFMAN_6_STRING 0x40
316 #define QPACK_HUFFMAN_7_STRING q0x80
318 typedef enum _http3_stream_dir
{
319 FROM_CLIENT_TO_SERVER
= 0,
320 FROM_SERVER_TO_CLIENT
= 1,
324 * Essential data structures.
328 * HTTP3 stream info - contains information about HTTP3 stream.
329 * HTTP3 streams roughly correspond to QUIC streams, with the
330 * HTTP3 Server Push being an exception to the rule.
332 typedef struct _http3_stream_info
{
333 uint64_t id
; /**< HTTP3 stream id */
334 uint64_t uni_stream_type
; /**< Unidirectional stream type */
335 uint64_t broken_from_offset
; /**< Unrecognized stream starting at offset (if non-zero). */
336 http3_stream_dir direction
;
337 } http3_stream_info_t
;
340 * HTTP3 session info - contains information about the HTTP3 session.
341 * HTTP3 sessions roughly correspond to QUIC connections, at least
342 * until the dissector will support connection migration and/or
343 * Multipath QUIC. When that happens, a single HTTP3 session would
344 * be mapped to multiple QUIC connections, or to multiple QUIC
345 * paths (in the MP-QUIC terminology).
348 typedef void *qpack_decoder_t
;
349 typedef void *qpack_decoder_ctx_t
;
350 typedef struct _http3_session_info
{
352 qpack_decoder_t qpack_decoder
[2]; /**< Decoders for outgoing/incoming QPACK streams. */
353 } http3_session_info_t
;
356 * Lookup or create new HTTP3 session object for the pinfo.
358 static http3_session_info_t
*http3_session_lookup_or_create(packet_info
*pinfo
);
361 * HTTP3 Header dissection support.
363 #define QPACK_MAX_DTABLE_SIZE 65536 /**< Max size of the QPACK dynamic table. */
364 #define QPACK_MAX_BLOCKED 512 /**< Upper limit on number of streams blocked on QPACK updates. */
367 * Decompressed header field definition.
368 * Header field definitions are cached separately,
369 * to preserve memory.
371 typedef struct _http3_header_field_def
{
374 } http3_header_field_def_t
;
377 * HTTP3 header field.
379 * The header field contains two sections:
380 * - encoded points to the location of the encoded field in the *original* packet TVB.
381 * - decoded points to the formatted header string, which is allocated in a cache map,
382 * to conserve memory.
383 * The decoded fields are used to create an auxiliary TVB which will
384 * be used for dissection of decoded header values.
386 typedef struct _http3_header_field
{
395 http3_header_field_def_t
*def
;
396 } http3_header_field_t
;
399 * HTTP3 encoded header data block.
401 * This helper structure is used to support header dissection.
403 typedef struct _header_block_encoded_iter
{
407 } header_block_encoded_iter_t
;
409 #define HEADER_BLOCK_ENC_ITER_PTR(hdata) \
410 ((hdata)->encoded.bytes == NULL \
412 : ((hdata)->encoded.pos == (hdata)->encoded.len) ? NULL : (hdata)->encoded.bytes + (hdata)->encoded.pos)
414 #define HEADER_BLOCK_ENC_ITER_REMAINING(hdata) \
415 ((hdata)->encoded.bytes == NULL ? 0 : ((hdata)->encoded.len - (hdata)->encoded.pos))
417 #define HEADER_BLOCK_ENC_ITER_INC(hdata, nread) \
420 (hdata)->encoded.pos += (nread); \
421 DISSECTOR_ASSERT((hdata)->encoded.pos <= (hdata)->encoded.len); \
426 * HTTP3 header data block.
428 * The data block corresponds to contents of a single HTTP3 HEADERS frame.
429 * If a packet contains multiple HTTP3 HEADERS frames,
430 * the corresponding blocks will be chained using the `next'
431 * pointer. In this case, individual headers blocks
432 * will be identified by the `offset' field.
434 typedef struct _http3_header_data
{
435 unsigned len
; /**< Length of the encoded headers block. */
436 unsigned offset
; /**< Offset of the headers block in the pinfo TVB. */
437 unsigned ds_idx
; /**< Index of the data source tvb in the pinfo. */
438 wmem_array_t
* header_fields
; /**< List of header fields contained in the header block. */
439 header_block_encoded_iter_t encoded
; /**< Used for dissection, not allocated. */
440 struct _http3_header_data
* next
; /**< Next pointer in the chain. */
441 } http3_header_data_t
;
443 /* HTTP3 QPACK encoder state
445 * Store information about how many entries a QPACK encoder stream
446 * has inserted into the decoder at a particular point in the capture
447 * file (both the number newly inserted in the portion of the stream
448 * contained in the current QUIC packet and the total up to that point.)
449 * If a capture frame contains multiple encoder stream segments, the
450 * corresponding blocks will be chained using the 'next' pointer. In this
451 * case, individual blocks will be identified by the data source index
452 * of the tvb within the capture frame and the offset in the ds_tvb.
453 * (Both are necessary for multiple QUIC packets coalesced in a single
454 * UDP datagram with multiple stream segments within a QUIC packet.)
456 typedef struct _http3_qpack_encoder_state
{
457 unsigned offset
; /**< Offset of the headers block in the pinfo TVB. */
458 unsigned ds_idx
; /**< Index of the data source tvb in the pinfo. */
459 uint32_t icnt_inc
; /**< Number of insertions in this header segment. */
460 uint64_t icnt
; /**< Total number of insertions up to this point. */
461 ptrdiff_t nread
; /**< Number of bytes read; if negative, an error code. */
462 struct _http3_qpack_encoder_state
* next
; /**< Next pointer in the chain. */
463 } http3_qpack_encoder_state_t
;
466 * File-scoped context.
467 * This data structure is used to maintain file-scoped
468 * lookup tables. It is reset when the file-scoped
469 * allocator is exited.
471 typedef struct _http3_file_local_ctx
{
472 wmem_map_t
*conn_info_map
;
474 wmem_map_t
*hdr_cache_map
;
475 wmem_map_t
*hdr_def_cache_map
;
477 } http3_file_local_ctx
;
480 * @function http3_get_file_local_ctx
481 * @abstract Will create a new instance for the first time
482 * the file is visited.
483 * This function is not intended to be invked directly,
484 * but should be used via the `HTTP3_CONN_INFO_MAP` et. al. below.
485 * @returns file-local context.
487 static http3_file_local_ctx
*http3_get_file_local_ctx(void);
489 #define HTTP3_CONN_INFO_MAP http3_get_file_local_ctx()->conn_info_map
492 #define HTTP3_HEADER_CACHE http3_get_file_local_ctx()->hdr_cache_map
493 #define HTTP3_HEADER_NAME_CACHE http3_get_file_local_ctx()->hdr_def_cache_map
495 /* This global carries header name_length + name + value_length + value.
496 * It is allocated with file scoped memory, and then either placed in the
497 * cache map or, if it matches something already in the cache map, the
498 * memory is reallocated for the next header encountered. */
499 static char *http3_header_pstr
;
503 * Check whether the argument represents a reserved code point,
504 * for Stream Type, Frame Type, Error Code, etc.
507 http3_is_reserved_code(uint64_t stream_type
)
509 return (stream_type
- 0x21) % 0x1f == 0;
513 * Attempt to parse QUIC-encoded variable integer.
516 try_get_quic_varint(tvbuff_t
*tvb
, int offset
, uint64_t *value
, int *lenvar
)
518 if (tvb_reported_length_remaining(tvb
, offset
) == 0) {
521 int len
= 1 << (tvb_get_uint8(tvb
, offset
) >> 6);
522 if (tvb_reported_length_remaining(tvb
, offset
) < len
) {
527 int n
= (int)tvb_get_varint(tvb
, offset
, -1, value
, ENC_VARINT_QUIC
);
528 DISSECTOR_ASSERT_CMPINT(n
, ==, len
);
534 * Return the size of entire HTTP/3 frame.
537 get_http3_frame_size(tvbuff_t
*tvb
, int offset
)
539 int type_size
, length_size
;
540 uint64_t frame_length
;
542 if (!try_get_quic_varint(tvb
, offset
, NULL
, &type_size
)) {
547 if (!try_get_quic_varint(tvb
, offset
, &frame_length
, &length_size
)) {
551 uint64_t frame_size
= type_size
+ length_size
+ frame_length
;
552 if (frame_size
> INT32_MAX
) {
553 // We do not support such large frames.
556 return (int)frame_size
;
560 * Check whether the pinfo contains at least one whole HTTP3 frame,
561 * and adjust the pinfo desegmentation settings for the lower
562 * layer (QUIC, generally) to continue the desegmentation process.
565 http3_check_frame_size(tvbuff_t
*tvb
, packet_info
*pinfo
, int offset
)
567 int frame_size
= get_http3_frame_size(tvb
, offset
);
568 int remaining
= tvb_reported_length_remaining(tvb
, offset
);
569 if (frame_size
&& frame_size
<= remaining
) {
573 pinfo
->desegment_offset
= offset
;
574 pinfo
->desegment_len
= frame_size
? (frame_size
- remaining
) : DESEGMENT_ONE_MORE_SEGMENT
;
579 * Functions to support decompression of HTTP3 headers.
583 * File-scoped callback to release resources allocated for the QPACK
587 qpack_decoder_del_cb(wmem_allocator_t
*allocator _U_
, wmem_cb_event_t event _U_
, void *user_data
)
589 nghttp3_qpack_decoder_del((nghttp3_qpack_decoder
*)user_data
);
590 /* If we have a decoder, then we might have set http3_header_pstr to
591 * point to file scoped memory. Make sure we set it to NULL when leaving
594 http3_header_pstr
= NULL
;
599 * Memory allocation callbacks for nghttp3_qpack functionality.
602 http3_nghttp3_malloc(size_t size
, void *user_data _U_
)
604 return wmem_alloc0(wmem_file_scope(), size
);
608 http3_nghttp3_free(void *ptr
, void *user_data _U_
)
610 wmem_free(wmem_file_scope(), ptr
);
614 http3_nghttp3_calloc(size_t nmemb
, size_t size
, void *user_data _U_
)
616 return wmem_alloc0(wmem_file_scope(), nmemb
* size
);
620 http3_nghttp3_realloc(void *ptr
, size_t size
, void *user_data _U_
)
622 return wmem_realloc(wmem_file_scope(), ptr
, size
);
625 static nghttp3_mem g_qpack_mem_allocator
= {
626 .malloc
= http3_nghttp3_malloc
,
627 .free
= http3_nghttp3_free
,
628 .calloc
= http3_nghttp3_calloc
,
629 .realloc
= http3_nghttp3_realloc
,
633 qpack_mem_allocator(wmem_allocator_t
*allocator _U_
, int debug _U_
)
636 mem
= &g_qpack_mem_allocator
;
641 * Initialization routine for the http3_session object.
642 * Invoked during the creation of the new http3_session.
645 http3_initialize_qpack_decoders(http3_session_info_t
*http3_session
)
647 for (int dir
= 0; dir
< 2; dir
++) {
648 nghttp3_qpack_decoder
**pdecoder
= (nghttp3_qpack_decoder
**)&(http3_session
->qpack_decoder
[dir
]);
649 nghttp3_qpack_decoder_new(pdecoder
, QPACK_MAX_DTABLE_SIZE
, QPACK_MAX_BLOCKED
,
650 qpack_mem_allocator(wmem_file_scope(), 1));
651 nghttp3_qpack_decoder_set_max_dtable_capacity(*pdecoder
, QPACK_MAX_DTABLE_SIZE
);
652 wmem_register_callback(wmem_file_scope(), qpack_decoder_del_cb
, *pdecoder
);
656 static GHashTable
*header_fields_hash
;
659 cid_to_string(const quic_cid_t
*cid
, wmem_allocator_t
*scope
)
664 char *str
= (char *)wmem_alloc0(scope
, 2 * cid
->len
+ 1);
665 bytes_to_hexstr(str
, cid
->cid
, cid
->len
);
669 /* Given a packet_info and a tvbuff_t, returns the index of the
670 * data source tvb among the data sources in the packet.
673 get_tvb_ds_idx(packet_info
*pinfo
, tvbuff_t
*tvb
)
676 tvbuff_t
*ds_tvb
= tvb_get_ds_tvb(tvb
);
678 struct data_source
*src
;
680 for (src_le
= pinfo
->data_src
; src_le
!= NULL
; src_le
= src_le
->next
) {
681 src
= (struct data_source
*)src_le
->data
;
682 if (ds_tvb
== get_data_source_tvb(src
)) {
689 /* If this gets made to a more general function, return a
690 * failure condition (-1?) that must be checked instead of asserting.
692 DISSECTOR_ASSERT(found
== true);
696 static http3_header_data_t
*
697 http3_get_header_data(packet_info
*pinfo
, tvbuff_t
*tvb
, unsigned offset
)
699 http3_header_data_t
*data
, *prev
= NULL
;
701 unsigned raw_offset
= tvb_raw_offset(tvb
) + offset
;
702 /* The raw offset is relative to the original data source, which is
703 * the decrypted QUIC packet. There can be multiple decrypted QUIC
704 * packets in a single QUIC layer, so this guarantees the same raw
705 * offset from different decrypted data gives different keys.
707 uint32_t ds_idx
= get_tvb_ds_idx(pinfo
, tvb
);
709 data
= (http3_header_data_t
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_http3
, PROTO_DATA_KEY_HEADER
);
712 * Attempt to find existing header data block.
713 * In most cases, data will be `NULL'
714 * and this loop won't be visited.
716 while (data
!= NULL
) {
717 if (data
->offset
== raw_offset
&& data
->ds_idx
== ds_idx
) {
719 * We found the matching data. Return it.
728 * We did not find header data matching the offset.
729 * Allocate a new header data block, and initialize
732 data
= wmem_new0(wmem_file_scope(), http3_header_data_t
);
733 data
->offset
= raw_offset
;
734 data
->ds_idx
= ds_idx
;
737 * Check whether the newly allocated data should be linked
738 * to the tail of existing header block chain, or whether
739 * it is the head of a new header block chain.
744 p_add_proto_data(wmem_file_scope(), pinfo
, proto_http3
, PROTO_DATA_KEY_HEADER
, data
);
750 static http3_qpack_encoder_state_t
*
751 http3_get_qpack_encoder_state(packet_info
*pinfo
, tvbuff_t
*tvb
, unsigned offset
)
753 http3_qpack_encoder_state_t
*data
, *prev
= NULL
;
755 unsigned raw_offset
= tvb_raw_offset(tvb
) + offset
;
756 /* The raw offset is relative to the original data source, which is
757 * the decrypted QUIC packet. There can be multiple decrypted QUIC
758 * packets in a single QUIC layer, so this guarantees the same raw
759 * offset from different decrypted data gives different keys.
761 uint32_t ds_idx
= get_tvb_ds_idx(pinfo
, tvb
);
763 data
= (http3_qpack_encoder_state_t
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_http3
, PROTO_DATA_KEY_QPACK
);
766 * Attempt to find existing header data block.
767 * In most cases, data will be `NULL'
768 * and this loop won't be visited.
770 while (data
!= NULL
) {
771 if (data
->offset
== raw_offset
&& data
->ds_idx
== ds_idx
) {
773 * We found the matching data. Return it.
782 * We did not find header data matching the offset.
783 * Allocate a new header data block, and initialize
786 data
= wmem_new0(wmem_file_scope(), http3_qpack_encoder_state_t
);
787 data
->offset
= raw_offset
;
788 data
->ds_idx
= ds_idx
;
791 * Check whether the newly allocated data should be linked
792 * to the tail of existing header block chain, or whether
793 * it is the head of a new header block chain.
798 p_add_proto_data(wmem_file_scope(), pinfo
, proto_http3
, PROTO_DATA_KEY_QPACK
, data
);
804 static inline http3_stream_dir
805 http3_packet_get_direction(quic_stream_info
*stream_info
)
807 return stream_info
->from_server
808 ? FROM_CLIENT_TO_SERVER
809 : FROM_SERVER_TO_CLIENT
;
813 try_append_method_path_info(packet_info
*pinfo
, proto_tree
*tree
, const char *method_header_value
,
814 const char *path_header_value
, const char *authority_header_value
)
816 if (method_header_value
!= NULL
) {
817 if ((strcmp(method_header_value
, "CONNECT_UDP") == 0) || (strcmp(method_header_value
, "CONNECT") == 0)) {
818 if (authority_header_value
!= NULL
) {
819 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, ": ", "%s %s", method_header_value
, authority_header_value
);
822 if (path_header_value
!= NULL
) {
823 /* append request information to info column (for example, HEADERS: GET /demo/1.jpg) */
824 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, ": ", "%s %s", method_header_value
, path_header_value
);
825 /* append request information to Stream node */
826 proto_item_append_text(tree
, ", %s %s", method_header_value
, path_header_value
);
833 try_add_named_header_field(proto_tree
*tree
, tvbuff_t
*tvb
, int offset
, uint32_t length
, const char *header_name
,
834 const char *header_value
)
837 header_field_info
*hfi
;
838 proto_item
*ti
= NULL
;
840 const int *entry
= (const int *)g_hash_table_lookup(header_fields_hash
, header_name
);
847 hfi
= proto_registrar_get_nth(hf_id
);
848 DISSECTOR_ASSERT(hfi
!= NULL
);
850 if (FT_IS_UINT32(hfi
->type
)) {
852 if (ws_strtou32(header_value
, NULL
, &value
)) {
853 ti
= proto_tree_add_uint(tree
, hf_id
, tvb
, offset
, length
, value
);
855 } else if (FT_IS_UINT(hfi
->type
)) {
857 if (ws_strtou64(header_value
, NULL
, &value
)) {
858 ti
= proto_tree_add_uint64(tree
, hf_id
, tvb
, offset
, length
, value
);
861 ti
= proto_tree_add_item(tree
, hf_id
, tvb
, offset
, length
, ENC_BIG_ENDIAN
);
867 dissect_http3_headers(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, unsigned tvb_offset
, unsigned offset
,
868 quic_stream_info
*stream_info
, http3_stream_info_t
*http3_stream
)
870 const char *authority_header_value
= NULL
;
871 const char *method_header_value
= NULL
;
872 const char *path_header_value
= NULL
;
873 const char *scheme_header_value
= NULL
;
874 const uint8_t *header_name
;
875 const uint8_t *header_value
;
878 uint32_t header_name_length
;
879 uint32_t header_value_length
;
880 http3_header_data_t
*header_data
;
881 http3_session_info_t
*http3_session
;
882 http3_stream_dir packet_direction
;
883 int header_len
= 0, hoffset
= 0;
884 nghttp3_qpack_decoder
*decoder
;
885 proto_item
*header
, *ti
, *ti_named_field
;
886 proto_tree
*header_tree
, *blocked_rcint_tree
;
887 tvbuff_t
*header_tvb
;
889 http3_session
= http3_session_lookup_or_create(pinfo
);
890 header_data
= http3_get_header_data(pinfo
, tvb
, offset
);
892 ws_noisy("pdinfo visited=%d", PINFO_FD_VISITED(pinfo
));
894 if (!PINFO_FD_VISITED(pinfo
)) {
896 * This packet has not been processed yet, which means this is
897 * the first linear scan. We do header decompression only
898 * once in linear scan and cache the result. If we don't
899 * cache, already processed data will be fed into decompressor
900 * again and again since dissector will be called randomly.
901 * This makes context out-of-sync.
904 length
= tvb_reported_length_remaining(tvb
, tvb_offset
);
905 packet_direction
= http3_packet_get_direction(stream_info
);
906 decoder
= http3_session
->qpack_decoder
[packet_direction
];
908 DISSECTOR_ASSERT(decoder
);
909 DISSECTOR_ASSERT(header_data
);
910 DISSECTOR_ASSERT(header_data
->encoded
.bytes
== NULL
);
911 DISSECTOR_ASSERT(header_data
->encoded
.len
== 0);
912 DISSECTOR_ASSERT(header_data
->header_fields
== NULL
);
914 header_data
->encoded
.bytes
= tvb_memdup(wmem_file_scope(), tvb
, tvb_offset
, length
);
915 header_data
->encoded
.pos
= 0;
916 header_data
->encoded
.len
= length
;
918 nghttp3_qpack_stream_context
*sctx
= NULL
;
919 nghttp3_qpack_stream_context_new(&sctx
, http3_stream
->id
, nghttp3_mem_default());
921 ws_debug("Header data: %p %d %d", header_data
->encoded
.bytes
, header_data
->encoded
.pos
,
922 header_data
->encoded
.len
);
924 proto_tree_add_expert_format(tree
, pinfo
, &ei_http3_header_encoded_state
, tvb
, tvb_offset
, 0,
925 "HTTP3 encoded headers - bytes %p pos %d len %d", header_data
->encoded
.bytes
,
926 header_data
->encoded
.pos
, header_data
->encoded
.len
);
928 * Attempt to decode headers.
930 * TODO: This may incorrectly put headers that were blocked
931 * for packet k in the past to this packet n. We will deal with this later
933 while (HEADER_BLOCK_ENC_ITER_REMAINING(header_data
)) {
937 ws_noisy("%p %p:%d decode decoder=%p sctx=%p", header_data
->encoded
.bytes
,
938 HEADER_BLOCK_ENC_ITER_PTR(header_data
),
939 HEADER_BLOCK_ENC_ITER_REMAINING(header_data
), decoder
, sctx
);
941 int32_t nread
= (int32_t)nghttp3_qpack_decoder_read_request(decoder
, sctx
, &nv
, &flags
,
942 HEADER_BLOCK_ENC_ITER_PTR(header_data
),
943 HEADER_BLOCK_ENC_ITER_REMAINING(header_data
), 1);
947 * This should be signaled up.
949 ws_debug("Early return nread=%d err=%s", nread
, nghttp3_strerror(nread
));
950 proto_tree_add_expert_format(tree
, pinfo
, &ei_http3_header_decoding_failed
, tvb
, tvb_offset
, 0,
951 "QPACK error decoder %p ctx %p flags %" PRIu8
" error %d (%s)", decoder
,
952 sctx
, flags
, nread
, nghttp3_strerror((int)nread
));
957 * Check whether the QPACK decoder is blocked on QPACK encoder stream.
959 if (flags
& NGHTTP3_QPACK_DECODE_FLAG_BLOCKED
) {
960 uint64_t ricnt
, wicnt
;
962 ricnt
= nghttp3_qpack_stream_context_get_ricnt(sctx
);
963 wicnt
= nghttp3_qpack_decoder_get_icnt(decoder
);
964 ti
= proto_tree_add_boolean(tree
, hf_http3_header_qpack_blocked
, tvb
, tvb_offset
, 0, true);
965 proto_item_set_generated(ti
);
966 blocked_rcint_tree
= proto_item_add_subtree(ti
, ett_http3_headers_qpack_blocked
);
967 ti
= proto_tree_add_uint(blocked_rcint_tree
, hf_http3_header_qpack_blocked_stream_rcint
, tvb
,
968 tvb_offset
, 0, (uint32_t)ricnt
);
969 proto_item_set_generated(ti
);
970 proto_tree_add_uint(blocked_rcint_tree
, hf_http3_header_qpack_blocked_decoder_wicnt
, tvb
, tvb_offset
, 0,
972 proto_tree_add_expert_format(tree
, pinfo
, &ei_http3_header_decoding_blocked
, tvb
, tvb_offset
, 0,
973 "QPACK - blocked decoder %p ctx %p flags=%" PRIu8
" ricnt=%" PRIu64
974 " wicnt=%" PRIu64
" error %d (%s)",
975 decoder
, sctx
, flags
, ricnt
, wicnt
, nread
, nghttp3_strerror((int)nread
));
976 ws_debug("Early return nread=%d blocked=%" PRIu8
" ricnt=%" PRIu64
" wicnt=%" PRIu64
,
977 nread
, flags
, ricnt
, wicnt
);
982 * Check whether the decoder has emitted header data.
984 if (flags
& NGHTTP3_QPACK_DECODE_FLAG_EMIT
) {
985 http3_header_field_t
*out
;
986 http3_header_field_def_t
*def
;
988 nghttp3_vec name_vec
;
989 nghttp3_vec value_vec
;
996 ws_noisy("Emit nread=%d flags=%" PRIu8
"", nread
, flags
);
998 if (header_data
->header_fields
== NULL
) {
999 header_data
->header_fields
= wmem_array_new(wmem_file_scope(), sizeof(http3_header_field_t
));
1002 name_vec
= nghttp3_rcbuf_get_buf(nv
.name
);
1003 name_len
= (uint32_t)name_vec
.len
;
1004 name
= name_vec
.base
;
1005 value_vec
= nghttp3_rcbuf_get_buf(nv
.value
);
1006 value_len
= (uint32_t)value_vec
.len
;
1007 value
= value_vec
.base
;
1009 ws_debug("HTTP header: %.*s: %.*s", name_len
, name
, value_len
, value
);
1011 pstr_len
= (name_len
+ value_len
+ 4 + 4);
1012 http3_header_pstr
= (char *)wmem_realloc(wmem_file_scope(), http3_header_pstr
, pstr_len
);
1013 phton32(&http3_header_pstr
[0], name_len
);
1014 memcpy(&http3_header_pstr
[4], name
, name_len
);
1015 phton32(&http3_header_pstr
[4 + name_len
], value_len
);
1016 memcpy(&http3_header_pstr
[4 + name_len
+ 4], value
, value_len
);
1018 /* Lookup a field definition, or create one if needed */
1019 def
= (http3_header_field_def_t
*)wmem_map_lookup(HTTP3_HEADER_NAME_CACHE
, http3_header_pstr
);
1021 char *def_name
= NULL
;
1022 def_name
= (char *)wmem_realloc(wmem_file_scope(), def_name
, name_len
+ 1);
1023 memcpy(def_name
, name
, name_len
);
1025 def
= wmem_new0(wmem_file_scope(), http3_header_field_def_t
);
1026 def
->name_len
= name_len
;
1027 def
->name
= (const char *)def_name
;
1029 wmem_map_insert(HTTP3_HEADER_NAME_CACHE
, http3_header_pstr
, def
);
1030 /* XXX: keys are not copied in wmem_maps, so once we use
1031 * http3_header_pstr, we should set it to NULL so that
1032 * the memory pointed to won't be realloc'ed. However,
1033 * we'll do that in the other map below, as we only insert
1034 * into these maps at the same time, we are guaranteed
1035 * that we will be setting it to NULL below. This is
1036 * fragile and should be replaced with a single map.
1037 * I also don't see the point of this map considering
1038 * that the name and name_len are contained within the
1039 * pstr value and can (and are) parsed from it; this
1040 * map doesn't seem to be used currently.
1044 /* Create an output field and add it to the headers array */
1045 out
= wmem_new0(wmem_file_scope(), http3_header_field_t
);
1048 cached_pstr
= (char *)wmem_map_lookup(HTTP3_HEADER_CACHE
, http3_header_pstr
);
1050 out
->decoded
.pstr
= cached_pstr
;
1052 out
->decoded
.pstr
= http3_header_pstr
;
1053 wmem_map_insert(HTTP3_HEADER_CACHE
, http3_header_pstr
, http3_header_pstr
);
1054 http3_header_pstr
= NULL
;
1056 out
->decoded
.pstr_len
= pstr_len
;
1058 wmem_array_append(header_data
->header_fields
, out
, 1);
1061 * Decrease the reference counts on the NGHTTP3 nv structure to avoid
1064 nghttp3_rcbuf_decref(nv
.name
);
1065 nghttp3_rcbuf_decref(nv
.value
);
1067 proto_tree_add_expert_format(tree
, pinfo
, &ei_http3_header_decoding_no_output
, tvb
, tvb_offset
, 0,
1068 "QPACK - nothing emitted decoder %p ctx %p flags %" PRIu8
" error %d (%s)",
1069 decoder
, sctx
, flags
, nread
, nghttp3_strerror((int)nread
));
1073 * Check whether the QPACK decoder has finished.
1075 if (nread
== 0 || (flags
& NGHTTP3_QPACK_DECODE_FLAG_FINAL
)) {
1079 HEADER_BLOCK_ENC_ITER_INC(header_data
, nread
);
1081 nghttp3_qpack_stream_context_del(sctx
);
1084 if ((header_data
->header_fields
== NULL
) || (wmem_array_get_count(header_data
->header_fields
) == 0)) {
1088 header_tvb
= tvb_new_composite();
1090 for (unsigned i
= 0; i
< wmem_array_get_count(header_data
->header_fields
); ++i
) {
1091 http3_header_field_t
*in
;
1094 in
= (http3_header_field_t
*)wmem_array_index(header_data
->header_fields
, i
);
1095 header_len
+= in
->decoded
.pstr_len
;
1097 /* Now setup the tvb buffer to have the new data */
1098 next_tvb
= tvb_new_child_real_data(tvb
, in
->decoded
.pstr
, in
->decoded
.pstr_len
, in
->decoded
.pstr_len
);
1099 tvb_composite_append(header_tvb
, next_tvb
);
1102 tvb_composite_finalize(header_tvb
);
1103 add_new_data_source(pinfo
, header_tvb
, "Decompressed Header");
1105 ti
= proto_tree_add_uint(tree
, hf_http3_header_length
, header_tvb
, hoffset
, 1, header_len
);
1106 proto_item_set_generated(ti
);
1108 ti
= proto_tree_add_uint(tree
, hf_http3_headers_count
, header_tvb
, hoffset
, 1,
1109 wmem_array_get_count(header_data
->header_fields
));
1110 proto_item_set_generated(ti
);
1112 for (unsigned i
= 0; i
< wmem_array_get_count(header_data
->header_fields
); ++i
) {
1113 http3_header_field_t
*in
;
1115 in
= (http3_header_field_t
*)wmem_array_index(header_data
->header_fields
, i
);
1117 /* Populate tree with header name/value details. */
1118 /* Add 'Header' subtree with description. */
1119 header
= proto_tree_add_item(tree
, hf_http3_header
, tvb
, tvb_offset
, in
->encoded
.len
, ENC_NA
);
1121 header_tree
= proto_item_add_subtree(header
, ett_http3_headers
);
1123 /* header value length */
1124 proto_tree_add_item_ret_uint(header_tree
, hf_http3_header_name_length
, header_tvb
, hoffset
, 4,
1125 ENC_BIG_ENDIAN
, &header_name_length
);
1128 /* Add header name. */
1129 proto_tree_add_item_ret_string(header_tree
, hf_http3_header_name
, header_tvb
, hoffset
, header_name_length
,
1130 ENC_ASCII
| ENC_NA
, pinfo
->pool
, &header_name
);
1131 hoffset
+= header_name_length
;
1133 /* header value length */
1134 proto_tree_add_item_ret_uint(header_tree
, hf_http3_header_value_length
, header_tvb
, hoffset
, 4,
1135 ENC_BIG_ENDIAN
, &header_value_length
);
1138 /* Add header value. */
1139 proto_tree_add_item_ret_string(header_tree
, hf_http3_header_value
, header_tvb
, hoffset
, header_value_length
,
1140 ENC_ASCII
| ENC_NA
, pinfo
->pool
, &header_value
);
1142 ti_named_field
= try_add_named_header_field(header_tree
, header_tvb
, hoffset
, header_value_length
, header_name
,
1145 hoffset
+= header_value_length
;
1147 proto_item_append_text(header
, ": %s: %s", header_name
, header_value
);
1149 /* Display :method, :path and :status in info column (just like http1.1 dissector does)*/
1150 if (strcmp(header_name
, HTTP3_HEADER_NAME_METHOD
) == 0) {
1151 method_header_value
= header_value
;
1152 try_append_method_path_info(pinfo
, tree
, method_header_value
, path_header_value
, authority_header_value
);
1153 } else if (strcmp(header_name
, HTTP3_HEADER_NAME_PATH
) == 0) {
1154 path_header_value
= header_value
;
1155 try_append_method_path_info(pinfo
, tree
, method_header_value
, path_header_value
, authority_header_value
);
1156 http_add_path_components_to_tree(header_tvb
, pinfo
, ti_named_field
, hoffset
- header_value_length
,
1157 header_value_length
);
1158 } else if (strcmp(header_name
, HTTP3_HEADER_NAME_AUTHORITY
) == 0) {
1159 authority_header_value
= header_value
;
1160 try_append_method_path_info(pinfo
, tree
, method_header_value
, path_header_value
, authority_header_value
);
1161 } else if (strcmp(header_name
, HTTP3_HEADER_NAME_STATUS
) == 0) {
1162 const char *reason_phase
=
1163 val_to_str_const((unsigned)strtoul(header_value
, NULL
, 10), vals_http_status_code
, "Unknown");
1164 /* append response status and reason phrase to info column (for example, HEADERS: 200 OK) */
1165 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, ": ", "%s %s", header_value
, reason_phase
);
1166 /* append response status and reason phrase to header_tree and Stream node */
1167 proto_item_append_text(header_tree
, " %s", reason_phase
);
1168 proto_item_append_text(tree
, ", %s %s", header_value
, reason_phase
);
1169 } else if (strcmp(header_name
, HTTP3_HEADER_NAME_AUTHORITY
) == 0) {
1170 authority_header_value
= header_value
;
1171 } else if (strcmp(header_name
, HTTP3_HEADER_NAME_SCHEME
) == 0) {
1172 scheme_header_value
= header_value
;
1175 tvb_offset
+= in
->encoded
.len
;
1179 * Use the `:authority' Header as an indication that this packet is a request.
1181 if (authority_header_value
) {
1186 * https://www.ietf.org/rfc/rfc9114.html#name-request-pseudo-header-field
1188 * All HTTP/3 requests MUST include exactly one value for the `:method',
1189 * `:scheme', and `:path' pseudo-header fields, unless the request is
1190 * a `CONNECT' request; see Section 4.4.
1192 if (method_header_value
&& strcmp(method_header_value
, HTTP3_HEADER_METHOD_CONNECT
) == 0) {
1193 uri
= wmem_strdup(pinfo
->pool
, authority_header_value
);
1195 uri
= wmem_strdup_printf(pinfo
->pool
, "%s://%s%s", scheme_header_value
, authority_header_value
,
1198 e_ti
= proto_tree_add_string(tree
, hf_http3_header_request_full_uri
, tvb
, 0, 0, uri
);
1199 proto_item_set_url(e_ti
);
1200 proto_item_set_generated(e_ti
);
1205 #endif /* HAVE_NGHTTP3 */
1207 static http3_session_info_t
*
1208 http3_session_new(void)
1210 http3_session_info_t
*http3_session
;
1212 http3_session
= wmem_new0(wmem_file_scope(), http3_session_info_t
);
1215 http3_initialize_qpack_decoders(http3_session
);
1218 return http3_session
;
1221 static http3_session_info_t
*
1222 http3_session_lookup_or_create(packet_info
*pinfo
)
1224 http3_session_info_t
*http3_session
;
1226 /* First, try to look up the session by initial QUIC DCID */
1227 quic_cid_t initial_dcid
= {0};
1228 if (quic_conn_data_get_conn_client_dcid_initial(pinfo
, &initial_dcid
)) {
1229 /* Look up the session data in the conn map */
1230 http3_session
= (http3_session_info_t
*)wmem_map_lookup(HTTP3_CONN_INFO_MAP
, &initial_dcid
);
1231 if (http3_session
== NULL
) {
1232 quic_cid_t
*dcid_p
= wmem_memdup(wmem_file_scope(), &initial_dcid
, sizeof(initial_dcid
));
1233 http3_session
= http3_session_new();
1234 wmem_map_insert(HTTP3_CONN_INFO_MAP
, dcid_p
, http3_session
);
1237 /* Initial DCID can not be found, use the 5-tuple for lookup */
1238 conversation_t
*conversation
= find_or_create_conversation(pinfo
);
1239 http3_session
= (http3_session_info_t
*)conversation_get_proto_data(conversation
, proto_http3
);
1241 if (http3_session
== NULL
) {
1242 http3_session
= http3_session_new();
1243 conversation_add_proto_data(conversation
, proto_http3
, http3_session
);
1247 return http3_session
;
1251 static conversation_t
*
1252 http3_find_inner_conversation(packet_info
*pinfo
, quic_stream_info
*stream_info
, http3_stream_info_t
*http3_stream
, void **ctx
)
1254 conversation_t
*inner_conv
= NULL
;
1256 if (stream_info
!= NULL
) {
1258 *ctx
= pinfo
->conv_elements
;
1261 wmem_array_t
*conversation_elements
= wmem_array_new(pinfo
->pool
, sizeof(conversation_element_t
));
1263 conversation_element_t h3_stream_addr
= {
1265 .addr_val
= (pinfo
->srcport
< pinfo
->destport
) ? pinfo
->src
: pinfo
->dst
,
1267 wmem_array_append_one(conversation_elements
, h3_stream_addr
);
1269 conversation_element_t h3_stream_port
= {
1271 .port_val
= (pinfo
->srcport
< pinfo
->destport
) ? pinfo
->srcport
: pinfo
->destport
,
1273 wmem_array_append_one(conversation_elements
, h3_stream_port
);
1275 conversation_element_t h3_stream_quic_stream
= {
1277 .uint64_val
= http3_stream
->id
,
1279 wmem_array_append_one(conversation_elements
, h3_stream_quic_stream
);
1281 conversation_element_t h3_stream_last
= {
1282 .type
= CE_CONVERSATION_TYPE
,
1283 .conversation_type_val
= CONVERSATION_LOG
,
1285 wmem_array_append_one(conversation_elements
, h3_stream_last
);
1287 pinfo
->conv_elements
= (conversation_element_t
*)wmem_array_get_raw(conversation_elements
);
1288 inner_conv
= find_conversation_pinfo(pinfo
, 0);
1290 inner_conv
= conversation_new_full(pinfo
->fd
->num
, pinfo
->conv_elements
);
1298 http3_reset_inner_conversation(packet_info
*pinfo
, void *ctx
)
1301 struct conversation_element
*conv_elements
= (struct conversation_element
*)ctx
;
1302 pinfo
->conv_elements
= conv_elements
;
1307 dissect_http3_data(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*http3_tree
, unsigned offset _U_
,
1308 quic_stream_info
*stream_info
, http3_stream_info_t
*http3_stream
)
1310 void *saved_ctx
= NULL
;
1312 conversation_t
*inner_conv _U_
;
1313 proto_item
*ti_data _U_
;
1315 remaining
= tvb_reported_length(tvb
);
1316 inner_conv
= http3_find_inner_conversation(pinfo
, stream_info
, http3_stream
, &saved_ctx
);
1317 ti_data
= proto_tree_add_item(http3_tree
, hf_http3_data
, tvb
, offset
, remaining
, ENC_NA
);
1318 http3_reset_inner_conversation(pinfo
, saved_ctx
);
1320 return tvb_reported_length(tvb
);
1325 dissect_http3_settings(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*http3_tree
, unsigned offset
)
1327 uint64_t settingsid
, value
;
1329 proto_item
*ti_settings
, *pi
;
1330 proto_tree
*settings_tree
;
1332 while (tvb_reported_length_remaining(tvb
, offset
) > 0) {
1333 ti_settings
= proto_tree_add_item(http3_tree
, hf_http3_settings
, tvb
, offset
, 2, ENC_NA
);
1334 settings_tree
= proto_item_add_subtree(ti_settings
, ett_http3_settings
);
1335 pi
= proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_identifier
, tvb
, offset
, -1,
1336 ENC_VARINT_QUIC
, &settingsid
, &lenvar
);
1337 /* Check if it is a GREASE Settings ID */
1338 if (http3_is_reserved_code(settingsid
)) {
1339 proto_item_set_text(pi
, "Settings Identifier: Reserved (%#" PRIx64
")", settingsid
);
1340 proto_item_append_text(ti_settings
, " - Reserved (GREASE)");
1342 proto_item_append_text(ti_settings
, " - %s",
1343 val64_to_str(settingsid
, http3_settings_vals
, "Unknown (%#" PRIx64
")"));
1347 proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_value
, tvb
, offset
, -1, ENC_VARINT_QUIC
, NULL
,
1350 switch (settingsid
) {
1351 case HTTP3_QPACK_MAX_TABLE_CAPACITY
:
1352 proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_qpack_max_table_capacity
, tvb
, offset
, -1,
1353 ENC_VARINT_QUIC
, &value
, &lenvar
);
1354 proto_item_append_text(ti_settings
, ": %" PRIu64
, value
);
1356 case HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE
:
1357 proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_max_field_section_size
, tvb
, offset
, -1,
1358 ENC_VARINT_QUIC
, &value
, &lenvar
);
1359 proto_item_append_text(ti_settings
, ": %" PRIu64
, value
);
1361 case HTTP3_QPACK_BLOCKED_STREAMS
:
1362 proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_qpack_blocked_streams
, tvb
, offset
, -1,
1363 ENC_VARINT_QUIC
, &value
, &lenvar
);
1364 proto_item_append_text(ti_settings
, ": %" PRIu64
, value
);
1366 case HTTP3_EXTENDED_CONNECT
:
1367 proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_extended_connect
, tvb
, offset
, -1,
1368 ENC_VARINT_QUIC
, &value
, &lenvar
);
1369 proto_item_append_text(ti_settings
, ": %" PRIu64
, value
);
1371 case HTTP3_WEBTRANSPORT
:
1372 proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_webtransport
, tvb
, offset
, -1,
1373 ENC_VARINT_QUIC
, &value
, &lenvar
);
1374 proto_item_append_text(ti_settings
, ": %" PRIu64
, value
);
1376 case HTTP3_H3_DATAGRAM
:
1377 proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_h3_datagram
, tvb
, offset
, -1,
1378 ENC_VARINT_QUIC
, &value
, &lenvar
);
1379 proto_item_append_text(ti_settings
, ": %" PRIu64
, value
);
1381 case HTTP3_H3_DATAGRAM_DRAFT04
:
1382 proto_tree_add_item_ret_varint(settings_tree
, hf_http3_settings_h3_datagram_draft04
, tvb
, offset
, -1,
1383 ENC_VARINT_QUIC
, &value
, &lenvar
);
1384 proto_item_append_text(ti_settings
, ": %" PRIu64
, value
);
1400 dissect_http3_priority_update(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*http3_tree
, unsigned offset
,
1401 uint64_t frame_length
)
1403 uint64_t priority_field_value_len
;
1406 proto_tree_add_item_ret_varint(http3_tree
, hf_http3_priority_update_element_id
, tvb
, offset
, -1, ENC_VARINT_QUIC
,
1409 priority_field_value_len
= frame_length
- lenvar
;
1411 proto_tree_add_item(http3_tree
, hf_http3_priority_update_field_value
, tvb
, offset
, (int)priority_field_value_len
,
1413 offset
+= (int)priority_field_value_len
;
1419 dissect_http3_frame(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
, quic_stream_info
*stream_info
, http3_stream_info_t
*http3_stream
)
1421 uint64_t frame_type
, frame_length
;
1422 int type_length_size
, lenvar
, payload_length
;
1423 proto_item
*ti_ft
, *ti_ft_type
;
1424 proto_tree
*ft_tree
;
1425 const char *ft_display_name
;
1427 ti_ft
= proto_tree_add_item(tree
, hf_http3_frame
, tvb
, offset
, -1, ENC_NA
);
1428 ft_tree
= proto_item_add_subtree(ti_ft
, ett_http3_frame
);
1430 ti_ft_type
= proto_tree_add_item_ret_varint(ft_tree
, hf_http3_frame_type
, tvb
, offset
, -1, ENC_VARINT_QUIC
, &frame_type
,
1433 type_length_size
= lenvar
;
1434 if (http3_is_reserved_code(frame_type
)) {
1435 proto_item_set_text(ti_ft_type
, "Type: Reserved (%#" PRIx64
")", frame_type
);
1436 ft_display_name
= "Reserved (GREASE)";
1438 ft_display_name
= val64_to_str(frame_type
, http3_frame_types
, "Unknown (%#" PRIx64
")");
1439 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ", ", ft_display_name
);
1441 proto_tree_add_item_ret_varint(ft_tree
, hf_http3_frame_length
, tvb
, offset
, -1, ENC_VARINT_QUIC
, &frame_length
,
1443 proto_item_set_text(ti_ft
, "%s len=%" PRId64
, ft_display_name
, frame_length
);
1445 type_length_size
+= lenvar
;
1447 if (frame_length
>= (uint64_t)(INT32_MAX
- type_length_size
)) {
1448 // There is no way for us to correctly handle these sizes. Most likely
1453 payload_length
= (int)frame_length
;
1454 proto_item_set_len(ti_ft
, type_length_size
+ payload_length
);
1455 if (payload_length
== 0) {
1459 proto_tree_add_item(ft_tree
, hf_http3_frame_payload
, tvb
, offset
, payload_length
, ENC_NA
);
1461 switch (frame_type
) {
1462 case HTTP3_DATA
: { /* TODO: dissect Data Frame */
1463 tvbuff_t
*next_tvb
= tvb_new_subset_length(tvb
, offset
, payload_length
);
1464 dissect_http3_data(next_tvb
, pinfo
, ft_tree
, 0, stream_info
, http3_stream
);
1466 case HTTP3_HEADERS
: {
1468 tvbuff_t
*next_tvb
= tvb_new_subset_length(tvb
, offset
, payload_length
);
1469 dissect_http3_headers(next_tvb
, pinfo
, ft_tree
, 0, offset
, stream_info
, http3_stream
);
1470 #endif /* HAVE_NGHTTP3 */
1472 case HTTP3_CANCEL_PUSH
: /* TODO: dissect Cancel_Push Frame */
1474 case HTTP3_SETTINGS
: { /* Settings Frame */
1475 tvbuff_t
*next_tvb
= tvb_new_subset_length(tvb
, offset
, payload_length
);
1476 dissect_http3_settings(next_tvb
, pinfo
, ft_tree
, 0);
1478 case HTTP3_PUSH_PROMISE
: /* TODO: dissect Push_Promise_Frame */
1480 case HTTP3_GOAWAY
: /* TODO: dissect Goaway Frame */
1482 case HTTP3_MAX_PUSH_ID
: /* TODO: dissect Max_Push_ID Frame */
1484 case HTTP3_PRIORITY_UPDATE_REQUEST_STREAM
:
1485 case HTTP3_PRIORITY_UPDATE_PUSH_STREAM
: { /* Priority_Update Frame */
1486 tvbuff_t
*next_tvb
= tvb_new_subset_length(tvb
, offset
, payload_length
);
1487 dissect_http3_priority_update(next_tvb
, pinfo
, ft_tree
, 0, frame_length
);
1489 default: /* TODO: add expert_advise */
1492 offset
+= payload_length
;
1497 report_unknown_stream_type(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
,
1498 quic_stream_info
*stream_info
, http3_stream_info_t
*http3_stream
)
1501 * https://www.rfc-editor.org/rfc/rfc9114.html#name-unidirectional-streams
1503 * "If the stream header indicates a stream type which is not supported by
1504 * the recipient, the remainder of the stream cannot be consumed as the
1505 * semantics are unknown."
1507 proto_tree_add_expert_format(tree
, pinfo
, &ei_http3_unknown_stream_type
, tvb
, offset
, 0,
1508 "Unknown stream type %#" PRIx64
" on Stream ID %#" PRIx64
,
1509 http3_stream
->uni_stream_type
, stream_info
->stream_id
);
1513 * https://www.rfc-editor.org/rfc/rfc7541#section-5.1
1515 * https://www.rfc-editor.org/rfc/rfc9204.html#name-prefixed-integers
1517 * Read a QPACK varint value, return number of consumed bytes, including the prefix byte.
1519 * Optionally return the value of the one-bit flag that precedes the QPACK prefixed integer.
1521 * Such flag is is interpreted differently, depending on the context:
1522 * - If the prefixed integer represents length of a string literal, the flag value
1523 * indicates that the following string literal is encoded using Huffman code.
1524 * See https://www.rfc-editor.org/rfc/rfc7541#section-5.2 for details.
1525 * - If the prefixed integer represents a name index, the flag value indicates
1526 * that the following name index belongs to the static/dynamic table.
1527 * See https://www.rfc-editor.org/rfc/rfc9204.html#name-insert-with-name-reference
1530 #define HTTP3_QPACK_MAX_SHIFT 62
1531 #define HTTP3_QPACK_MAX_INT ((1ull << HTTP3_QPACK_MAX_SHIFT) - 1)
1534 read_qpack_prefixed_integer(tvbuff_t
*tvb
, int offset
, int prefix
,
1535 uint64_t *out_result
, bool *out_fin
, bool *out_flag
)
1538 * This can throw a ReportedBoundError; in fact, we count on that
1539 * currently in order to detect QPACK fields split across packets.
1541 const uint8_t *buf
= tvb_get_ptr(tvb
, offset
, -1);
1542 const uint8_t *end
= buf
+ tvb_captured_length_remaining(tvb
, offset
);
1543 uint64_t k
= (uint8_t)((1 << prefix
) - 1);
1547 const uint8_t *p
= buf
;
1550 *out_flag
= *p
& (1 << prefix
);
1553 if (((*p
) & k
) != k
) {
1554 *out_result
= (*p
) & k
;
1564 return (int)(p
- buf
);
1567 for (; p
!= end
; ++p
, shift
+= 7) {
1569 if (shift
> HTTP3_QPACK_MAX_SHIFT
) {
1572 if ((HTTP3_QPACK_MAX_INT
>> shift
) < add
) {
1576 if (HTTP3_QPACK_MAX_INT
- add
< n
) {
1582 if (((*p
) & (1 << 7)) == 0) {
1589 /* If we consumed all bytes, return the consumed bytes */
1592 return (int)(p
- buf
);
1595 /* Otherwise, consume extra byte and mark the fin output param */
1599 return (int)(p
+ 1 - buf
);
1603 dissect_http3_qpack_encoder_stream(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
,
1604 int offset
, http3_stream_info_t
*http3_stream _U_
)
1607 proto_item
*opcode_ti
;
1608 proto_tree
*opcode_tree
;
1609 tvbuff_t
*decoded_tvb
;
1610 unsigned decoded
= 0;
1613 volatile bool can_continue
= true;
1615 remaining
= tvb_captured_length_remaining(tvb
, offset
);
1617 while (decoded
< remaining
&& can_continue
) {
1618 int opcode_offset
= offset
+ decoded
;
1624 opcode
= tvb_get_uint8(tvb
, opcode_offset
) & QPACK_OPCODE_MASK
;
1626 ws_noisy("Decoding opcode=%" PRIu8
" decoded=%d remaining=%d", opcode
, decoded
, remaining
);
1628 if (opcode
& QPACK_OPCODE_INSERT_INDEXED
) {
1629 int table_entry_len
= 0;
1630 uint64_t table_entry
= 0;
1631 int value_offset
= 0;
1633 int val_bytes_offset
= 0;
1634 uint64_t val_bytes_len
= 0;
1635 bool value_huffman
= false;
1639 * +---+---+---+---+---+---+---+---+
1640 * | 1 | T | Name Index (6+) |
1641 * +---+---+-----------------------+
1642 * | H | Value Length (7+) |
1643 * +---+---------------------------+
1644 * | Value String (Length bytes) |
1645 * +-------------------------------+
1648 decoded
+= read_qpack_prefixed_integer(tvb
, opcode_offset
, 6, &table_entry
, &fin
, NULL
);
1649 table_entry_len
= offset
+ decoded
- opcode_offset
;
1651 value_offset
= offset
+ decoded
;
1652 decoded
+= read_qpack_prefixed_integer(tvb
, value_offset
, 7, &val_bytes_len
, &fin
, &value_huffman
);
1653 val_bytes_offset
= offset
+ decoded
;
1655 decoded
+= (uint32_t)val_bytes_len
;
1656 value_len
= offset
+ decoded
- value_offset
;
1658 opcode_len
= offset
+ decoded
- opcode_offset
;
1660 opcode_ti
= proto_tree_add_item(tree
, hf_http3_qpack_encoder_opcode_insert_indexed
, tvb
, opcode_offset
,
1661 opcode_len
, ENC_NA
);
1662 opcode_tree
= proto_item_add_subtree(opcode_ti
, ett_http3_qpack_opcode
);
1663 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_indexed_ref
, tvb
, opcode_offset
,
1664 table_entry_len
, ENC_NA
);
1665 if (value_huffman
) {
1666 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_indexed_hval
, tvb
,
1667 val_bytes_offset
, (uint32_t)val_bytes_len
, ENC_NA
);
1668 decoded_tvb
= tvb_child_uncompress_hpack_huff(tvb
, (int)val_bytes_offset
, (int)val_bytes_len
);
1670 add_new_data_source(pinfo
, decoded_tvb
, "Decoded QPACK Value");
1671 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_indexed_val
, decoded_tvb
,
1672 0, tvb_captured_length(decoded_tvb
), ENC_NA
);
1675 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_indexed_val
, tvb
,
1676 val_bytes_offset
, (uint32_t)val_bytes_len
, ENC_NA
);
1678 proto_item_set_text(opcode_ti
, "QPACK encoder INSERT_INDEXED ref_len=%d ref=%" PRIu64
" val_len=%d",
1679 table_entry_len
, table_entry
, value_len
);
1680 } else if (opcode
& QPACK_OPCODE_INSERT
) {
1681 unsigned name_len_offset
= 0;
1682 unsigned name_len_len
= 0;
1683 unsigned name_len
= 0;
1684 bool name_huffman
= false;
1685 unsigned name_bytes_offset
= 0;
1686 uint64_t name_bytes_len
= 0;
1687 unsigned val_len_offset
= 0;
1688 unsigned val_len_len
= 0;
1689 unsigned val_len
= 0;
1690 bool value_huffman
= false;
1691 unsigned val_bytes_offset
= 0;
1692 uint64_t val_bytes_len
= 0;
1695 * Insert with literal name:
1696 * See https://datatracker.ietf.org/doc/html/rfc9204#name-insert-with-literal-name
1699 * +---+---+---+---+---+---+---+---+
1700 * | 0 | 1 | H | Name Length (5+) |
1701 * +---+---+---+-------------------+
1702 * | Name String (Length bytes) |
1703 * +---+---------------------------+
1704 * | H | Value Length (7+) |
1705 * +---+---------------------------+
1706 * | Value String (Length bytes) |
1707 * +-------------------------------+
1711 /* Read the 5-encoded name length */
1712 name_len_offset
= offset
+ decoded
;
1713 decoded
+= read_qpack_prefixed_integer(tvb
, name_len_offset
, 5, &name_bytes_len
, &fin
, &name_huffman
);
1714 name_len_len
= offset
+ decoded
- name_len_offset
;
1715 name_len
= name_len_len
+ (uint32_t)name_bytes_len
;
1716 name_bytes_offset
= offset
+ decoded
;
1717 decoded
+= (uint32_t)name_bytes_len
;
1719 /* Read the 7-encoded value length */
1720 val_len_offset
= offset
+ decoded
;
1721 decoded
+= read_qpack_prefixed_integer(tvb
, val_len_offset
, 7, &val_bytes_len
, &fin
, &value_huffman
);
1722 val_len_len
= offset
+ decoded
- val_len_offset
;
1723 val_len
= val_len_len
+ (uint32_t)val_bytes_len
;
1724 val_bytes_offset
= offset
+ decoded
;
1726 decoded
+= (uint32_t)val_bytes_len
;
1728 opcode_len
= offset
+ decoded
- opcode_offset
;
1730 proto_tree_add_item(tree
, hf_http3_qpack_encoder_opcode_insert
, tvb
, opcode_offset
, opcode_len
, ENC_NA
);
1731 opcode_tree
= proto_item_add_subtree(opcode_ti
, ett_http3_qpack_opcode
);
1733 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_hname
, tvb
, name_bytes_offset
,
1734 (uint32_t)name_bytes_len
, ENC_NA
);
1735 decoded_tvb
= tvb_child_uncompress_hpack_huff(tvb
, (int)name_bytes_offset
, (int)name_bytes_len
);
1737 add_new_data_source(pinfo
, decoded_tvb
, "Decoded QPACK Name");
1738 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_name
, decoded_tvb
,
1739 0, tvb_captured_length(decoded_tvb
), ENC_NA
);
1742 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_name
, tvb
, name_bytes_offset
,
1743 (uint32_t)name_bytes_len
, ENC_NA
);
1746 if (value_huffman
) {
1747 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_hval
, tvb
, val_bytes_offset
,
1748 (uint32_t)val_bytes_len
, ENC_NA
);
1749 decoded_tvb
= tvb_child_uncompress_hpack_huff(tvb
, (int)val_bytes_offset
, (int)val_bytes_len
);
1751 add_new_data_source(pinfo
, decoded_tvb
, "Decoded QPACK Value");
1752 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_val
, decoded_tvb
,
1753 0, tvb_captured_length(decoded_tvb
), ENC_NA
);
1756 proto_tree_add_item(opcode_tree
, hf_http3_qpack_encoder_opcode_insert_val
, tvb
, val_bytes_offset
,
1757 (uint32_t)val_bytes_len
, ENC_NA
);
1759 proto_item_set_text(opcode_ti
, "QPACK encoder opcode: INSERT name_len=%d val_len=%d", name_len
, val_len
);
1760 } else if (opcode
& QPACK_OPCODE_SET_DTABLE_CAP
) {
1761 uint64_t dynamic_capacity
= 0;
1765 * +---+---+---+---+---+---+---+---+
1766 * | 0 | 0 | 1 | Capacity (5+) |
1767 * +---+---+---+-------------------+
1770 decoded
+= read_qpack_prefixed_integer(tvb
, opcode_offset
, 5, &dynamic_capacity
, &fin
, NULL
);
1771 opcode_len
= offset
+ decoded
- opcode_offset
;
1773 opcode_ti
= proto_tree_add_item(tree
, hf_http3_qpack_encoder_opcode_dtable_cap
, tvb
, opcode_offset
,
1774 opcode_len
, ENC_NA
);
1775 opcode_tree
= proto_item_add_subtree(opcode_ti
, ett_http3_qpack_opcode
);
1776 proto_tree_add_uint64(opcode_tree
, hf_http3_qpack_encoder_opcode_dtable_cap_val
, tvb
, opcode_offset
, opcode_len
,
1778 proto_item_set_text(opcode_ti
, "QPACK encoder opcode: Set DTable Cap=%" PRIu64
, dynamic_capacity
);
1779 } else if (opcode
== QPACK_OPCODE_DUPLICATE
) {
1780 uint64_t duplicate_of
= 0;
1784 * +---+---+---+---+---+---+---+---+
1785 * | 0 | 0 | 0 | Index (5+) |
1786 * +---+---+---+-------------------+
1789 inc
= read_qpack_prefixed_integer(tvb
, opcode_offset
, 5, &duplicate_of
, &fin
, NULL
);
1790 DISSECTOR_ASSERT(0 < inc
);
1791 DISSECTOR_ASSERT(decoded
+ inc
<= remaining
);
1794 opcode_len
= offset
+ decoded
- opcode_offset
;
1795 proto_tree_add_item(tree
, hf_http3_qpack_encoder_opcode_duplicate
, tvb
, opcode_offset
,
1796 opcode_len
, ENC_NA
);
1798 ws_debug("Opcode=%" PRIu8
": UNKNOWN", opcode
);
1799 can_continue
= false;
1802 CATCH(ReportedBoundsError
) {
1803 decoded
= opcode_offset
- offset
;
1804 can_continue
= false;
1813 dissect_http3_qpack_enc(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
,
1814 quic_stream_info
*stream_info
, http3_stream_info_t
*http3_stream
)
1816 int remaining
, remaining_captured
, retval
, decoded
= 0;
1817 proto_item
* qpack_update
;
1818 proto_tree
* qpack_update_tree
;
1819 http3_session_info_t
*http3_session
;
1821 remaining_captured
= tvb_captured_length_remaining(tvb
, offset
);
1822 remaining
= tvb_reported_length_remaining(tvb
, offset
);
1823 DISSECTOR_ASSERT(remaining_captured
== remaining
);
1826 http3_session
= http3_session_lookup_or_create(pinfo
);
1827 DISSECTOR_ASSERT(http3_session
);
1830 * Add a QPACK encoder tree item.
1832 qpack_update
= proto_tree_add_item(tree
, hf_http3_qpack_encoder
, tvb
, offset
, remaining
, ENC_NA
);
1833 qpack_update_tree
= proto_item_add_subtree(qpack_update
, ett_http3_qpack_update
);
1834 decoded
= dissect_http3_qpack_encoder_stream(tvb
, pinfo
, qpack_update_tree
, offset
,
1837 if (!PINFO_FD_VISITED(pinfo
)) {
1838 ws_debug("decode encoder stream: Wireshark decoded=%u of %u", decoded
, remaining
);
1840 if (decoded
< remaining
) {
1841 pinfo
->desegment_offset
= offset
+ decoded
;
1842 pinfo
->desegment_len
= DESEGMENT_ONE_MORE_SEGMENT
;
1846 if (remaining
> 0) {
1848 http3_stream_dir packet_direction
= http3_packet_get_direction(stream_info
);
1849 nghttp3_qpack_decoder
*decoder
= http3_session
->qpack_decoder
[packet_direction
];
1851 http3_qpack_encoder_state_t
*encoder_state
= http3_get_qpack_encoder_state(pinfo
, tvb
, offset
);
1853 if (!PINFO_FD_VISITED(pinfo
)) {
1856 * Since we are now defragmenting, pass only the number of bytes
1857 * decoded to the nghttp3_qpack_decoder. Otherwise, we'll end up
1858 * sending the same bytes to the decoder again when the packet
1861 uint8_t *qpack_buf
= (uint8_t *)tvb_memdup(pinfo
->pool
, tvb
, offset
, decoded
);
1862 int qpack_buf_len
= decoded
;
1865 * Get the instr count prior to processing the data.
1867 uint64_t icnt_before
= nghttp3_qpack_decoder_get_icnt(decoder
);
1869 encoder_state
->nread
= nghttp3_qpack_decoder_read_encoder(decoder
, qpack_buf
, qpack_buf_len
);
1870 encoder_state
->icnt
= nghttp3_qpack_decoder_get_icnt(decoder
);
1871 encoder_state
->icnt_inc
= (uint32_t)(encoder_state
->icnt
- icnt_before
);
1873 ws_debug("decode encoder stream: decoder=%p nread=%td new insertions=%u total insertions=%" PRIu64
, decoder
, encoder_state
->nread
, encoder_state
->icnt_inc
, encoder_state
->icnt
);
1876 /* nghttp3_qpack_decoder_read_encoder() returns a nghttp3_ssize
1877 * (ptrdiff_t), negative in the case of errors, but nghttp3_strerror()
1878 * accepts int instead.
1880 if (encoder_state
->nread
< 0) {
1881 quic_cid_t quic_cid
= {.len
= 0};
1882 bool initial_cid_found
= quic_conn_data_get_conn_client_dcid_initial(pinfo
, &quic_cid
);
1883 proto_tree_add_expert_format(
1884 tree
, pinfo
, &ei_http3_qpack_failed
, tvb
, offset
, 0, "QPACK decoder %p DCID %s [found=%d] error %d (%s)",
1885 decoder
, cid_to_string(&quic_cid
, pinfo
->pool
), initial_cid_found
, (int)encoder_state
->nread
, nghttp3_strerror((int)encoder_state
->nread
));
1888 proto_item_set_text(qpack_update
, "QPACK encoder stream; %d opcodes (%" PRIu64
" total)", encoder_state
->icnt_inc
,
1889 encoder_state
->icnt
);
1891 ti
= proto_tree_add_uint(qpack_update_tree
, hf_http3_qpack_encoder_icnt_inc
, tvb
, offset
, 0,
1892 encoder_state
->icnt_inc
);
1893 proto_item_set_generated(ti
);
1894 ti
= proto_tree_add_uint64(qpack_update_tree
, hf_http3_qpack_encoder_icnt
, tvb
, offset
, 0,
1895 encoder_state
->icnt
);
1896 proto_item_set_generated(ti
);
1902 #endif /* HAVE_NGHTTP3 */
1908 dissect_http3_client_bidi_stream(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
,
1909 quic_stream_info
*stream_info
, http3_stream_info_t
*http3_stream
)
1911 proto_item
*ti_stream
;
1912 proto_tree
*stream_tree
;
1914 ti_stream
= proto_tree_add_item(tree
, hf_http3_stream_bidi
, tvb
, offset
, 1, ENC_NA
);
1915 stream_tree
= proto_item_add_subtree(ti_stream
, ett_http3_stream_bidi
);
1917 while (tvb_reported_length_remaining(tvb
, offset
)) {
1918 if (!http3_check_frame_size(tvb
, pinfo
, offset
)) {
1919 return tvb_captured_length(tvb
);
1921 offset
= dissect_http3_frame(tvb
, pinfo
, stream_tree
, offset
, stream_info
, http3_stream
);
1928 dissect_http3_uni_stream(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
, quic_stream_info
*stream_info
,
1929 http3_stream_info_t
*http3_stream
)
1931 uint64_t stream_type
;
1933 proto_item
*ti_stream
, *ti_stream_type
;
1934 proto_tree
*stream_tree
;
1935 const char *stream_display_name
;
1937 ti_stream
= proto_tree_add_item(tree
, hf_http3_stream_uni
, tvb
, offset
, -1, ENC_NA
);
1938 stream_tree
= proto_item_add_subtree(ti_stream
, ett_http3_stream_uni
);
1940 if (stream_info
->offset
== 0) {
1941 ti_stream_type
= proto_tree_add_item_ret_varint(stream_tree
, hf_http3_stream_uni_type
, tvb
, offset
, -1, ENC_VARINT_QUIC
, &stream_type
,
1944 http3_stream
->uni_stream_type
= stream_type
;
1945 if (http3_is_reserved_code(stream_type
)) {
1946 // Reserved to exercise requirement that unknown types are ignored.
1947 proto_item_set_text(ti_stream_type
, "Stream Type: Reserved (%#" PRIx64
")", stream_type
);
1948 stream_display_name
= "Reserved (GREASE)";
1951 stream_display_name
= val64_to_str(stream_type
, http3_stream_types
, "Unknown (%#" PRIx64
")");
1953 proto_item_set_text(ti_stream
, "UNI STREAM: %s off=%" PRIu64
, stream_display_name
, stream_info
->stream_offset
);
1955 stream_type
= http3_stream
->uni_stream_type
;
1956 /*ti_stream_type = proto_tree_add_item(stream_tree, hf_http3_stream_uni_type, tvb, offset, -1, ENC_NA);*/
1959 switch (stream_type
) {
1960 case HTTP3_STREAM_TYPE_CONTROL
:
1961 while (tvb_reported_length_remaining(tvb
, offset
)) {
1962 if (!http3_check_frame_size(tvb
, pinfo
, offset
)) {
1963 return tvb_captured_length(tvb
);
1965 offset
= dissect_http3_frame(tvb
, pinfo
, stream_tree
, offset
, stream_info
, http3_stream
);
1968 case HTTP3_STREAM_TYPE_PUSH
:
1969 // The remaining data of this stream consists of HTTP/3 frames.
1970 if (stream_info
->offset
== 0) {
1971 proto_tree_add_item_ret_varint(stream_tree
, hf_http3_push_id
, tvb
, offset
, -1, ENC_VARINT_QUIC
, NULL
, &lenvar
);
1975 case HTTP3_STREAM_TYPE_QPACK_ENCODER
:
1976 offset
= dissect_http3_qpack_enc(tvb
, pinfo
, stream_tree
, offset
, stream_info
, http3_stream
);
1978 case HTTP3_STREAM_TYPE_QPACK_DECODER
:
1980 offset
= tvb_captured_length(tvb
);
1982 case HTTP3_STREAM_TYPE_WEBTRANSPORT
:
1984 offset
= tvb_captured_length(tvb
);
1987 // Unknown or reserved stream type, consume everything.
1988 if (!http3_is_reserved_code(stream_type
)) {
1989 if (!PINFO_FD_VISITED(pinfo
)) {
1990 http3_stream
->broken_from_offset
= stream_info
->offset
+ offset
;
1992 report_unknown_stream_type(tvb
, pinfo
, stream_tree
, offset
, stream_info
, http3_stream
);
1994 offset
= tvb_captured_length(tvb
);
2002 dissect_http3(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
2004 quic_stream_info
* stream_info
= (quic_stream_info
*)data
;
2006 proto_tree
* http3_tree
;
2008 http3_stream_info_t
*http3_stream
;
2014 switch (QUIC_STREAM_TYPE(stream_info
->stream_id
)) {
2015 case QUIC_STREAM_CLIENT_BIDI
:
2016 /* Used for HTTP requests and responses. */
2017 if (!http3_check_frame_size(tvb
, pinfo
, offset
)) {
2018 return tvb_captured_length(tvb
);
2021 case QUIC_STREAM_SERVER_BIDI
:
2022 /* "HTTP/3 does not use server-initiated bidirectional streams,
2023 * though an extension could define a use for these streams." */
2025 case QUIC_STREAM_CLIENT_UNI
:
2026 case QUIC_STREAM_SERVER_UNI
:
2030 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "HTTP3");
2031 // Only clear the columns if this is the first HTTP/3 STREAM in the packet.
2032 if (!proto_is_frame_protocol(pinfo
->layers
, "http3")) {
2033 col_clear(pinfo
->cinfo
, COL_INFO
);
2036 ti
= proto_tree_add_item(tree
, proto_http3
, tvb
, 0, -1, ENC_NA
);
2037 http3_tree
= proto_item_add_subtree(ti
, ett_http3
);
2039 http3_stream
= (http3_stream_info_t
*)quic_stream_get_proto_data(pinfo
, stream_info
);
2040 if (!http3_stream
) {
2041 http3_stream
= wmem_new0(wmem_file_scope(), http3_stream_info_t
);
2042 quic_stream_add_proto_data(pinfo
, stream_info
, http3_stream
);
2043 http3_stream
->id
= stream_info
->stream_id
;
2046 // If a STREAM has unknown data, everything afterwards cannot be dissected.
2047 if (http3_stream
->broken_from_offset
&& http3_stream
->broken_from_offset
<= stream_info
->offset
+ offset
) {
2048 report_unknown_stream_type(tvb
, pinfo
, tree
, offset
, stream_info
, http3_stream
);
2049 return tvb_captured_length(tvb
);
2052 switch (QUIC_STREAM_TYPE(stream_info
->stream_id
)) {
2053 case QUIC_STREAM_CLIENT_BIDI
:
2054 /* Used for HTTP requests and responses. */
2055 dissect_http3_client_bidi_stream(tvb
, pinfo
, http3_tree
, offset
, stream_info
, http3_stream
);
2058 case QUIC_STREAM_SERVER_BIDI
:
2059 /* "HTTP/3 does not use server-initiated bidirectional streams,
2060 * though an extension could define a use for these streams." */
2062 return tvb_captured_length(tvb
);
2064 case QUIC_STREAM_CLIENT_UNI
:
2065 case QUIC_STREAM_SERVER_UNI
:
2066 dissect_http3_uni_stream(tvb
, pinfo
, http3_tree
, offset
, stream_info
, http3_stream
);
2070 return tvb_captured_length(tvb
);
2075 register_static_headers(void)
2077 header_fields_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
2080 * Here hf[x].hfinfo.name is a header method which is used as key
2081 * for matching ids while processing http3 packets.
2083 static hf_register_info hf
[] = {
2084 { &hf_http3_headers_authority
,
2085 { ":authority", "http3.headers.authority",
2086 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2087 "Authority portion of the target URI", HFILL
}
2089 { &hf_http3_headers_status
,
2090 { ":status", "http3.headers.status",
2091 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
2094 { &hf_http3_headers_path
,
2095 { ":path", "http3.headers.path",
2096 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2099 { &hf_http3_headers_method
,
2100 { ":method", "http3.headers.method",
2101 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2104 { &hf_http3_headers_scheme
,
2105 { ":scheme", "http3.headers.scheme",
2106 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2109 { &hf_http3_headers_accept
,
2110 { "accept", "http3.headers.accept",
2111 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2112 "Media types that are acceptable to the user agent", HFILL
}
2114 { &hf_http3_headers_accept_charset
,
2115 { "accept-charset", "http3.headers.accept_charset",
2116 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2117 "Acceptable charsets in textual responses for the user agent", HFILL
}
2119 { &hf_http3_headers_accept_encoding
,
2120 { "accept-encoding", "http3.headers.accept_encoding",
2121 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2122 "Acceptable content codings (like compression) in responses for the user agent", HFILL
}
2124 { &hf_http3_headers_accept_language
,
2125 { "accept-language", "http3.headers.accept_language",
2126 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2127 "Preferred natural languages for the user agent", HFILL
}
2129 { &hf_http3_headers_accept_ranges
,
2130 { "accept-ranges", "http3.headers.accept_ranges",
2131 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2132 "Bytes range which server may use for partial data transfer", HFILL
}
2134 { &hf_http3_headers_access_control_allow_origin
,
2135 { "access-control-allow-origin", "http3.headers.access_control_allow_origin",
2136 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2137 "Origin control for cross-origin resource sharing", HFILL
}
2139 { &hf_http3_headers_age
,
2140 { "age", "http3.headers.age",
2141 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2142 "Time in seconds which was spent for transferring data through proxy", HFILL
}
2144 { &hf_http3_headers_allow
,
2145 { "allow", "http3.headers.allow",
2146 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2147 "List of allowed methods for request", HFILL
}
2149 { &hf_http3_headers_authorization
,
2150 { "authorization", "http3.headers.authorization",
2151 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2152 "Credentials for a server-side authorization", HFILL
}
2154 { &hf_http3_headers_cache_control
,
2155 { "cache-control", "http3.headers.cache_control",
2156 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2157 "Request or response directives for a cache control", HFILL
}
2159 { &hf_http3_headers_content_disposition
,
2160 { "content-disposition", "http3.headers.content_disposition",
2161 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2162 "Indicates that response will be displayed as page or downloaded with dialog box", HFILL
}
2164 { &hf_http3_headers_content_encoding
,
2165 { "content-encoding", "http3.headers.content_encoding",
2166 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2169 { &hf_http3_headers_content_language
,
2170 { "content-language", "http3.headers.content_language",
2171 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2174 { &hf_http3_headers_content_length
,
2175 { "content-length", "http3.headers.content_length",
2176 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2177 "Size of body in bytes", HFILL
}
2179 { &hf_http3_headers_content_location
,
2180 { "content-location", "http3.headers.content_location",
2181 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2182 "Alternative URL for a response data", HFILL
}
2184 { &hf_http3_headers_content_range
,
2185 { "content-range", "http3.headers.content_range",
2186 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2187 "Range of bytes which was sent by server for partial data transfer", HFILL
}
2189 { &hf_http3_headers_content_type
,
2190 { "content-type", "http3.headers.content_type",
2191 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2192 "MIME type of response", HFILL
}
2194 { &hf_http3_headers_cookie
,
2195 { "cookie", "http3.headers.cookie",
2196 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2197 "Stored cookies", HFILL
}
2199 { &hf_http3_headers_date
,
2200 { "date", "http3.headers.date",
2201 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2202 "Date and time at which the data was originated", HFILL
}
2204 { &hf_http3_headers_etag
,
2205 { "etag", "http3.headers.etag",
2206 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2207 "Directive for version indication of resource", HFILL
}
2209 { &hf_http3_headers_expect
,
2210 { "expect", "http3.headers.expect",
2211 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2212 "Expectations that need to be fulfilled for correct request", HFILL
}
2214 { &hf_http3_headers_expires
,
2215 { "expires", "http3.headers.expires",
2216 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2217 "Data after which resource will be stale", HFILL
}
2219 { &hf_http3_headers_from
,
2220 { "from", "http3.headers.from",
2221 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2222 "Email of a person who responsible for a requesting data", HFILL
}
2224 { &hf_http3_headers_if_match
,
2225 { "if-match", "http3.headers.if_match",
2226 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2227 "Mechanism for requesting data matched by a list of ETags", HFILL
}
2229 { &hf_http3_headers_if_modified_since
,
2230 { "if-modified-since", "http3.headers.if_modified_since",
2231 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2232 "Resource will be sent with status code 200 if it was modified otherwise with status code 304", HFILL
}
2234 { &hf_http3_headers_if_none_match
,
2235 { "if-none-match", "http3.headers.if_none_match",
2236 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2237 "Mechanism for requesting data not matched by a list of ETags", HFILL
}
2239 { &hf_http3_headers_if_range
,
2240 { "if-range", "http3.headers.if_range",
2241 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2242 "Mechanism for a range request which is used to check if a resource was modified", HFILL
}
2244 { &hf_http3_headers_if_unmodified_since
,
2245 { "if-unmodified-since", "http3.headers.if_unmodified_since",
2246 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2247 "Resource will be processed if it was not modified otherwise 412 error will be returned", HFILL
}
2249 { &hf_http3_headers_last_modified
,
2250 { "last-modified", "http3.headers.last_modified",
2251 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2252 "Date and time at which the origin server believes the resource was last modified", HFILL
}
2254 { &hf_http3_headers_link
,
2255 { "link", "http3.headers.link",
2256 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2257 "Mechanism for indicating that resource will be preloaded", HFILL
}
2259 { &hf_http3_headers_location
,
2260 { "location", "http3.headers.location",
2261 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2262 "Mechanism for indicating that client will be redirected", HFILL
}
2264 { &hf_http3_headers_max_forwards
,
2265 { "max-forwards", "http3.headers.max_forwards",
2266 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2267 "Mechanism for limiting the number of proxies", HFILL
}
2269 { &hf_http3_headers_proxy_authenticate
,
2270 { "proxy-authenticate", "http3.headers.proxy_authenticate",
2271 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2272 "Authentication method that should be used to gain access to a resource behind a proxy server", HFILL
}
2274 { &hf_http3_headers_proxy_authorization
,
2275 { "proxy-authorization", "http3.headers.proxy_authorization",
2276 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2277 "Credentials for a proxy-side authorization", HFILL
}
2279 { &hf_http3_headers_range
,
2280 { "range", "http3.headers.range",
2281 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2282 "Range of resource bytes that server should return", HFILL
}
2284 { &hf_http3_headers_referer
,
2285 { "referer", "http3.headers.referer",
2286 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2287 "Address of the previous web page", HFILL
}
2289 { &hf_http3_headers_refresh
,
2290 { "refresh", "http3.headers.refresh",
2291 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2292 "Time in seconds after which client will be redirected by given url", HFILL
}
2294 { &hf_http3_headers_retry_after
,
2295 { "retry-after", "http3.headers.retry_after",
2296 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2297 "Mechanism to indicate when resource expected to be available", HFILL
}
2299 { &hf_http3_headers_server
,
2300 { "server", "http3.headers.server",
2301 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2302 "Information about server software", HFILL
}
2304 { &hf_http3_headers_set_cookie
,
2305 { "set-cookie", "http3.headers.set_cookie",
2306 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2307 "Send a cookie to the client", HFILL
}
2309 { &hf_http3_headers_strict_transport_security
,
2310 { "strict-transport-security", "http3.headers.strict_transport_security",
2311 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2312 "HSTS indicates that resource should be accessed only using HTTPS", HFILL
}
2314 { &hf_http3_headers_user_agent
,
2315 { "user-agent", "http3.headers.user_agent",
2316 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2317 "Information about client software", HFILL
}
2319 { &hf_http3_headers_vary
,
2320 { "vary", "http3.headers.vary",
2321 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2322 "Mechanism for selecting which header will be used for content negotiation algorithm", HFILL
}
2324 { &hf_http3_headers_via
,
2325 { "via", "http3.headers.via",
2326 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2327 "Additional information for loop detection and protocol capabilities in proxy requests", HFILL
}
2329 { &hf_http3_headers_www_authenticate
,
2330 { "www-authenticate", "http3.headers.www_authenticate",
2331 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2332 "Authentication method that should be used to gain access to a resource", HFILL
}
2337 for (unsigned i
= 0; i
< G_N_ELEMENTS(hf
); ++i
) {
2338 header_name
= g_strdup(hf
[i
].hfinfo
.name
);
2340 g_hash_table_insert(header_fields_hash
, header_name
, &hf
[i
].hfinfo
.id
);
2342 proto_register_field_array(proto_http3
, hf
, G_N_ELEMENTS(hf
));
2344 #endif /* HAVE_NGHTTP3 */
2347 proto_register_http3(void)
2349 expert_module_t
* expert_http3
;
2350 module_t
*module_http3 _U_
;
2352 static hf_register_info hf
[] = {
2353 { &hf_http3_stream_uni
,
2354 { "Uni Stream", "http3.stream.uni",
2355 FT_NONE
, BASE_NONE
, NULL
, 0x0,
2358 { &hf_http3_stream_uni_type
,
2359 { "Uni Stream Type", "http3.stream_uni_type",
2360 FT_UINT64
, BASE_HEX
|BASE_VAL64_STRING
, VALS64(http3_stream_types
), 0x0,
2363 { &hf_http3_stream_bidi
,
2364 { "Request Stream", "http3.stream",
2365 FT_NONE
, BASE_NONE
, NULL
, 0x0,
2368 { &hf_http3_push_id
,
2369 { "Push ID", "http3.push_id",
2370 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2374 { "Frame", "http3.frame",
2375 FT_NONE
, BASE_NONE
, NULL
, 0x0,
2378 { &hf_http3_frame_type
,
2379 { "Type", "http3.frame_type",
2380 FT_UINT64
, BASE_HEX
|BASE_VAL64_STRING
, VALS64(http3_frame_types
), 0x0,
2381 "Frame Type", HFILL
}
2383 { &hf_http3_frame_length
,
2384 { "Length", "http3.frame_length",
2385 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2386 "Length of the Frame Payload", HFILL
}
2388 { &hf_http3_frame_payload
,
2389 { "Frame Payload", "http3.frame_payload",
2390 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2395 { "Data", "http3.data",
2396 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2400 //{ &hf_http3_headers,
2401 // { "Header", "http3.headers",
2402 // FT_UINT32, BASE_DEC, NULL, 0x0,
2405 { &hf_http3_headers_count
,
2406 { "Headers Count", "http3.headers.count",
2407 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2411 { "Header", "http3.headers.header",
2412 FT_NONE
, BASE_NONE
, NULL
, 0x0,
2415 { &hf_http3_header_length
,
2416 { "Header Length", "http3.headers.header.length",
2417 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2420 { &hf_http3_header_name_length
,
2421 { "Name Length", "http3.headers.header.name.length",
2422 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2425 { &hf_http3_header_name
,
2426 { "Name", "http3.header.header.name",
2427 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2430 { &hf_http3_header_value_length
,
2431 { "Value Length", "http3.headers.header.value.length",
2432 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2435 { &hf_http3_header_value
,
2436 { "Value", "http3.headers.header.value",
2437 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2440 { &hf_http3_header_request_full_uri
,
2441 { "Full request URI", "http3.request.full_uri",
2442 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2443 "The full requested URI (including host name)", HFILL
}
2445 { &hf_http3_header_qpack_blocked
,
2446 { "HEADERS head-of-line-blocked on QPACK encoder stream", "http3.header.qpack.blocked",
2447 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
2450 { &hf_http3_header_qpack_blocked_stream_rcint
,
2451 { "Required instruction count", "http3.header.qpack.blocked.rcint",
2452 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2455 { &hf_http3_header_qpack_blocked_decoder_wicnt
,
2456 { "Available instruction count", "http3.header.qpack.blocked.wcint",
2457 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2460 //{ &hf_http3_header_qpack_fatal,
2461 // { "QPACK decoding error", "http3.header.qpack.fatal",
2462 // FT_BOOLEAN, BASE_NONE, NULL, 0x0,
2465 //{ &hf_http3_qpack,
2466 // { "QPACK", "http3.qpack",
2467 // FT_BYTES, BASE_NONE, NULL, 0x0,
2470 { &hf_http3_qpack_encoder
,
2471 { "QPACK encoder", "http3.qpack.encoder",
2472 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2475 //{ &hf_http3_qpack_encoder_length,
2476 // { "QPACK encoder update length", "http3.qpack.encoder.length",
2477 // FT_UINT32, BASE_DEC, NULL, 0x0,
2480 { &hf_http3_qpack_encoder_icnt
,
2481 { "QPACK encoder instruction count", "http3.qpack.encoder.icnt",
2482 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2485 { &hf_http3_qpack_encoder_icnt_inc
,
2486 { "QPACK encoder instruction count increment", "http3.qpack.encoder.icnt.inc",
2487 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2490 //{ &hf_http3_qpack_encoder_opcode,
2491 // { "QPACK encoder opcode", "http3.qpack.encoder.opcode",
2492 // FT_BYTES, BASE_NONE, NULL, 0x0,
2495 { &hf_http3_qpack_encoder_opcode_insert_indexed
,
2496 { "Insert with Name Reference", "http3.qpack.encoder.opcode.insert_indexed",
2497 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2500 { &hf_http3_qpack_encoder_opcode_insert_indexed_ref
,
2501 { "Name Reference", "http3.qpack.encoder.opcode.insert_indexed.ref",
2502 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2505 { &hf_http3_qpack_encoder_opcode_insert_indexed_val
,
2506 { "Value", "http3.qpack.encoder.opcode.insert_indexed.val",
2507 FT_BYTES
, BASE_SHOW_ASCII_PRINTABLE
, NULL
, 0x0,
2510 { &hf_http3_qpack_encoder_opcode_insert_indexed_hval
,
2511 { "Value (Huffman)", "http3.qpack.encoder.opcode.insert_indexed.hval",
2512 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2515 { &hf_http3_qpack_encoder_opcode_insert
,
2516 { "Insert with Literal Name", "http3.qpack.encoder.opcode.insert",
2517 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2520 { &hf_http3_qpack_encoder_opcode_insert_name
,
2521 { "Literal Name", "http3.qpack.encoder.opcode.insert.name",
2522 FT_BYTES
, BASE_SHOW_ASCII_PRINTABLE
, NULL
, 0x0,
2525 { &hf_http3_qpack_encoder_opcode_insert_hname
,
2526 { "Literal Name (Huffman)", "http3.qpack.encoder.opcode.insert.hname",
2527 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2530 { &hf_http3_qpack_encoder_opcode_insert_val
,
2531 { "Value", "http3.qpack.encoder.opcode.insert.val",
2532 FT_BYTES
, BASE_SHOW_ASCII_PRINTABLE
, NULL
, 0x0,
2535 { &hf_http3_qpack_encoder_opcode_insert_hval
,
2536 { "Value (Huffman)", "http3.qpack.encoder.opcode.insert.hval",
2537 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2540 { &hf_http3_qpack_encoder_opcode_duplicate
,
2541 { "Duplicate", "http3.qpack.encoder.opcode.duplicate",
2542 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2545 //{ &hf_http3_qpack_encoder_opcode_duplicate_val,
2546 // { "Duplicate Index", "http3.qpack.encoder.opcode.duplicate.val",
2547 // FT_BYTES, BASE_NONE, NULL, 0x0,
2550 { &hf_http3_qpack_encoder_opcode_dtable_cap
,
2551 { "Set Dynamic Table Capacity", "http3.qpack.encoder.opcode.dtable_cap",
2552 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2555 { &hf_http3_qpack_encoder_opcode_dtable_cap_val
,
2556 { "Capacity", "http3.qpack.encoder.opcode.dtable_cap.val",
2557 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2562 { &hf_http3_settings
,
2563 { "Settings", "http3.settings",
2564 FT_NONE
, BASE_NONE
, NULL
, 0x0,
2567 { &hf_http3_settings_identifier
,
2568 { "Settings Identifier", "http3.settings.id",
2569 FT_UINT64
, BASE_HEX
|BASE_VAL64_STRING
, VALS64(http3_settings_vals
), 0x0,
2572 { &hf_http3_settings_value
,
2573 { "Settings Value", "http3.settings.value",
2574 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2577 { &hf_http3_settings_qpack_max_table_capacity
,
2578 { "Max Table Capacity", "http3.settings.qpack.max_table_capacity",
2579 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2582 { &hf_http3_settings_max_field_section_size
,
2583 { "Max header list size", "http3.settings.max_field_section_size",
2584 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2585 "The default value is unlimited.", HFILL
}
2587 { &hf_http3_settings_qpack_blocked_streams
,
2588 { "Blocked Streams", "http3.settings.qpack.blocked_streams",
2589 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2592 { &hf_http3_settings_extended_connect
,
2593 { "Extended CONNECT", "http3.settings.extended_connect",
2594 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2597 { &hf_http3_settings_webtransport
,
2598 { "WebTransport", "http3.settings.webtransport",
2599 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2602 { &hf_http3_settings_h3_datagram
,
2603 { "H3 DATAGRAM", "http3.settings.h3_datagram",
2604 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2607 { &hf_http3_settings_h3_datagram_draft04
,
2608 { "H3 DATAGRAM Draft04", "http3.settings.h3_datagram_draft04",
2609 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2613 /* Priority Update */
2614 { &hf_http3_priority_update_element_id
,
2615 { "Priority Update Element ID", "http3.priority_update_element_id",
2616 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2619 { &hf_http3_priority_update_field_value
,
2620 { "Priority Update Field Value", "http3.priority_update_field_value",
2621 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2626 static int *ett
[] = {&ett_http3
,
2627 &ett_http3_stream_uni
,
2628 &ett_http3_stream_bidi
,
2630 &ett_http3_settings
,
2632 &ett_http3_headers_qpack_blocked
,
2633 &ett_http3_qpack_update
,
2634 &ett_http3_qpack_opcode
};
2636 static ei_register_info ei
[] = {
2637 { &ei_http3_unknown_stream_type
,
2638 { "http3.unknown_stream_type", PI_UNDECODED
, PI_WARN
,
2639 "An unknown stream type was encountered", EXPFILL
}
2641 //{ &ei_http3_data_not_decoded,
2642 // { "http3.data_not_decoded", PI_UNDECODED, PI_WARN,
2643 // "Data not decoded", EXPFILL }
2645 // { &ei_http3_qpack_enc_update,
2646 // { "http3.qpack_enc_update", PI_UNDECODED, PI_WARN,
2647 // "Success decoding QPACK buffer", EXPFILL }
2649 { &ei_http3_qpack_failed
,
2650 { "http3.qpack_enc_failed", PI_UNDECODED
, PI_NOTE
,
2651 "Error decoding QPACK buffer", EXPFILL
}
2653 { &ei_http3_header_encoded_state
,
2654 { "http3.expert.header.encoded_state", PI_DEBUG
, PI_NOTE
,
2655 "HTTP3 header encoded block", EXPFILL
}
2657 { &ei_http3_header_decoding_failed
,
2658 { "http3.expert.header_decoding.failed", PI_UNDECODED
, PI_NOTE
,
2659 "Failed to decode HTTP3 header name/value", EXPFILL
}
2661 { &ei_http3_header_decoding_blocked
,
2662 { "http3.expert.header_decoding.blocked", PI_UNDECODED
, PI_NOTE
,
2663 "Failed to decode HTTP3 header name/value (blocked on QPACK)", EXPFILL
}
2665 { &ei_http3_header_decoding_no_output
,
2666 { "http3.expert.header_decoding.no_output", PI_UNDECODED
, PI_NOTE
,
2667 "Failed to decode HTTP3 header name/value (QPACK decoder no emission)", EXPFILL
}
2671 proto_http3
= proto_register_protocol("Hypertext Transfer Protocol Version 3", "HTTP3", "http3");
2673 proto_register_field_array(proto_http3
, hf
, array_length(hf
));
2674 proto_register_subtree_array(ett
, array_length(ett
));
2676 module_http3
= prefs_register_protocol(proto_http3
, NULL
);
2678 expert_http3
= expert_register_protocol(proto_http3
);
2679 expert_register_field_array(expert_http3
, ei
, array_length(ei
));
2681 http3_handle
= register_dissector("http3", dissect_http3
, proto_http3
);
2683 /* Fill hash table with static headers */
2684 register_static_headers();
2689 proto_reg_handoff_http3(void)
2691 dissector_add_string("quic.proto", "h3", http3_handle
);
2695 * Implementation of helper functions.
2697 static http3_file_local_ctx
*g_http3_file_local_ctx
;
2700 http3_conn_info_hash(const void *key
)
2702 uint8_t bkey
[QUIC_MAX_CID_LENGTH
];
2703 const quic_cid_t
*v
;
2707 v
= (const quic_cid_t
*)key
;
2708 memset(&bkey
[0], 0, QUIC_MAX_CID_LENGTH
);
2709 memcpy(&bkey
[0], &v
->cid
[0], MIN(v
->len
, QUIC_MAX_CID_LENGTH
));
2710 h
= wmem_strong_hash(&bkey
[0], QUIC_MAX_CID_LENGTH
);
2716 http3_conn_info_equal(const void *lhs
, const void *rhs
)
2718 const quic_cid_t
*a
= (const quic_cid_t
*)lhs
;
2719 const quic_cid_t
*b
= (const quic_cid_t
*)rhs
;
2720 size_t alen
= a
->len
;
2721 size_t blen
= b
->len
;
2723 return alen
== blen
&& memcmp(&a
->cid
[0], &b
->cid
[0], alen
) == 0;
2727 /* Due to QPACK compression, we may get lots of relatively large
2728 header decoded_header_fields (e.g., 4KiB). Allocating each of them requires lots
2729 of memory. The maximum compression is achieved in QPACK by
2730 referencing header field stored in dynamic table by one or two
2731 bytes. We reduce memory usage by caching header field in this
2732 wmem_map_t to reuse its memory region when we see the same header
2736 http3_hdrcache_length(const void *vv
)
2738 const uint8_t *v
= (const uint8_t *)vv
;
2739 uint32_t namelen
, valuelen
;
2741 namelen
= pntoh32(v
);
2742 valuelen
= pntoh32(v
+ sizeof(namelen
) + namelen
);
2744 return namelen
+ sizeof(namelen
) + valuelen
+ sizeof(valuelen
);
2748 http3_hdrcache_hash(const void *key
)
2750 return wmem_strong_hash((const uint8_t *)key
, http3_hdrcache_length(key
));
2754 http3_hdrcache_equal(const void *lhs
, const void *rhs
)
2756 const uint8_t *a
= (const uint8_t *)lhs
;
2757 const uint8_t *b
= (const uint8_t *)rhs
;
2758 size_t alen
= http3_hdrcache_length(a
);
2759 size_t blen
= http3_hdrcache_length(b
);
2761 return alen
== blen
&& memcmp(a
, b
, alen
) == 0;
2765 http3_hdrdefcache_length(const void *vv
)
2767 const uint8_t *v
= (const uint8_t *)vv
;
2770 namelen
= pntoh32(v
);
2772 return namelen
+ sizeof(namelen
);
2776 http3_hdrdefcache_hash(const void *key
)
2778 return wmem_strong_hash((const uint8_t *)key
, http3_hdrdefcache_length(key
));
2782 http3_hdrdefcache_equal(const void *lhs
, const void *rhs
)
2784 const uint8_t *a
= (const uint8_t *)lhs
;
2785 const uint8_t *b
= (const uint8_t *)rhs
;
2786 size_t alen
= http3_hdrdefcache_length(a
);
2787 size_t blen
= http3_hdrdefcache_length(b
);
2789 return alen
== blen
&& memcmp(a
, b
, alen
) == 0;
2793 /* Deallocation callback */
2795 http3_file_local_ctx_del_cb(wmem_allocator_t
*allocator _U_
, wmem_cb_event_t event _U_
, void *user_data _U_
)
2797 g_http3_file_local_ctx
= NULL
;
2801 static http3_file_local_ctx
*
2802 http3_get_file_local_ctx(void)
2804 if (g_http3_file_local_ctx
== NULL
) {
2806 * The file-local context hasn't been initialized yet
2807 * for the current file.
2809 g_http3_file_local_ctx
= wmem_new(wmem_file_scope(), http3_file_local_ctx
);
2810 g_http3_file_local_ctx
->conn_info_map
=
2811 wmem_map_new(wmem_file_scope(), http3_conn_info_hash
, http3_conn_info_equal
);
2813 g_http3_file_local_ctx
->hdr_cache_map
=
2814 wmem_map_new(wmem_file_scope(), http3_hdrcache_hash
, http3_hdrcache_equal
);
2815 g_http3_file_local_ctx
->hdr_def_cache_map
=
2816 wmem_map_new(wmem_file_scope(), http3_hdrdefcache_hash
, http3_hdrdefcache_equal
);
2818 wmem_register_callback(wmem_file_scope(), http3_file_local_ctx_del_cb
, NULL
);
2821 return g_http3_file_local_ctx
;
2825 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2830 * indent-tabs-mode: nil
2833 * vi: set shiftwidth=4 tabstop=8 expandtab:
2834 * :indentSize=4:tabSize=8:noTabs=true: