Coverage Report

Created: 2026-08-14 06:45

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