/* packet-gquic.c
 * Routines for (Google) Quick UDP Internet Connections dissection
 * Copyright 2013, Alexis La Goutte <alexis.lagoutte at gmail dot com>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/*
QUIC Wire Layout Specification : https://docs.google.com/document/d/1WJvyZflAO2pq77yOLbp9NsGjC1CHetAXV8I0fQe-B_U/

QUIC Crypto : https://docs.google.com/document/d/1g5nIXAIkN_Y-7XJW5K45IblHd_L2f5LTaDUDwvZ5L6g/

QUIC source code in Chromium : https://code.google.com/p/chromium/codesearch#chromium/src/net/quic/quic_utils.h&sq=package:chromium

*/
#include "config.h"

#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <epan/conversation.h>
#include "packet-http2.h"
#include "packet-quic.h"
#include <wsutil/strtoi.h>

void proto_register_gquic(void);
void proto_reg_handoff_gquic(void);

static dissector_handle_t gquic_handle;
static dissector_handle_t tls13_handshake_handle;
static dissector_handle_t quic_handle;

static int proto_gquic = -1;
static int hf_gquic_header_form = -1;
static int hf_gquic_fixed_bit = -1;
static int hf_gquic_long_packet_type = -1;
static int hf_gquic_long_reserved = -1;
static int hf_gquic_packet_number_length = -1;
static int hf_gquic_dcil = -1;
static int hf_gquic_scil = -1;
static int hf_gquic_puflags = -1;
static int hf_gquic_puflags_vrsn = -1;
static int hf_gquic_puflags_rst = -1;
static int hf_gquic_puflags_dnonce = -1;
static int hf_gquic_puflags_cid = -1;
static int hf_gquic_puflags_cid_old = -1;
static int hf_gquic_puflags_pkn = -1;
static int hf_gquic_puflags_mpth = -1;
static int hf_gquic_puflags_rsv = -1;
static int hf_gquic_cid = -1;
static int hf_gquic_version = -1;
static int hf_gquic_diversification_nonce = -1;
static int hf_gquic_packet_number = -1;
static int hf_gquic_prflags = -1;
static int hf_gquic_prflags_entropy = -1;
static int hf_gquic_prflags_fecg = -1;
static int hf_gquic_prflags_fec = -1;
static int hf_gquic_prflags_rsv = -1;
static int hf_gquic_message_authentication_hash = -1;
static int hf_gquic_frame = -1;
static int hf_gquic_frame_type = -1;
static int hf_gquic_frame_type_padding_length = -1;
static int hf_gquic_frame_type_padding = -1;
static int hf_gquic_frame_type_rsts_stream_id = -1;
static int hf_gquic_frame_type_rsts_byte_offset = -1;
static int hf_gquic_frame_type_rsts_error_code = -1;
static int hf_gquic_frame_type_cc_error_code = -1;
static int hf_gquic_frame_type_cc_reason_phrase_length = -1;
static int hf_gquic_frame_type_cc_reason_phrase = -1;
static int hf_gquic_frame_type_goaway_error_code = -1;
static int hf_gquic_frame_type_goaway_last_good_stream_id = -1;
static int hf_gquic_frame_type_goaway_reason_phrase_length = -1;
static int hf_gquic_frame_type_goaway_reason_phrase = -1;
static int hf_gquic_frame_type_wu_stream_id = -1;
static int hf_gquic_frame_type_wu_byte_offset = -1;
static int hf_gquic_frame_type_blocked_stream_id = -1;
static int hf_gquic_frame_type_sw_send_entropy = -1;
static int hf_gquic_frame_type_sw_least_unacked_delta = -1;
static int hf_gquic_crypto_offset = -1;
static int hf_gquic_crypto_length = -1;
static int hf_gquic_crypto_crypto_data = -1;
static int hf_gquic_frame_type_stream = -1;
static int hf_gquic_frame_type_stream_f = -1;
static int hf_gquic_frame_type_stream_d = -1;
static int hf_gquic_frame_type_stream_ooo = -1;
static int hf_gquic_frame_type_stream_ss = -1;
/* ACK */
static int hf_gquic_frame_type_ack = -1;
static int hf_gquic_frame_type_ack_n = -1;
static int hf_gquic_frame_type_ack_u = -1;
static int hf_gquic_frame_type_ack_t = -1;
static int hf_gquic_frame_type_ack_ll = -1;
static int hf_gquic_frame_type_ack_mm = -1;
/* ACK Before Q034 */
static int hf_gquic_frame_type_ack_received_entropy = -1;
static int hf_gquic_frame_type_ack_largest_observed = -1;
static int hf_gquic_frame_type_ack_ack_delay_time = -1;
static int hf_gquic_frame_type_ack_num_timestamp = -1;
static int hf_gquic_frame_type_ack_delta_largest_observed = -1;
static int hf_gquic_frame_type_ack_first_timestamp = -1;
static int hf_gquic_frame_type_ack_time_since_previous_timestamp = -1;
static int hf_gquic_frame_type_ack_num_ranges = -1;
static int hf_gquic_frame_type_ack_missing_packet = -1;
static int hf_gquic_frame_type_ack_range_length = -1;
static int hf_gquic_frame_type_ack_num_revived = -1;
static int hf_gquic_frame_type_ack_revived_packet = -1;
/* ACK After Q034 */
static int hf_gquic_frame_type_ack_largest_acked = -1;
static int hf_gquic_frame_type_ack_largest_acked_delta_time = -1;
static int hf_gquic_frame_type_ack_num_blocks = -1;
static int hf_gquic_frame_type_ack_first_ack_block_length = -1;
static int hf_gquic_frame_type_ack_gap_to_next_block = -1;
static int hf_gquic_frame_type_ack_ack_block_length = -1;
static int hf_gquic_frame_type_ack_delta_largest_acked = -1;
static int hf_gquic_frame_type_ack_time_since_largest_acked = -1;
static int hf_gquic_stream_id = -1;
static int hf_gquic_offset = -1;
static int hf_gquic_data_len = -1;
static int hf_gquic_tag = -1;
static int hf_gquic_tags = -1;
static int hf_gquic_tag_number = -1;
static int hf_gquic_tag_value = -1;
static int hf_gquic_tag_type = -1;
static int hf_gquic_tag_offset_end = -1;
static int hf_gquic_tag_length = -1;
static int hf_gquic_tag_sni = -1;
static int hf_gquic_tag_pad = -1;
static int hf_gquic_tag_ver = -1;
static int hf_gquic_tag_ccs = -1;
static int hf_gquic_tag_pdmd = -1;
static int hf_gquic_tag_uaid = -1;
static int hf_gquic_tag_stk = -1;
static int hf_gquic_tag_sno = -1;
static int hf_gquic_tag_prof = -1;
static int hf_gquic_tag_scfg = -1;
static int hf_gquic_tag_scfg_number = -1;
static int hf_gquic_tag_rrej = -1;
static int hf_gquic_tag_crt = -1;
static int hf_gquic_tag_aead = -1;
static int hf_gquic_tag_scid = -1;
static int hf_gquic_tag_pubs = -1;
static int hf_gquic_tag_kexs = -1;
static int hf_gquic_tag_obit = -1;
static int hf_gquic_tag_expy = -1;
static int hf_gquic_tag_nonc = -1;
static int hf_gquic_tag_mspc = -1;
static int hf_gquic_tag_tcid = -1;
static int hf_gquic_tag_srbf = -1;
static int hf_gquic_tag_icsl = -1;
static int hf_gquic_tag_scls = -1;
static int hf_gquic_tag_copt = -1;
static int hf_gquic_tag_ccrt = -1;
static int hf_gquic_tag_irtt = -1;
static int hf_gquic_tag_cfcw = -1;
static int hf_gquic_tag_sfcw = -1;
static int hf_gquic_tag_cetv = -1;
static int hf_gquic_tag_xlct = -1;
static int hf_gquic_tag_nonp = -1;
static int hf_gquic_tag_csct = -1;
static int hf_gquic_tag_ctim = -1;
static int hf_gquic_tag_mids = -1;
static int hf_gquic_tag_fhol = -1;
static int hf_gquic_tag_sttl = -1;
static int hf_gquic_tag_smhl = -1;
static int hf_gquic_tag_tbkp = -1;
static int hf_gquic_tag_mad0 = -1;
static int hf_gquic_tag_qlve = -1;
static int hf_gquic_tag_cgst = -1;
static int hf_gquic_tag_epid = -1;
static int hf_gquic_tag_srst = -1;

/* Public Reset Tags */
static int hf_gquic_tag_rnon = -1;
static int hf_gquic_tag_rseq = -1;
static int hf_gquic_tag_cadr_addr_type = -1;
static int hf_gquic_tag_cadr_addr_ipv4 = -1;
static int hf_gquic_tag_cadr_addr_ipv6 = -1;
static int hf_gquic_tag_cadr_addr = -1;
static int hf_gquic_tag_cadr_port = -1;

static int hf_gquic_tag_unknown = -1;

static int hf_gquic_padding = -1;
static int hf_gquic_stream_data = -1;
static int hf_gquic_payload = -1;

#define QUIC_PORT_RANGE "80,443"
static gboolean g_gquic_debug = FALSE;

static gint ett_gquic = -1;
static gint ett_gquic_puflags = -1;
static gint ett_gquic_prflags = -1;
static gint ett_gquic_ft = -1;
static gint ett_gquic_ftflags = -1;
static gint ett_gquic_tag_value = -1;

static expert_field ei_gquic_tag_undecoded = EI_INIT;
static expert_field ei_gquic_tag_length = EI_INIT;
static expert_field ei_gquic_tag_unknown = EI_INIT;
static expert_field ei_gquic_version_invalid = EI_INIT;
static expert_field ei_gquic_invalid_parameter = EI_INIT;
static expert_field ei_gquic_length_invalid = EI_INIT;
static expert_field ei_gquic_data_invalid = EI_INIT;

static const value_string gquic_short_long_header_vals[] = {
    { 0, "Short Header" },
    { 1, "Long Header" },
    { 0, NULL }
};
static const value_string gquic_long_packet_type_vals[] = {
    { 0, "Initial" },
    { 2, "Handshake" },
    { 1, "0-RTT" },
    { 0, NULL }
};
static const value_string gquic_packet_number_lengths[] = {
    { 0, "1 bytes" },
    { 1, "2 bytes" },
    { 2, "3 bytes" },
    { 3, "4 bytes" },
    { 0, NULL }
};
static const value_string quic_cid_lengths[] = {
    { 0, "0 bytes" },
    { 5, "8 bytes" },
    { 0, NULL }
};

#define GQUIC_MIN_LENGTH 3
#define GQUIC_MAGIC2 0x513032
#define GQUIC_MAGIC3 0x513033
#define GQUIC_MAGIC4 0x513034

#define GQUIC_VERSION_Q046 0x51303436

/**************************************************************************/
/*                      Public Flags                                      */
/**************************************************************************/
#define PUFLAGS_VRSN    0x01
#define PUFLAGS_RST     0x02
#define PUFLAGS_DNONCE  0x04
#define PUFLAGS_CID     0x08
#define PUFLAGS_CID_OLD 0x0C
#define PUFLAGS_PKN     0x30
#define PUFLAGS_MPTH    0x40
#define PUFLAGS_RSV     0x80

static const true_false_string puflags_cid_tfs = {
    "8 Bytes",
    "0 Byte"
};

static const value_string puflags_cid_old_vals[] = {
    { 0, "0 Byte" },
    { 1, "1 Bytes" },
    { 2, "4 Bytes" },
    { 3, "8 Bytes" },
    { 0, NULL }
};

static const value_string puflags_pkn_vals[] = {
    { 0, "1 Byte" },
    { 1, "2 Bytes" },
    { 2, "4 Bytes" },
    { 3, "6 Bytes" },
    { 0, NULL }
};

/**************************************************************************/
/*                      Private Flags                                     */
/**************************************************************************/
#define PRFLAGS_ENTROPY 0x01
#define PRFLAGS_FECG    0x02
#define PRFLAGS_FEC     0x04
#define PRFLAGS_RSV     0xF8


/**************************************************************************/
/*                      Frame Type Regular                                */
/**************************************************************************/
#define FT_PADDING          0x00
#define FT_RST_STREAM       0x01
#define FT_CONNECTION_CLOSE 0x02
#define FT_GOAWAY           0x03
#define FT_WINDOW_UPDATE    0x04
#define FT_BLOCKED          0x05
#define FT_STOP_WAITING     0x06
#define FT_PING             0x07
/* CRYPTO is not a real GQUIC frame, but a QUIC one. Since some GQUIC flows
 * have this kind of frame, try handling it like all the others */
#define FT_CRYPTO           0x08

/**************************************************************************/
/*                      Frame Type Special                                */
/**************************************************************************/
#define FTFLAGS_SPECIAL     0xE0

#define FTFLAGS_STREAM      0x80
#define FTFLAGS_STREAM_F    0x40
#define FTFLAGS_STREAM_D    0x20
#define FTFLAGS_STREAM_OOO  0x1C
#define FTFLAGS_STREAM_SS   0x03

#define FTFLAGS_ACK         0x40
#define FTFLAGS_ACK_N       0x20
#define FTFLAGS_ACK_U       0x10
#define FTFLAGS_ACK_T       0x10
#define FTFLAGS_ACK_LL      0x0C
#define FTFLAGS_ACK_MM      0x03

static const range_string frame_type_vals[] = {
  { 0,0,         "PADDING" },
  { 1,1,         "RST_STREAM" },
  { 2,2,         "CONNECTION_CLOSE" },
  { 3,3,         "GOAWAY" },
  { 4,4,         "WINDOW_UPDATE" },
  { 5,5,         "BLOCKED" },
  { 6,6,         "STOP_WAITING" },
  { 7,7,         "PING" },
  { 8,8,         "CRYPTO" },
  { 9,31,        "Unknown" },
  { 32,63,       "CONGESTION_FEEDBACK (Special Frame Type)" },
  { 64,127,      "ACK (Special Frame Type)" },
  { 128,256,     "STREAM (Special Frame Type)" },
  { 0,0, NULL }
};

static const value_string len_offset_vals[] = {
    { 0, "0 Byte" },
    { 1, "2 Bytes" },
    { 2, "3 Bytes" },
    { 3, "4 Bytes" },
    { 4, "5 Bytes" },
    { 5, "6 Bytes" },
    { 6, "7 Bytes" },
    { 7, "8 Bytes" },
    { 0, NULL }
};

static const value_string len_stream_vals[] = {
    { 0, "1 Byte" },
    { 1, "2 Bytes" },
    { 2, "3 Bytes" },
    { 3, "4 Bytes" },
    { 0, NULL }
};

static const true_false_string len_data_vals = {
    "2 Bytes",
    "0 Byte"
};

static const value_string len_largest_observed_vals[] = {
    { 0, "1 Byte" },
    { 1, "2 Bytes" },
    { 2, "4 Bytes" },
    { 3, "6 Bytes" },
    { 0, NULL }
};

static const value_string len_missing_packet_vals[] = {
    { 0, "1 Byte" },
    { 1, "2 Bytes" },
    { 2, "4 Bytes" },
    { 3, "6 Bytes" },
    { 0, NULL }
};


/**************************************************************************/
/*                      Message tag                                       */
/**************************************************************************/

#define MTAG_CHLO 0x43484C4F
#define MTAG_SHLO 0x53484C4F
#define MTAG_REJ  0x52454A00
#define MTAG_PRST 0x50525354

static const value_string message_tag_vals[] = {
    { MTAG_CHLO, "Client Hello" },
    { MTAG_SHLO, "Server Hello" },
    { MTAG_REJ, "Rejection" },
    { MTAG_PRST, "Public Reset" },
    { 0, NULL }
};

/**************************************************************************/
/*                      Tag                                               */
/**************************************************************************/
/* See https://chromium.googlesource.com/chromium/src.git/+/master/net/third_party/quic/core/crypto/crypto_protocol.h */

#define TAG_PAD  0x50414400
#define TAG_SNI  0x534E4900
#define TAG_VER  0x56455200
#define TAG_CCS  0x43435300
#define TAG_UAID 0x55414944
#define TAG_PDMD 0x50444d44
#define TAG_STK  0x53544b00
#define TAG_SNO  0x534E4F00
#define TAG_PROF 0x50524F46
#define TAG_SCFG 0x53434647
#define TAG_RREJ 0x5252454A
#define TAG_CRT  0x435254FF
#define TAG_AEAD 0x41454144
#define TAG_SCID 0x53434944
#define TAG_PUBS 0x50554253
#define TAG_KEXS 0x4B455853
#define TAG_OBIT 0x4F424954
#define TAG_EXPY 0x45585059
#define TAG_NONC 0x4E4F4E43
#define TAG_MSPC 0x4D535043
#define TAG_TCID 0x54434944
#define TAG_SRBF 0x53524246
#define TAG_ICSL 0x4943534C
#define TAG_SCLS 0x53434C53
#define TAG_COPT 0x434F5054
#define TAG_CCRT 0x43435254
#define TAG_IRTT 0x49525454
#define TAG_CFCW 0x43464357
#define TAG_SFCW 0x53464357
#define TAG_CETV 0x43455456
#define TAG_XLCT 0x584C4354
#define TAG_NONP 0x4E4F4E50
#define TAG_CSCT 0x43534354
#define TAG_CTIM 0x4354494D
#define TAG_MIDS 0x4D494453
#define TAG_FHOL 0x46484F4C
#define TAG_STTL 0x5354544C
#define TAG_SMHL 0x534D484C
#define TAG_TBKP 0x54424B50
#define TAG_MAD0 0x4d414400
#define TAG_QLVE 0x514C5645
#define TAG_CGST 0x43475354
#define TAG_EPID 0x45504944
#define TAG_SRST 0x53525354

/* Public Reset Tag */
#define TAG_RNON 0x524E4F4E
#define TAG_RSEQ 0x52534551
#define TAG_CADR 0x43414452

static const value_string tag_vals[] = {
    { TAG_PAD, "Padding" },
    { TAG_SNI, "Server Name Indication" },
    { TAG_VER, "Version" },
    { TAG_CCS, "Common Certificate Sets" },
    { TAG_UAID, "Client's User Agent ID" },
    { TAG_PDMD, "Proof Demand" },
    { TAG_STK, "Source Address Token" },
    { TAG_SNO, "Server nonce" },
    { TAG_PROF, "Proof (Signature)" },
    { TAG_SCFG, "Server Config" },
    { TAG_RREJ, "Reasons for server sending" },
    { TAG_CRT, "Certificate chain" },
    { TAG_AEAD, "Authenticated encryption algorithms" },
    { TAG_SCID, "Server config ID" },
    { TAG_PUBS, "Public value" },
    { TAG_KEXS, "Key exchange algorithms" },
    { TAG_OBIT, "Server Orbit" },
    { TAG_EXPY, "Expiry" },
    { TAG_NONC, "Client Nonce" },
    { TAG_MSPC, "Max streams per connection" },
    { TAG_TCID, "Connection ID truncation" },
    { TAG_SRBF, "Socket receive buffer" },
    { TAG_ICSL, "Idle connection state" },
    { TAG_SCLS, "Silently close on timeout" },
    { TAG_COPT, "Connection options" },
    { TAG_CCRT, "Cached certificates" },
    { TAG_IRTT, "Estimated initial RTT" },
    { TAG_CFCW, "Initial session/connection" },
    { TAG_SFCW, "Initial stream flow control" },
    { TAG_CETV, "Client encrypted tag-value" },
    { TAG_XLCT, "Expected leaf certificate" },
    { TAG_NONP, "Client Proof Nonce" },
    { TAG_CSCT, "Signed cert timestamp (RFC6962) of leaf cert" },
    { TAG_CTIM, "Client Timestamp" },
    { TAG_MIDS, "Max incoming dynamic streams" },
    { TAG_FHOL, "Force Head Of Line blocking" },
    { TAG_STTL, "Server Config TTL" },
    { TAG_SMHL, "Support Max Header List (size)" },
    { TAG_TBKP, "Token Binding Key Params" },
    { TAG_MAD0, "Max Ack Delay (IETF QUIC)" },
    { TAG_QLVE, "Legacy Version Encapsulation" },
    { TAG_CGST, "Congestion Control Feedback Type" },
    { TAG_EPID, "Endpoint Identifier" },
    { TAG_SRST, "Stateless Reset Token" },

    { TAG_RNON, "Public Reset Nonce Proof" },
    { TAG_RSEQ, "Rejected Packet Number" },
    { TAG_CADR, "Client Address" },
    { 0, NULL }
};


/**************************************************************************/
/*                      AEAD Tag                                          */
/**************************************************************************/

#define AEAD_AESG  0x41455347
#define AEAD_S20P  0x53323050
#define AEAD_CC12  0x43433132

static const value_string tag_aead_vals[] = {
    { AEAD_AESG, "AES-GCM with a 12-byte tag and IV" },
    { AEAD_S20P, "Salsa20 with Poly1305" },
    { AEAD_CC12, "Salsa20 with Poly1305" },
    { 0, NULL }
};

/**************************************************************************/
/*                      KEXS Tag                                          */
/**************************************************************************/

#define KEXS_C255  0x43323535
#define KEXS_P256  0x50323536

static const value_string tag_kexs_vals[] = {
    { KEXS_C255, "Curve25519" },
    { KEXS_P256, "P-256" },
    { 0, NULL }
};

/**************************************************************************/
/*                      Client Address Type                               */
/**************************************************************************/

static const value_string cadr_type_vals[] = {
    { 2, "IPv4" },
    { 10, "IPv6" },
    { 0, NULL }
};

/**************************************************************************/
/*                      Error Code                                        */
/**************************************************************************/
/* See https://chromium.googlesource.com/chromium/src.git/+/master/net/third_party/quic/core/quic_error_codes.h */

enum QuicErrorCode {
    QUIC_NO_ERROR = 0,
    /* Connection has reached an invalid state. */
    QUIC_INTERNAL_ERROR = 1,
    /* There were data frames after the a fin or reset. */
    QUIC_STREAM_DATA_AFTER_TERMINATION = 2,
    /* Control frame is malformed. */
    QUIC_INVALID_PACKET_HEADER = 3,
    /* Frame data is malformed. */
    QUIC_INVALID_FRAME_DATA = 4,
    /* The packet contained no payload. */
    QUIC_MISSING_PAYLOAD = 48,
    /* FEC data is malformed. */
    QUIC_INVALID_FEC_DATA = 5,
    /* STREAM frame data is malformed. */
    QUIC_INVALID_STREAM_DATA = 46,
    /* STREAM frame data overlaps with buffered data. */
    QUIC_OVERLAPPING_STREAM_DATA = 87,
    /* STREAM frame data is not encrypted. */
    QUIC_UNENCRYPTED_STREAM_DATA = 61,
    /* Attempt to send unencrypted STREAM frame. */
    QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA = 88,
    /* Received a frame which is likely the result of memory corruption. */
    QUIC_MAYBE_CORRUPTED_MEMORY = 89,
    /* FEC frame data is not encrypted. */
    QUIC_UNENCRYPTED_FEC_DATA = 77,
    /* RST_STREAM frame data is malformed. */
    QUIC_INVALID_RST_STREAM_DATA = 6,
    /* CONNECTION_CLOSE frame data is malformed. */
    QUIC_INVALID_CONNECTION_CLOSE_DATA = 7,
    /* GOAWAY frame data is malformed. */
    QUIC_INVALID_GOAWAY_DATA = 8,
    /* WINDOW_UPDATE frame data is malformed. */
    QUIC_INVALID_WINDOW_UPDATE_DATA = 57,
    /* BLOCKED frame data is malformed. */
    QUIC_INVALID_BLOCKED_DATA = 58,
    /* STOP_WAITING frame data is malformed. */
    QUIC_INVALID_STOP_WAITING_DATA = 60,
    /* PATH_CLOSE frame data is malformed. */
    QUIC_INVALID_PATH_CLOSE_DATA = 78,
    /* ACK frame data is malformed. */
    QUIC_INVALID_ACK_DATA = 9,
    /* deprecated: */
    QUIC_INVALID_CONGESTION_FEEDBACK_DATA = 47,
    /* Version negotiation packet is malformed. */
    QUIC_INVALID_VERSION_NEGOTIATION_PACKET = 10,
    /* Public RST packet is malformed. */
    QUIC_INVALID_PUBLIC_RST_PACKET = 11,
    /* There was an error decrypting. */
    QUIC_DECRYPTION_FAILURE = 12,
    /* There was an error encrypting. */
    QUIC_ENCRYPTION_FAILURE = 13,
    /* The packet exceeded kMaxPacketSize. */
    QUIC_PACKET_TOO_LARGE = 14,
    /* Data was sent for a stream which did not exist. */
    QUIC_PACKET_FOR_NONEXISTENT_STREAM = 15,
    /* The peer is going away.   May be a client or server. */
    QUIC_PEER_GOING_AWAY = 16,
    /* A stream ID was invalid. */
    QUIC_INVALID_STREAM_ID = 17,
    /* A priority was invalid. */
    QUIC_INVALID_PRIORITY = 49,
    /* Too many streams already open. */
    QUIC_TOO_MANY_OPEN_STREAMS = 18,
    /* The peer created too many available streams. */
    QUIC_TOO_MANY_AVAILABLE_STREAMS = 76,
    /* The peer must send a FIN/RST for each stream, and has not been doing so. */
    QUIC_TOO_MANY_UNFINISHED_STREAMS = 66,
    /* Received public reset for this connection. */
    QUIC_PUBLIC_RESET = 19,
    /* Invalid protocol version. */
    QUIC_INVALID_VERSION = 20,
    /* deprecated: */
    QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED = 21,
    /* The Header ID for a stream was too far from the previous. */
    QUIC_INVALID_HEADER_ID = 22,
    /* Negotiable parameter received during handshake had invalid value. */
    QUIC_INVALID_NEGOTIATED_VALUE = 23,
    /* There was an error decompressing data. */
    QUIC_DECOMPRESSION_FAILURE = 24,
    /* We hit our prenegotiated (or default) timeout */
    QUIC_CONNECTION_TIMED_OUT = 25,
    /* We hit our overall connection timeout */
    QUIC_CONNECTION_OVERALL_TIMED_OUT = 67,
    /* There was an error encountered migrating addresses */
    QUIC_ERROR_MIGRATING_ADDRESS = 26,
    /* There was an error encountered migrating port only. */
    QUIC_ERROR_MIGRATING_PORT = 86,
    /* There was an error while writing to the socket. */
    QUIC_PACKET_WRITE_ERROR = 27,
    /* There was an error while reading from the socket. */
    QUIC_PACKET_READ_ERROR = 51,
    /* We received a STREAM_FRAME with no data and no fin flag set. */
    QUIC_INVALID_STREAM_FRAME = 50,
    /* We received invalid data on the headers stream. */
    QUIC_INVALID_HEADERS_STREAM_DATA = 56,
    /* Invalid data on the headers stream received because of decompression failure. */
    QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE = 97,
    /* The peer received too much data, violating flow control. */
    QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA = 59,
    /* The peer sent too much data, violating flow control. */
    QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA = 63,
    /* The peer received an invalid flow control window. */
    QUIC_FLOW_CONTROL_INVALID_WINDOW = 64,
    /* The connection has been IP pooled into an existing connection. */
    QUIC_CONNECTION_IP_POOLED = 62,
    /* The connection has too many outstanding sent packets. */
    QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS = 68,
    /* The connection has too many outstanding received packets. */
    QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS = 69,
    /* The quic connection job to load server config is cancelled. */
    QUIC_CONNECTION_CANCELLED = 70,
    /* Disabled QUIC because of high packet loss rate. */
    QUIC_BAD_PACKET_LOSS_RATE = 71,
    /* Disabled QUIC because of too many PUBLIC_RESETs post handshake. */
    QUIC_PUBLIC_RESETS_POST_HANDSHAKE = 73,
    /* Disabled QUIC because of too many timeouts with streams open. */
    QUIC_TIMEOUTS_WITH_OPEN_STREAMS = 74,
    /* Closed because we failed to serialize a packet. */
    QUIC_FAILED_TO_SERIALIZE_PACKET = 75,
    /* QUIC timed out after too many RTOs. */
    QUIC_TOO_MANY_RTOS = 85,

    /* Crypto errors. */
    /* Handshake failed. */
    QUIC_HANDSHAKE_FAILED = 28,
    /* Handshake message contained out of order tags. */
    QUIC_CRYPTO_TAGS_OUT_OF_ORDER = 29,
    /* Handshake message contained too many entries. */
    QUIC_CRYPTO_TOO_MANY_ENTRIES = 30,
    /* Handshake message contained an invalid value length. */
    QUIC_CRYPTO_INVALID_VALUE_LENGTH = 31,
    /* A crypto message was received after the handshake was complete. */
    QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE = 32,
    /* A crypto message was received with an illegal message tag. */
    QUIC_INVALID_CRYPTO_MESSAGE_TYPE = 33,
    /* A crypto message was received with an illegal parameter. */
    QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER = 34,
    /* An invalid channel id signature was supplied. */
    QUIC_INVALID_CHANNEL_ID_SIGNATURE = 52,
    /* A crypto message was received with a mandatory parameter missing. */
    QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND = 35,
    /* A crypto message was received with a parameter that has no overlap
       with the local parameter. */
    QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP = 36,
    /* A crypto message was received that contained a parameter with too few
       values. */
    QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND = 37,
    /* A demand for an unsupport proof type was received. */
    QUIC_UNSUPPORTED_PROOF_DEMAND = 94,
    /* An internal error occurred in crypto processing. */
    QUIC_CRYPTO_INTERNAL_ERROR = 38,
    /* A crypto handshake message specified an unsupported version. */
    QUIC_CRYPTO_VERSION_NOT_SUPPORTED = 39,
    /* A crypto handshake message resulted in a stateless reject. */
    QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT = 72,
    /* There was no intersection between the crypto primitives supported by the
       peer and ourselves. */
    QUIC_CRYPTO_NO_SUPPORT = 40,
    /* The server rejected our client hello messages too many times. */
    QUIC_CRYPTO_TOO_MANY_REJECTS = 41,
    /* The client rejected the server's certificate chain or signature. */
    QUIC_PROOF_INVALID = 42,
    /* A crypto message was received with a duplicate tag. */
    QUIC_CRYPTO_DUPLICATE_TAG = 43,
    /* A crypto message was received with the wrong encryption level (i.e. it
       should have been encrypted but was not. ) */
    QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT = 44,
    /* The server config for a server has expired. */
    QUIC_CRYPTO_SERVER_CONFIG_EXPIRED = 45,
    /* We failed to setup the symmetric keys for a connection. */
    QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED = 53,
    /* A handshake message arrived, but we are still validating the
       previous handshake message. */
    QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO = 54,
    /* A server config update arrived before the handshake is complete. */
    QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE = 65,
    /* CHLO cannot fit in one packet. */
    QUIC_CRYPTO_CHLO_TOO_LARGE = 90,
    /* This connection involved a version negotiation which appears to have been
       tampered with. */
    QUIC_VERSION_NEGOTIATION_MISMATCH = 55,

    /* Multipath is not enabled, but a packet with multipath flag on is received. */
    QUIC_BAD_MULTIPATH_FLAG = 79,
    /* A path is supposed to exist but does not. */
    QUIC_MULTIPATH_PATH_DOES_NOT_EXIST = 91,
    /* A path is supposed to be active but is not. */
    QUIC_MULTIPATH_PATH_NOT_ACTIVE = 92,

    /* IP address changed causing connection close. */
    QUIC_IP_ADDRESS_CHANGED = 80,

    /* Connection migration errors. */
    /* Network changed, but connection had no migratable streams. */
    QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS = 81,
    /* Connection changed networks too many times. */
    QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES = 82,
    /* Connection migration was attempted, but there was no new network to migrate to. */
    QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK = 83,
    /* Network changed, but connection had one or more non-migratable streams. */
    QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM = 84,
    /* Network changed, but connection migration was disabled by config. */
    QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG = 99,
    /* Network changed, but error was encountered on the alternative network. */
    QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR = 100,

    /* Stream frames arrived too discontiguously so that stream sequencer buffer maintains too many gaps. */
    QUIC_TOO_MANY_FRAME_GAPS = 93,

    /* Sequencer buffer get into weird state where continuing read/write will lead
       to crash. */
    QUIC_STREAM_SEQUENCER_INVALID_STATE = 95,
    /* Connection closed because of server hits max number of sessions allowed. */
    QUIC_TOO_MANY_SESSIONS_ON_SERVER = 96,

    /* Receive a RST_STREAM with offset larger than kMaxStreamLength. */
    QUIC_STREAM_LENGTH_OVERFLOW = 98,

    /* No error. Used as bound while iterating. */
    QUIC_LAST_ERROR = 101
};


static const value_string error_code_vals[] = {
    { QUIC_NO_ERROR, "There was no error" },
    { QUIC_INTERNAL_ERROR, "Connection has reached an invalid state" },
    { QUIC_STREAM_DATA_AFTER_TERMINATION, "There were data frames after the a fin or reset" },
    { QUIC_INVALID_PACKET_HEADER, "Control frame is malformed" },
    { QUIC_INVALID_FRAME_DATA, "Frame data is malformed" },
    { QUIC_INVALID_FEC_DATA, "FEC data is malformed" },
    { QUIC_INVALID_RST_STREAM_DATA, "RST_STREAM frame data is malformed" },
    { QUIC_INVALID_CONNECTION_CLOSE_DATA, "CONNECTION_CLOSE frame data is malformed" },
    { QUIC_INVALID_GOAWAY_DATA, "GOAWAY frame data is malformed" },
    { QUIC_INVALID_ACK_DATA, "ACK frame data is malformed" },
    { QUIC_INVALID_VERSION_NEGOTIATION_PACKET, "Version negotiation packet is malformed" },
    { QUIC_INVALID_PUBLIC_RST_PACKET, "Public RST packet is malformed" },
    { QUIC_DECRYPTION_FAILURE, "There was an error decrypting" },
    { QUIC_ENCRYPTION_FAILURE, "There was an error encrypting" },
    { QUIC_PACKET_TOO_LARGE, "The packet exceeded kMaxPacketSize" },
    { QUIC_PACKET_FOR_NONEXISTENT_STREAM, "Data was sent for a stream which did not exist" },
    { QUIC_PEER_GOING_AWAY, "The peer is going away. May be a client or server" },
    { QUIC_INVALID_STREAM_ID, "A stream ID was invalid" },
    { QUIC_TOO_MANY_OPEN_STREAMS, "Too many streams already open" },
    { QUIC_PUBLIC_RESET, "Received public reset for this connection" },
    { QUIC_INVALID_VERSION, "Invalid protocol version" },
    { QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED, "Stream RST before Headers decompressed (Deprecated)" },
    { QUIC_INVALID_HEADER_ID, "The Header ID for a stream was too far from the previous" },
    { QUIC_INVALID_NEGOTIATED_VALUE, "Negotiable parameter received during handshake had invalid value" },
    { QUIC_DECOMPRESSION_FAILURE, "There was an error decompressing data" },
    { QUIC_CONNECTION_TIMED_OUT, "We hit our prenegotiated (or default) timeout" },
    { QUIC_ERROR_MIGRATING_ADDRESS, "There was an error encountered migrating addresses" },
    { QUIC_PACKET_WRITE_ERROR, "There was an error while writing to the socket" },
    { QUIC_HANDSHAKE_FAILED, "Handshake failed" },
    { QUIC_CRYPTO_TAGS_OUT_OF_ORDER, "Handshake message contained out of order tags" },
    { QUIC_CRYPTO_TOO_MANY_ENTRIES, "Handshake message contained too many entries" },
    { QUIC_CRYPTO_INVALID_VALUE_LENGTH, "Handshake message contained an invalid value length" },
    { QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, "A crypto message was received after the handshake was complete" },
    { QUIC_INVALID_CRYPTO_MESSAGE_TYPE, "A crypto message was received with an illegal message tag" },
    { QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "A crypto message was received with an illegal parameter" },
    { QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, "A crypto message was received with a mandatory parameter missing" },
    { QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, "A crypto message was received with a parameter that has no overlap with the local parameter" },
    { QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND, "A crypto message was received that contained a parameter with too few values" },
    { QUIC_CRYPTO_INTERNAL_ERROR, "An internal error occurred in crypto processing" },
    { QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "A crypto handshake message specified an unsupported version" },

    { QUIC_CRYPTO_NO_SUPPORT, "There was no intersection between the crypto primitives supported by the peer and ourselves" },
    { QUIC_CRYPTO_TOO_MANY_REJECTS, "The server rejected our client hello messages too many times" },
    { QUIC_PROOF_INVALID, "The client rejected the server's certificate chain or signature" },
    { QUIC_CRYPTO_DUPLICATE_TAG, "A crypto message was received with a duplicate tag" },
    { QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, "A crypto message was received with the wrong encryption level (i.e. it should have been encrypted but was not" },
    { QUIC_CRYPTO_SERVER_CONFIG_EXPIRED, "The server config for a server has expired" },
    { QUIC_INVALID_STREAM_DATA, "STREAM frame data is malformed" },
    { QUIC_INVALID_CONGESTION_FEEDBACK_DATA, "Invalid congestion Feedback data (Deprecated)" },
    { QUIC_MISSING_PAYLOAD, "The packet contained no payload" },
    { QUIC_INVALID_PRIORITY, "A priority was invalid" },
    { QUIC_INVALID_STREAM_FRAME, "We received a STREAM_FRAME with no data and no fin flag set" },
    { QUIC_PACKET_READ_ERROR, "There was an error while reading from the socket" },
    { QUIC_INVALID_CHANNEL_ID_SIGNATURE, "An invalid channel id signature was supplied" },
    { QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED, "We failed to setup the symmetric keys for a connection" },
    { QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO, "A handshake message arrived, but we are still validating the previous handshake message" },
    { QUIC_VERSION_NEGOTIATION_MISMATCH, "This connection involved a version negotiation which appears to have been tampered with" },
    { QUIC_INVALID_HEADERS_STREAM_DATA, "We received invalid data on the headers stream" },
    { QUIC_INVALID_WINDOW_UPDATE_DATA, "WINDOW_UPDATE frame data is malformed" },
    { QUIC_INVALID_BLOCKED_DATA, "BLOCKED frame data is malformed" },

    { QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, "The peer received too much data, violating flow control" },
    { QUIC_INVALID_STOP_WAITING_DATA, "STOP_WAITING frame data is malformed" },
    { QUIC_UNENCRYPTED_STREAM_DATA, "STREAM frame data is not encrypted" },
    { QUIC_CONNECTION_IP_POOLED, "The connection has been IP pooled into an existing connection" },
    { QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, "The peer sent too much data, violating flow control" },
    { QUIC_FLOW_CONTROL_INVALID_WINDOW, "The peer received an invalid flow control window" },
    { QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE, "A server config update arrived before the handshake is complete" },
    { QUIC_TOO_MANY_UNFINISHED_STREAMS, "The peer must send a FIN/RST for each stream, and has not been doing so" },
    { QUIC_CONNECTION_OVERALL_TIMED_OUT, "We hit our overall connection timeout" },
    { QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, "The connection has too many outstanding sent packets" },
    { QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS, "The connection has too many outstanding received packets" },
    { QUIC_CONNECTION_CANCELLED, "The quic connection job to load server config is cancelled" },
    { QUIC_BAD_PACKET_LOSS_RATE, "Disabled QUIC because of high packet loss rate" },
    { QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "A crypto handshake message resulted in a stateless reject" },
    { QUIC_PUBLIC_RESETS_POST_HANDSHAKE, "Disabled QUIC because of too many PUBLIC_RESETs post handshake" },
    { QUIC_TIMEOUTS_WITH_OPEN_STREAMS, "Disabled QUIC because of too many timeouts with streams open" },
    { QUIC_FAILED_TO_SERIALIZE_PACKET, "Closed because we failed to serialize a packet" },
    { QUIC_TOO_MANY_AVAILABLE_STREAMS, "The peer created too many available streams" },
    { QUIC_UNENCRYPTED_FEC_DATA, "FEC frame data is not encrypted" },
    { QUIC_INVALID_PATH_CLOSE_DATA, "PATH_CLOSE frame data is malformed" },
    { QUIC_BAD_MULTIPATH_FLAG, "Multipath is not enabled, but a packet with multipath flag on is received" },
    { QUIC_IP_ADDRESS_CHANGED, "IP address changed causing connection close" },
    { QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS, "Network changed, but connection had no migratable stream" },
    { QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES, "Connection changed networks too many times" },
    { QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK, "Connection migration was attempted, but there was no new network to migrate to" },
    { QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM, "Network changed, but connection had one or more non-migratable streams" },
    { QUIC_TOO_MANY_RTOS, "QUIC timed out after too many RTOs" },
    { QUIC_ERROR_MIGRATING_PORT, "There was an error encountered migrating port only" },
    { QUIC_OVERLAPPING_STREAM_DATA, "STREAM frame data overlaps with buffered data" },
    { QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA, "Attempt to send unencrypted STREAM frame" },
    { QUIC_MAYBE_CORRUPTED_MEMORY, "Received a frame which is likely the result of memory corruption" },
    { QUIC_CRYPTO_CHLO_TOO_LARGE, "CHLO cannot fit in one packet" },
    { QUIC_MULTIPATH_PATH_DOES_NOT_EXIST, "A path is supposed to exist but does not" },
    { QUIC_MULTIPATH_PATH_NOT_ACTIVE, "A path is supposed to be active but is not" },
    { QUIC_TOO_MANY_FRAME_GAPS, "Stream frames arrived too discontiguously so that stream sequencer buffer maintains too many gaps" },
    { QUIC_UNSUPPORTED_PROOF_DEMAND, "A demand for an unsupport proof type was received" },
    { QUIC_STREAM_SEQUENCER_INVALID_STATE, "Sequencer buffer get into weird state where continuing read/write will lead to crash" },
    { QUIC_TOO_MANY_SESSIONS_ON_SERVER, "Connection closed because of server hits max number of sessions allowed" },
    { QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE, "Invalid data on the headers stream received because of decompression failure" },
    { QUIC_STREAM_LENGTH_OVERFLOW, "Receive a RST_STREAM with offset larger than kMaxStreamLength" },
    { QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG, "Network changed, but connection migration was disabled by config" },
    { QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR, "Network changed, but error was encountered on the alternative network" },
    { QUIC_LAST_ERROR, "No error. Used as bound while iterating" },
    { 0, NULL }
};

static value_string_ext error_code_vals_ext = VALUE_STRING_EXT_INIT(error_code_vals);

/**************************************************************************/
/*                      RST Stream Error Code                             */
/**************************************************************************/
/* See https://chromium.googlesource.com/chromium/src.git/+/master/net/third_party/quic/core/quic_error_codes.h (enum QuicRstStreamErrorCode) */

enum QuicRstStreamErrorCode {
  /* Complete response has been sent, sending a RST to ask the other endpoint to stop sending request data without discarding the response. */

  QUIC_STREAM_NO_ERROR = 0,
  /* There was some error which halted stream processing.*/
  QUIC_ERROR_PROCESSING_STREAM,
  /* We got two fin or reset offsets which did not match.*/
  QUIC_MULTIPLE_TERMINATION_OFFSETS,
  /* We got bad payload and can not respond to it at the protocol level. */
  QUIC_BAD_APPLICATION_PAYLOAD,
  /* Stream closed due to connection error. No reset frame is sent when this happens. */
  QUIC_STREAM_CONNECTION_ERROR,
  /* GoAway frame sent. No more stream can be created. */
  QUIC_STREAM_PEER_GOING_AWAY,
  /* The stream has been cancelled. */
  QUIC_STREAM_CANCELLED,
  /* Closing stream locally, sending a RST to allow for proper flow control accounting. Sent in response to a RST from the peer. */
  QUIC_RST_ACKNOWLEDGEMENT,
  /* Receiver refused to create the stream (because its limit on open streams has been reached).  The sender should retry the request later (using another stream). */
  QUIC_REFUSED_STREAM,
  /* Invalid URL in PUSH_PROMISE request header. */
  QUIC_INVALID_PROMISE_URL,
  /* Server is not authoritative for this URL. */
  QUIC_UNAUTHORIZED_PROMISE_URL,
  /* Can't have more than one active PUSH_PROMISE per URL. */
  QUIC_DUPLICATE_PROMISE_URL,
  /* Vary check failed. */
  QUIC_PROMISE_VARY_MISMATCH,
  /* Only GET and HEAD methods allowed. */
  QUIC_INVALID_PROMISE_METHOD,
  /* The push stream is unclaimed and timed out. */
  QUIC_PUSH_STREAM_TIMED_OUT,
  /* Received headers were too large. */
  QUIC_HEADERS_TOO_LARGE,
  /* The data is not likely arrive in time. */
  QUIC_STREAM_TTL_EXPIRED,
  /* No error. Used as bound while iterating. */
  QUIC_STREAM_LAST_ERROR,
};

static const value_string rststream_error_code_vals[] = {
    { QUIC_STREAM_NO_ERROR, "Complete response has been sent, sending a RST to ask the other endpoint to stop sending request data without discarding the response." },
    { QUIC_ERROR_PROCESSING_STREAM, "There was some error which halted stream processing" },
    { QUIC_MULTIPLE_TERMINATION_OFFSETS, "We got two fin or reset offsets which did not match" },
    { QUIC_BAD_APPLICATION_PAYLOAD, "We got bad payload and can not respond to it at the protocol level" },
    { QUIC_STREAM_CONNECTION_ERROR, "Stream closed due to connection error. No reset frame is sent when this happens" },
    { QUIC_STREAM_PEER_GOING_AWAY, "GoAway frame sent. No more stream can be created" },
    { QUIC_STREAM_CANCELLED, "The stream has been cancelled" },
    { QUIC_RST_ACKNOWLEDGEMENT, "Closing stream locally, sending a RST to allow for proper flow control accounting. Sent in response to a RST from the peer" },
    { QUIC_REFUSED_STREAM, "Receiver refused to create the stream (because its limit on open streams has been reached). The sender should retry the request later (using another stream)" },
    { QUIC_INVALID_PROMISE_URL, "Invalid URL in PUSH_PROMISE request header" },
    { QUIC_UNAUTHORIZED_PROMISE_URL, "Server is not authoritative for this URL" },
    { QUIC_DUPLICATE_PROMISE_URL, "Can't have more than one active PUSH_PROMISE per URL" },
    { QUIC_PROMISE_VARY_MISMATCH, "Vary check failed" },
    { QUIC_INVALID_PROMISE_METHOD, "Only GET and HEAD methods allowed" },
    { QUIC_PUSH_STREAM_TIMED_OUT, "The push stream is unclaimed and timed out" },
    { QUIC_HEADERS_TOO_LARGE, "Received headers were too large" },
    { QUIC_STREAM_TTL_EXPIRED, "The data is not likely arrive in time" },
    { QUIC_STREAM_LAST_ERROR, "No error. Used as bound while iterating" },
    { 0, NULL }
};
static value_string_ext rststream_error_code_vals_ext = VALUE_STRING_EXT_INIT(rststream_error_code_vals);

/**************************************************************************/
/*                      Handshake Failure Reason                          */
/**************************************************************************/
/* See https://chromium.googlesource.com/chromium/src.git/+/master/net/third_party/quic/core/crypto/crypto_handshake.h */

enum HandshakeFailureReason {
    HANDSHAKE_OK = 0,

    /* Failure reasons for an invalid client nonce in CHLO. */

    /* The default error value for nonce verification failures from strike register (covers old strike registers and unknown failures). */
    CLIENT_NONCE_UNKNOWN_FAILURE = 1,
    /* Client nonce had incorrect length. */
    CLIENT_NONCE_INVALID_FAILURE = 2,
    /* Client nonce is not unique. */
    CLIENT_NONCE_NOT_UNIQUE_FAILURE = 3,
    /* Client orbit is invalid or incorrect. */
    CLIENT_NONCE_INVALID_ORBIT_FAILURE = 4,
    /* Client nonce's timestamp is not in the strike register's valid time range. */
    CLIENT_NONCE_INVALID_TIME_FAILURE = 5,
    /* Strike register's RPC call timed out, client nonce couldn't be verified. */
    CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT = 6,
    /* Strike register is down, client nonce couldn't be verified. */
    CLIENT_NONCE_STRIKE_REGISTER_FAILURE = 7,

    /* Failure reasons for an invalid server nonce in CHLO. */

    /* Unbox of server nonce failed. */
    SERVER_NONCE_DECRYPTION_FAILURE = 8,
    /* Decrypted server nonce had incorrect length. */
    SERVER_NONCE_INVALID_FAILURE = 9,
    /* Server nonce is not unique. */
    SERVER_NONCE_NOT_UNIQUE_FAILURE = 10,
    /* Server nonce's timestamp is not in the strike register's valid time range. */
    SERVER_NONCE_INVALID_TIME_FAILURE = 11,
    /* The server requires handshake confirmation. */
    SERVER_NONCE_REQUIRED_FAILURE = 20,

    /* Failure reasons for an invalid server config in CHLO. */

    /* Missing Server config id (kSCID) tag. */
    SERVER_CONFIG_INCHOATE_HELLO_FAILURE = 12,
    /* Couldn't find the Server config id (kSCID). */
    SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE = 13,

    /* Failure reasons for an invalid source-address token. */

    /* Missing Source-address token (kSourceAddressTokenTag) tag. */
    SOURCE_ADDRESS_TOKEN_INVALID_FAILURE = 14,
    /* Unbox of Source-address token failed. */
    SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE = 15,
    /* Couldn't parse the unbox'ed Source-address token. */
    SOURCE_ADDRESS_TOKEN_PARSE_FAILURE = 16,
    /* Source-address token is for a different IP address. */
    SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE = 17,
    /* The source-address token has a timestamp in the future. */
    SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE = 18,
    /* The source-address token has expired. */
    SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE = 19,

    /* The expected leaf certificate hash could not be validated. */
    INVALID_EXPECTED_LEAF_CERTIFICATE = 21,

    MAX_FAILURE_REASON = 22
};

static const value_string handshake_failure_reason_vals[] = {
    { HANDSHAKE_OK, "Handshake OK" },
    { CLIENT_NONCE_UNKNOWN_FAILURE, "The default error value for nonce verification failures from strike register (covers old strike registers and unknown failures)" },
    { CLIENT_NONCE_INVALID_FAILURE, "Client nonce had incorrect length" },
    { CLIENT_NONCE_NOT_UNIQUE_FAILURE, "Client nonce is not unique" },
    { CLIENT_NONCE_INVALID_ORBIT_FAILURE, "Client orbit is invalid or incorrect" },
    { CLIENT_NONCE_INVALID_TIME_FAILURE, "Client nonce's timestamp is not in the strike register's valid time range" },
    { CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT, "Strike register's RPC call timed out, client nonce couldn't be verified" },
    { CLIENT_NONCE_STRIKE_REGISTER_FAILURE, "Strike register is down, client nonce couldn't be verified" },
    { SERVER_NONCE_DECRYPTION_FAILURE, "Unbox of server nonce failed" },
    { SERVER_NONCE_INVALID_FAILURE, "Decrypted server nonce had incorrect length" },
    { SERVER_NONCE_NOT_UNIQUE_FAILURE, "Server nonce is not unique" },
    { SERVER_NONCE_INVALID_TIME_FAILURE, "Server nonce's timestamp is not in the strike register's valid time range" },
    { SERVER_CONFIG_INCHOATE_HELLO_FAILURE, "Missing Server config id (kSCID) tag" },
    { SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE, "Couldn't find the Server config id (kSCID)" },
    { SOURCE_ADDRESS_TOKEN_INVALID_FAILURE, "Missing Source-address token (kSourceAddressTokenTag) tag" },
    { SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, "Unbox of Source-address token failed" },
    { SOURCE_ADDRESS_TOKEN_PARSE_FAILURE, "Couldn't parse the unbox'ed Source-address token" },
    { SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, "Source-address token is for a different IP address" },
    { SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE, "The source-address token has a timestamp in the future" },
    { SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE, "The source-address token has expired" },
    { SERVER_NONCE_REQUIRED_FAILURE, "The server requires handshake confirmation" },
    { INVALID_EXPECTED_LEAF_CERTIFICATE, "The expected leaf certificate hash could not be validated" },
    { 0, NULL }
};
static value_string_ext handshake_failure_reason_vals_ext = VALUE_STRING_EXT_INIT(handshake_failure_reason_vals);


static guint32 get_len_offset(guint8 frame_type){

    switch((frame_type & FTFLAGS_STREAM_OOO) >> 2){
        case 0:
            return 0;
        break;
        case 1:
            return 2;
        break;
        case 2:
            return 3;
        break;
        case 3:
            return 4;
        break;
        case 4:
            return 5;
        break;
        case 5:
            return 6;
        break;
        case 6:
            return 7;
        break;
        case 7:
            return 8;
        break;
        default:
        break;
    }
    return 0;
}
static guint32 get_len_stream(guint8 frame_type){

    switch(frame_type & FTFLAGS_STREAM_SS){
        case 0:
            return 1;
        break;
        case 1:
            return 2;
        break;
        case 2:
            return 3;
        break;
        case 3:
            return 4;
        break;
        default:
        break;
    }
    return 1;
}

static guint32 get_len_largest_observed(guint8 frame_type){

    switch((frame_type & FTFLAGS_ACK_LL) >> 2){
        case 0:
            return 1;
        break;
        case 1:
            return 2;
        break;
        case 2:
            return 4;
        break;
        case 3:
            return 6;
        break;
        default:
        break;
    }
    return 1;
}
static guint32 get_len_missing_packet(guint8 frame_type){

    switch(frame_type & FTFLAGS_ACK_MM){
        case 0:
            return 1;
        break;
        case 1:
            return 2;
        break;
        case 2:
            return 4;
        break;
        case 3:
            return 6;
        break;
        default:
        break;
    }
    return 1;
}

static guint32 get_len_packet_number(guint8 puflags){

    switch((puflags & PUFLAGS_PKN) >> 4){
        case 0:
            return 1;
        break;
        case 1:
            return 2;
        break;
        case 2:
            return 4;
        break;
        case 3:
            return 6;
        break;
        default:
        break;
    }
    return 6;
}

static
gboolean is_gquic_unencrypt(tvbuff_t *tvb, packet_info *pinfo, guint offset, guint16 len_pkn, gquic_info_data_t *gquic_info){
    guint8 frame_type;
    guint8 num_ranges, num_revived, num_blocks = 0, num_timestamp;
    guint32 len_stream = 0, len_offset = 0, len_data = 0, len_largest_observed = 1, len_missing_packet = 1;
    guint32 message_tag;


    if(tvb_captured_length_remaining(tvb, offset) <= 13){
        return FALSE;
    }
    /* Message Authentication Hash */
    offset += 12;

    if(gquic_info->version_valid && gquic_info->version < 34){ /* No longer Private Flags after Q034 */
        /* Private Flags */
        offset += 1;
    }

    while(tvb_reported_length_remaining(tvb, offset) > 0){

        if (tvb_captured_length_remaining(tvb, offset) <= 1){
            return FALSE;
        }
        /* Frame type */
        frame_type = tvb_get_guint8(tvb, offset);
        if((frame_type & FTFLAGS_SPECIAL) == 0){
            offset += 1;
            switch(frame_type){
                case FT_PADDING:
                    return FALSE; /* Pad on rest of packet.. */
                break;
                case FT_RST_STREAM:
                    /* Stream ID */
                    offset += 4;
                    /* Byte Offset */
                    offset += 8;
                    /* Error Code */
                    offset += 4;
                break;
                case FT_CONNECTION_CLOSE:{
                    guint16 len_reason;

                    /* Error Code */
                    offset += 4;
                    /* Reason Phrase Length */
                    if (tvb_captured_length_remaining(tvb, offset) <= 2){
                        return FALSE;
                    }
                    len_reason = tvb_get_guint16(tvb, offset, gquic_info->encoding);
                    offset += 2;
                    /* Reason Phrase */
                    /* If length remaining == len_reason, it is Connection Close */
                    if (tvb_captured_length_remaining(tvb, offset) == len_reason){
                        return TRUE;
                    }
                    }
                break;
                case FT_GOAWAY:{
                    guint16 len_reason;

                    /* Error Code */
                    offset += 4;
                    /* Last Good Stream ID */
                    offset += 4;
                    /* Reason Phrase Length */
                    if (tvb_captured_length_remaining(tvb, offset) <= 2){
                        return FALSE;
                    }
                    len_reason = tvb_get_guint16(tvb, offset, gquic_info->encoding);
                    offset += 2;
                    /* Reason Phrase */
                    offset += len_reason;
                    }
                break;
                case FT_WINDOW_UPDATE:
                    /* Stream ID */
                    offset += 4;
                    /* Byte Offset */
                    offset += 8;
                break;
                case FT_BLOCKED:
                    /* Stream ID */
                    offset += 4;
                break;
                case FT_STOP_WAITING:
                    if(gquic_info->version_valid && gquic_info->version < 34){ /* No longer Entropy after Q034 */
                        /* Send Entropy */
                        offset += 1;
                    }
                    /* Least Unacked Delta */
                    offset += len_pkn;
                break;
                case FT_PING: /* No Payload */
                default: /* No default */
                break;
            }
        } else {
            /* Special Frame Type */
            if(frame_type & FTFLAGS_STREAM){ /* Stream */

                if(frame_type & FTFLAGS_STREAM_D){
                    len_data = 2;
                }
                len_offset = get_len_offset(frame_type);
                len_stream = get_len_stream(frame_type);

                /* Frame Type */
                offset += 1;

                /* Stream */
                offset += len_stream;

                /* Offset */
                offset += len_offset;

                /* Data length */
                offset += len_data;

                if (tvb_captured_length_remaining(tvb, offset) <= 4){
                    return FALSE;
                }

                /* Check if the Message Tag is CHLO (Client Hello) or SHLO (Server Hello) or REJ (Rejection) */
                message_tag = tvb_get_ntohl(tvb, offset);
                if (message_tag == MTAG_CHLO|| message_tag == MTAG_SHLO || message_tag == MTAG_REJ) {
                    if(message_tag == MTAG_CHLO && pinfo->srcport != 443) { /* Found */
                        gquic_info->server_port = pinfo->destport;
                    }
                    return TRUE;
                }


            } else if (frame_type & FTFLAGS_ACK) {
            /* ACK Flags */

                len_largest_observed = get_len_largest_observed(frame_type);
                len_missing_packet = get_len_missing_packet(frame_type);

                /* Frame Type */
                offset += 1;

                if(gquic_info->version_valid && gquic_info->version < 34){ /* No longer Entropy after Q034 */
                    /* Received Entropy */
                    offset += 1;

                    /* Largest Observed */
                    offset += len_largest_observed;

                    /* Ack Delay Time */
                    offset += 2;

                    /* Num Timestamp */
                    if (tvb_captured_length_remaining(tvb, offset) <= 1){
                        return FALSE;
                    }
                    num_timestamp = tvb_get_guint8(tvb, offset);
                    offset += 1;

                    if(num_timestamp > 0){
                        /* Delta Largest Observed */
                        offset += 1;

                        /* First Timestamp */
                        offset += 4;

                        /* Num Timestamp (-1)x (Delta Largest Observed + Time Since Previous Timestamp) */
                        offset += (num_timestamp - 1)*(1+2);
                    }

                    if(frame_type & FTFLAGS_ACK_N){
                        /* Num Ranges */
                        if (tvb_captured_length_remaining(tvb, offset) <= 1){
                            return FALSE;
                        }
                        num_ranges = tvb_get_guint8(tvb, offset);
                        offset += 1;

                        /* Num Range x (Missing Packet + Range Length) */
                        offset += num_ranges*(len_missing_packet+1);

                        /* Num Revived */
                        if (tvb_captured_length_remaining(tvb, offset) <= 1){
                            return FALSE;
                        }
                        num_revived = tvb_get_guint8(tvb, offset);
                        offset += 1;

                        /* Num Revived x Length Largest Observed */
                        offset += num_revived*len_largest_observed;

                    }
                } else {

                    /* Largest Acked */
                    offset += len_largest_observed;

                    /* Largest Acked Delta Time*/
                    offset += 2;

                    /* Ack Block */
                    if(frame_type & FTFLAGS_ACK_N){
                        if (tvb_captured_length_remaining(tvb, offset) <= 1){
                            return FALSE;
                        }
                        num_blocks = tvb_get_guint8(tvb, offset);
                        offset += 1;
                    }

                    /* First Ack Block Length */
                    offset += len_missing_packet;
                    if(num_blocks){
                        offset += (num_blocks)*(1 + len_missing_packet);
                    }

                    /* Timestamp */
                    if (tvb_captured_length_remaining(tvb, offset) <= 1){
                        return FALSE;
                    }
                    num_timestamp = tvb_get_guint8(tvb, offset);
                    offset += 1;

                    if(num_timestamp > 0){

                        /* Delta Largest Acked */
                        offset += 1;

                        /* Time Since Largest Acked */
                        offset += 4;

                        /* Num Timestamp x (Delta Largest Acked + Time Since Previous Timestamp) */
                        offset += (num_timestamp - 1)*(1+2);
                    }

                }
            } else { /* Other Special Frame type */
                offset += 1;
            }
        }
    }

    return FALSE;

}

static guint32
// NOLINTNEXTLINE(misc-no-recursion)
dissect_gquic_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, guint offset, guint32 tag_number){
    guint32 tag_offset_start = offset + tag_number*4*2;
    guint32 tag_offset = 0, total_tag_len = 0;
    gint32 tag_len;

    while(tag_number){
        proto_tree *tag_tree, *ti_len, *ti_tag, *ti_type;
        guint32 offset_end, tag, num_iter;
        const guint8* tag_str;

        ti_tag = proto_tree_add_item(gquic_tree, hf_gquic_tags, tvb, offset, 8, ENC_NA);
        tag_tree = proto_item_add_subtree(ti_tag, ett_gquic_tag_value);
        ti_type = proto_tree_add_item_ret_string(tag_tree, hf_gquic_tag_type, tvb, offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str);
        tag = tvb_get_ntohl(tvb, offset);
        proto_item_append_text(ti_type, " (%s)", val_to_str_const(tag, tag_vals, "Unknown"));
        proto_item_append_text(ti_tag, ": %s (%s)", tag_str, val_to_str_const(tag, tag_vals, "Unknown"));
        offset += 4;

        proto_tree_add_item(tag_tree, hf_gquic_tag_offset_end, tvb, offset, 4, ENC_LITTLE_ENDIAN);
        offset_end = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);

        tag_len = offset_end - tag_offset;
        ti_len = proto_tree_add_uint(tag_tree, hf_gquic_tag_length, tvb, offset, 4, tag_len);
        proto_item_append_text(ti_tag, " (l=%u)", tag_len);
        proto_item_set_generated(ti_len);
        offset += 4;

        /* Fix issue with CRT.. (Fragmentation ?) */
        if( tag_len > tvb_reported_length_remaining(tvb, tag_offset_start + tag_offset)){
            tag_len = tvb_reported_length_remaining(tvb, tag_offset_start + tag_offset);
            offset_end = tag_offset + tag_len;
            expert_add_info(pinfo, ti_len, &ei_gquic_tag_length);
        }

        total_tag_len += tag_len;

        proto_tree_add_item(tag_tree, hf_gquic_tag_value, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);

        increment_dissection_depth(pinfo);
        switch(tag){
            case TAG_PAD:
                proto_tree_add_item(tag_tree, hf_gquic_tag_pad, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_SNI:
                proto_tree_add_item_ret_string(tag_tree, hf_gquic_tag_sni, tvb, tag_offset_start + tag_offset, tag_len, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str);
                proto_item_append_text(ti_tag, ": %s", tag_str);
                tag_offset += tag_len;
            break;
            case TAG_VER:
                num_iter = 1;
                while(offset_end - tag_offset >= 4){
                    proto_tree_add_item_ret_string(tag_tree, hf_gquic_tag_ver, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str);
                    proto_item_append_text(ti_tag, "%s %s", num_iter == 1 ? ":" : ",", tag_str);
                    tag_offset += 4;
                    num_iter++;
                }
            break;
            case TAG_CCS:
                while(offset_end - tag_offset >= 8){
                    proto_tree_add_item(tag_tree, hf_gquic_tag_ccs, tvb, tag_offset_start + tag_offset, 8, ENC_NA);
                    tag_offset += 8;
                }
            break;
            case TAG_PDMD:
                proto_tree_add_item_ret_string(tag_tree, hf_gquic_tag_pdmd, tvb, tag_offset_start + tag_offset, tag_len, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str);
                proto_item_append_text(ti_tag, ": %s", tag_str);
                tag_offset += tag_len;
            break;
            case TAG_UAID:
                proto_tree_add_item_ret_string(tag_tree, hf_gquic_tag_uaid, tvb, tag_offset_start + tag_offset, tag_len, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str);
                proto_item_append_text(ti_tag, ": %s", tag_str);
                tag_offset += tag_len;
            break;
            case TAG_STK:
                proto_tree_add_item(tag_tree, hf_gquic_tag_stk, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_SNO:
                proto_tree_add_item(tag_tree, hf_gquic_tag_sno, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_PROF:
                proto_tree_add_item(tag_tree, hf_gquic_tag_prof, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_SCFG:{
                guint32 scfg_tag_number;

                proto_tree_add_item(tag_tree, hf_gquic_tag_scfg, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII);
                tag_offset += 4;
                proto_tree_add_item(tag_tree, hf_gquic_tag_scfg_number, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                scfg_tag_number = tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN);
                tag_offset += 4;

                dissect_gquic_tag(tvb, pinfo, tag_tree, tag_offset_start + tag_offset, scfg_tag_number);
                tag_offset += tag_len - 4 - 4;
                }
            break;
            case TAG_RREJ:
                while(offset_end - tag_offset >= 4){
                    proto_tree_add_item(tag_tree, hf_gquic_tag_rrej, tvb, tag_offset_start + tag_offset, 4,  ENC_LITTLE_ENDIAN);
                    proto_item_append_text(ti_tag, ", Code %s", val_to_str_ext_const(tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN),
                                                                                     &handshake_failure_reason_vals_ext,
                                                                                     "Unknown"));
                    tag_offset += 4;
                }
            break;
            case TAG_CRT:
                proto_tree_add_item(tag_tree, hf_gquic_tag_crt, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_AEAD:
                while(offset_end - tag_offset >= 4){
                    proto_tree *ti_aead;
                    ti_aead = proto_tree_add_item(tag_tree, hf_gquic_tag_aead, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII);
                    proto_item_append_text(ti_aead, " (%s)", val_to_str_const(tvb_get_ntohl(tvb, tag_offset_start + tag_offset), tag_aead_vals, "Unknown"));
                    proto_item_append_text(ti_tag, ", %s", val_to_str_const(tvb_get_ntohl(tvb, tag_offset_start + tag_offset), tag_aead_vals, "Unknown"));
                    tag_offset += 4;
                }
            break;
            case TAG_SCID:
                proto_tree_add_item(tag_tree, hf_gquic_tag_scid, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_PUBS:
                /*TODO FIX: 24 Length + Pubs key?.. ! */
                proto_tree_add_item(tag_tree, hf_gquic_tag_pubs, tvb, tag_offset_start + tag_offset, 2, ENC_LITTLE_ENDIAN);
                tag_offset += 2;
                while(offset_end - tag_offset >= 3){
                    proto_tree_add_item(tag_tree, hf_gquic_tag_pubs, tvb, tag_offset_start + tag_offset, 3, ENC_LITTLE_ENDIAN);
                    tag_offset += 3;
                }
            break;
            case TAG_KEXS:
                while(offset_end - tag_offset >= 4){
                    proto_tree *ti_kexs;
                    ti_kexs = proto_tree_add_item(tag_tree, hf_gquic_tag_kexs, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII);
                    proto_item_append_text(ti_kexs, " (%s)", val_to_str_const(tvb_get_ntohl(tvb, tag_offset_start + tag_offset), tag_kexs_vals, "Unknown"));
                    proto_item_append_text(ti_tag, ", %s", val_to_str_const(tvb_get_ntohl(tvb, tag_offset_start + tag_offset), tag_kexs_vals, "Unknown"));
                    tag_offset += 4;
                }
            break;
            case TAG_OBIT:
                proto_tree_add_item(tag_tree, hf_gquic_tag_obit, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_EXPY:
                proto_tree_add_item(tag_tree, hf_gquic_tag_expy, tvb, tag_offset_start + tag_offset, 8, ENC_LITTLE_ENDIAN);
                tag_offset += 8;
            break;
            case TAG_NONC:
                /*TODO: Enhance display: 32 bytes consisting of 4 bytes of timestamp (big-endian, UNIX epoch seconds), 8 bytes of server orbit and 20 bytes of random data. */
                proto_tree_add_item(tag_tree, hf_gquic_tag_nonc, tvb, tag_offset_start + tag_offset, 32, ENC_NA);
                tag_offset += 32;
            break;
            case TAG_MSPC:
                proto_tree_add_item(tag_tree, hf_gquic_tag_mspc, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                proto_item_append_text(ti_tag, ": %u", tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN));
                tag_offset += 4;
            break;
            case TAG_TCID:
                proto_tree_add_item(tag_tree, hf_gquic_tag_tcid, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                tag_offset += 4;
            break;
            case TAG_SRBF:
                proto_tree_add_item(tag_tree, hf_gquic_tag_srbf, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                tag_offset += 4;
            break;
            case TAG_ICSL:
                proto_tree_add_item(tag_tree, hf_gquic_tag_icsl, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                tag_offset += 4;
            break;
            case TAG_SCLS:
                proto_tree_add_item(tag_tree, hf_gquic_tag_scls, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                tag_offset += 4;
            break;
            case TAG_COPT:
                while(offset_end - tag_offset >= 4){
                    proto_tree_add_item(tag_tree, hf_gquic_tag_copt, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII);
                    tag_offset += 4;
                }
            break;
            case TAG_CCRT:
                proto_tree_add_item(tag_tree, hf_gquic_tag_ccrt, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_IRTT:
                proto_tree_add_item(tag_tree, hf_gquic_tag_irtt, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                proto_item_append_text(ti_tag, ": %u", tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN));
                tag_offset += 4;
            break;
            case TAG_CFCW:
                proto_tree_add_item(tag_tree, hf_gquic_tag_cfcw, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                proto_item_append_text(ti_tag, ": %u", tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN));
                tag_offset += 4;
            break;
            case TAG_SFCW:
                proto_tree_add_item(tag_tree, hf_gquic_tag_sfcw, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                proto_item_append_text(ti_tag, ": %u", tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN));
                tag_offset += 4;
            break;
            case TAG_CETV:
                proto_tree_add_item(tag_tree, hf_gquic_tag_cetv, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_XLCT:
                proto_tree_add_item(tag_tree, hf_gquic_tag_xlct, tvb, tag_offset_start + tag_offset, 8, ENC_NA);
                tag_offset += 8;
            break;
            case TAG_NONP:
                proto_tree_add_item(tag_tree, hf_gquic_tag_nonp, tvb, tag_offset_start + tag_offset, 32, ENC_NA);
                tag_offset += 32;
            break;
            case TAG_CSCT:
                proto_tree_add_item(tag_tree, hf_gquic_tag_csct, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_CTIM:
                proto_tree_add_item(tag_tree, hf_gquic_tag_ctim, tvb, tag_offset_start + tag_offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_SECS_NSECS);
                tag_offset += 8;
            break;
            case TAG_RNON: /* Public Reset Tag */
                proto_tree_add_item(tag_tree, hf_gquic_tag_rnon, tvb, tag_offset_start + tag_offset, 8, ENC_LITTLE_ENDIAN);
                tag_offset += 8;
            break;
            case TAG_RSEQ: /* Public Reset Tag */
                proto_tree_add_item(tag_tree, hf_gquic_tag_rseq, tvb, tag_offset_start + tag_offset, 8, ENC_LITTLE_ENDIAN);
                tag_offset += 8;
            break;
            case TAG_CADR: /* Public Reset Tag */{
                guint32 addr_type;
                proto_tree_add_item_ret_uint(tag_tree, hf_gquic_tag_cadr_addr_type, tvb, tag_offset_start + tag_offset, 2, ENC_LITTLE_ENDIAN, &addr_type);
                tag_offset += 2;
                switch(addr_type){
                    case 2: /* IPv4 */
                    proto_tree_add_item(tag_tree, hf_gquic_tag_cadr_addr_ipv4, tvb, tag_offset_start + tag_offset, 4, ENC_NA);
                    tag_offset += 4;
                    break;
                    case 10: /* IPv6 */
                    proto_tree_add_item(tag_tree, hf_gquic_tag_cadr_addr_ipv6, tvb, tag_offset_start + tag_offset, 16, ENC_NA);
                    tag_offset += 16;
                    break;
                    default: /* Unknown */
                    proto_tree_add_item(tag_tree, hf_gquic_tag_cadr_addr, tvb, tag_offset_start + tag_offset, tag_len - 2 - 2, ENC_NA);
                    tag_offset += tag_len + 2 + 2 ;
                    break;
                }
                proto_tree_add_item(tag_tree, hf_gquic_tag_cadr_port, tvb, tag_offset_start + tag_offset, 2, ENC_LITTLE_ENDIAN);
                tag_offset += 2;
            }
            break;
            case TAG_MIDS:
                proto_tree_add_item(tag_tree, hf_gquic_tag_mids, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                proto_item_append_text(ti_tag, ": %u", tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN));
                tag_offset += 4;
            break;
            case TAG_FHOL:
                proto_tree_add_item(tag_tree, hf_gquic_tag_fhol, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                proto_item_append_text(ti_tag, ": %u", tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN));
                tag_offset += 4;
            break;
            case TAG_STTL:
                proto_tree_add_item(tag_tree, hf_gquic_tag_sttl, tvb, tag_offset_start + tag_offset, 8, ENC_LITTLE_ENDIAN);
                tag_offset += 8;
            break;
            case TAG_SMHL:
                proto_tree_add_item(tag_tree, hf_gquic_tag_smhl, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                proto_item_append_text(ti_tag, ": %u", tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN));
                tag_offset += 4;
            break;
            case TAG_TBKP:
                proto_tree_add_item_ret_string(tag_tree, hf_gquic_tag_tbkp, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str);
                proto_item_append_text(ti_tag, ": %s", tag_str);
                tag_offset += 4;
            break;
            case TAG_MAD0:
                proto_tree_add_item(tag_tree, hf_gquic_tag_mad0, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN);
                proto_item_append_text(ti_tag, ": %u", tvb_get_guint32(tvb, tag_offset_start + tag_offset, ENC_LITTLE_ENDIAN));
                tag_offset += 4;
            break;
            case TAG_QLVE:
            {
                proto_tree_add_item(tag_tree, hf_gquic_tag_qlve, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);

                /* Newest GQUIC versions (usually Q050) encapsulate their first flight in Q043 packets.
		 * (Q050 is handled by QUIC dissector) */
                tvbuff_t *next_tvb = tvb_new_subset_length(tvb, tag_offset_start + tag_offset, tag_len);
                call_dissector_with_data(quic_handle, next_tvb, pinfo, tag_tree, NULL);

                tag_offset += tag_len;
            }
            break;
            case TAG_CGST:
                proto_tree_add_item(tag_tree, hf_gquic_tag_cgst, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            case TAG_EPID:
                proto_tree_add_item_ret_string(tag_tree, hf_gquic_tag_epid, tvb, tag_offset_start + tag_offset, tag_len, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str);
                proto_item_append_text(ti_tag, ": %s", tag_str);
                tag_offset += tag_len;
            break;
            case TAG_SRST:
                proto_tree_add_item(tag_tree, hf_gquic_tag_srst, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                tag_offset += tag_len;
            break;
            default:
                proto_tree_add_item(tag_tree, hf_gquic_tag_unknown, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA);
                expert_add_info_format(pinfo, ti_tag, &ei_gquic_tag_undecoded,
                                 "Dissector for (Google) QUIC Tag"
                                 " %s (%s) code not implemented, Contact"
                                 " Wireshark developers if you want this supported",
                                 tvb_get_string_enc(pinfo->pool, tvb, offset-8, 4, ENC_ASCII|ENC_NA),
                                 val_to_str_const(tag, tag_vals, "Unknown"));
                tag_offset += tag_len;
            break;
        }
        decrement_dissection_depth(pinfo);

        if(tag_offset != offset_end){
            /* Wrong Tag len... */
            proto_tree_add_expert(tag_tree, pinfo, &ei_gquic_tag_unknown, tvb, tag_offset_start + tag_offset, tag_len);
            tag_offset = offset_end;
        }

        tag_number--;
    }

    if (offset + total_tag_len <= offset) {
        expert_add_info_format(pinfo, gquic_tree, &ei_gquic_length_invalid,
                                "Invalid total tag length: %u", total_tag_len);
        return offset + tvb_reported_length_remaining(tvb, offset);
    }
    return offset + total_tag_len;

}

guint32
dissect_gquic_tags(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ft_tree, guint offset){
    guint32 tag_number;

    proto_tree_add_item(ft_tree, hf_gquic_tag_number, tvb, offset, 2, ENC_LITTLE_ENDIAN);
    tag_number = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
    offset += 2;

    proto_tree_add_item(ft_tree, hf_gquic_padding, tvb, offset, 2, ENC_NA);
    offset += 2;

    offset = dissect_gquic_tag(tvb, pinfo, ft_tree, offset, tag_number);

    return offset;
}

int
dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, guint offset, guint8 len_pkn, gquic_info_data_t *gquic_info){
    if (!gquic_info) {
        expert_add_info(pinfo, gquic_tree, &ei_gquic_data_invalid);
        return offset + tvb_reported_length_remaining(tvb, offset);
    }

    proto_item *ti, *ti_ft, *ti_ftflags /*, *expert_ti*/;
    proto_tree *ft_tree, *ftflags_tree;
    guint8 frame_type;
    guint8 num_ranges, num_revived, num_blocks = 0, num_timestamp;
    guint32 len_stream = 0, len_offset = 0, len_data = 0, len_largest_observed = 1, len_missing_packet = 1;

    ti_ft = proto_tree_add_item(gquic_tree, hf_gquic_frame, tvb, offset, 1, ENC_NA);
    ft_tree = proto_item_add_subtree(ti_ft, ett_gquic_ft);

    /* Frame type */
    ti_ftflags = proto_tree_add_item(ft_tree, hf_gquic_frame_type, tvb, offset, 1, ENC_NA);
    frame_type = tvb_get_guint8(tvb, offset);
    proto_item_set_text(ti_ft, "%s", rval_to_str_const(frame_type, frame_type_vals, "Unknown"));

    if((frame_type & FTFLAGS_SPECIAL) == 0 && frame_type != FT_CRYPTO){ /* Regular Stream Flags */
        offset += 1;
        switch(frame_type){
            case FT_PADDING:{
                proto_item *ti_pad_len;
                guint32 pad_len = tvb_reported_length_remaining(tvb, offset);

                ti_pad_len = proto_tree_add_uint(ft_tree, hf_gquic_frame_type_padding_length, tvb, offset, 0, pad_len);
                proto_item_set_generated(ti_pad_len);
                proto_item_append_text(ti_ft, " Length: %u", pad_len);
                if(pad_len > 0) /* Avoid Malformed Exception with pad_len == 0 */
		    proto_tree_add_item(ft_tree, hf_gquic_frame_type_padding, tvb, offset, -1, ENC_NA);
                offset += pad_len;
                }
            break;
            case FT_RST_STREAM:{
                guint32 stream_id, error_code;
                proto_tree_add_item_ret_uint(ft_tree, hf_gquic_frame_type_rsts_stream_id, tvb, offset, 4, gquic_info->encoding, &stream_id);
                offset += 4;
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_rsts_byte_offset, tvb, offset, 8, gquic_info->encoding);
                offset += 8;
                proto_tree_add_item_ret_uint(ft_tree, hf_gquic_frame_type_rsts_error_code, tvb, offset, 4, gquic_info->encoding, &error_code);
                offset += 4;
                proto_item_append_text(ti_ft, " Stream ID: %u, Error code: %s", stream_id, val_to_str_ext(error_code, &rststream_error_code_vals_ext, "Unknown (%d)"));
                col_set_str(pinfo->cinfo, COL_INFO, "RST STREAM");
                }
            break;
            case FT_CONNECTION_CLOSE:{
                guint16 len_reason;
                guint32 error_code;

                proto_tree_add_item_ret_uint(ft_tree, hf_gquic_frame_type_cc_error_code, tvb, offset, 4, gquic_info->encoding, &error_code);
                offset += 4;
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_cc_reason_phrase_length, tvb, offset, 2, gquic_info->encoding);
                len_reason = tvb_get_guint16(tvb, offset, gquic_info->encoding);
                offset += 2;
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_cc_reason_phrase, tvb, offset, len_reason, ENC_ASCII);
                offset += len_reason;
                proto_item_append_text(ti_ft, " Error code: %s", val_to_str_ext(error_code, &error_code_vals_ext, "Unknown (%d)"));
                col_set_str(pinfo->cinfo, COL_INFO, "Connection Close");
                }
            break;
            case FT_GOAWAY:{
                guint16 len_reason;
                guint32 error_code, last_good_stream_id;

                proto_tree_add_item_ret_uint(ft_tree, hf_gquic_frame_type_goaway_error_code, tvb, offset, 4, gquic_info->encoding, &error_code);
                offset += 4;
                proto_tree_add_item_ret_uint(ft_tree, hf_gquic_frame_type_goaway_last_good_stream_id, tvb, offset, 4, gquic_info->encoding, &last_good_stream_id);
                offset += 4;
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_goaway_reason_phrase_length, tvb, offset, 2, gquic_info->encoding);
                len_reason = tvb_get_guint16(tvb, offset, gquic_info->encoding);
                offset += 2;
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_goaway_reason_phrase, tvb, offset, len_reason, ENC_ASCII);
                offset += len_reason;
                proto_item_append_text(ti_ft, " Stream ID: %u, Error code: %s", last_good_stream_id, val_to_str_ext(error_code, &error_code_vals_ext, "Unknown (%d)"));
                col_set_str(pinfo->cinfo, COL_INFO, "GOAWAY");
                }
            break;
            case FT_WINDOW_UPDATE:{
                guint32 stream_id;

                proto_tree_add_item_ret_uint(ft_tree, hf_gquic_frame_type_wu_stream_id, tvb, offset, 4, gquic_info->encoding, &stream_id);
                offset += 4;
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_wu_byte_offset, tvb, offset, 8, gquic_info->encoding);
                offset += 8;
                proto_item_append_text(ti_ft, " Stream ID: %u", stream_id);
                }
            break;
            case FT_BLOCKED:{
                guint32 stream_id;

                proto_tree_add_item_ret_uint(ft_tree, hf_gquic_frame_type_blocked_stream_id, tvb, offset, 4, gquic_info->encoding, &stream_id);
                offset += 4;
                proto_item_append_text(ti_ft, " Stream ID: %u", stream_id);
                }
            break;
            case FT_STOP_WAITING:{
                guint8 send_entropy;
                if(gquic_info->version_valid && gquic_info->version < 34){ /* No longer Entropy after Q034 */
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_sw_send_entropy, tvb, offset, 1, ENC_NA);
                    send_entropy = tvb_get_guint8(tvb, offset);
                    proto_item_append_text(ti_ft, " Send Entropy: %u", send_entropy);
                    offset += 1;
                }
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_sw_least_unacked_delta, tvb, offset, len_pkn, gquic_info->encoding);
                offset += len_pkn;

                }
            break;
            case FT_PING: /* No Payload */
            default: /* No default */
            break;
        }
    }
    else { /* Special Frame Types */
        guint32 stream_id, message_tag;
        const guint8* message_tag_str;
        proto_item *ti_stream;

        ftflags_tree = proto_item_add_subtree(ti_ftflags, ett_gquic_ftflags);
        proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream , tvb, offset, 1, ENC_NA);

        if(frame_type == FT_CRYPTO) {
            guint64 crypto_offset, crypto_length;
            gint32 lenvar;

            DISSECTOR_ASSERT(gquic_info->version_valid && gquic_info->version >= 50);

            col_append_fstr(pinfo->cinfo, COL_INFO, ", CRYPTO");
            offset += 1;
            proto_tree_add_item_ret_varint(ft_tree, hf_gquic_crypto_offset, tvb, offset, -1, ENC_VARINT_QUIC, &crypto_offset, &lenvar);
            offset += lenvar;
            proto_tree_add_item_ret_varint(ft_tree, hf_gquic_crypto_length, tvb, offset, -1, ENC_VARINT_QUIC, &crypto_length, &lenvar);
            offset += lenvar;
            proto_tree_add_item(ft_tree, hf_gquic_crypto_crypto_data, tvb, offset, (guint32)crypto_length, ENC_NA);

            if (gquic_info->version == 50) {
	        message_tag = tvb_get_ntohl(tvb, offset);
                ti = proto_tree_add_item_ret_string(ft_tree, hf_gquic_tag, tvb, offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &message_tag_str);
                proto_item_append_text(ti, " (%s)", val_to_str_const(message_tag, message_tag_vals, "Unknown Tag"));
                col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const(message_tag, message_tag_vals, "Unknown"));
                offset += 4;

                offset = dissect_gquic_tags(tvb, pinfo, ft_tree, offset);
	    } else { /* T050 and T051 */
                tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, (int)crypto_length);
                col_set_writable(pinfo->cinfo, -1, FALSE);
                call_dissector_with_data(tls13_handshake_handle, next_tvb, pinfo, ft_tree, GUINT_TO_POINTER(crypto_offset));
                col_set_writable(pinfo->cinfo, -1, TRUE);
                offset += (guint32)crypto_length;
	    }

	} else if(frame_type & FTFLAGS_STREAM){ /* Stream Flags */
            proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream_f, tvb, offset, 1, ENC_NA);
            proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream_d, tvb, offset, 1, ENC_NA);
            if(frame_type & FTFLAGS_STREAM_D){
                len_data = 2;
            }
            proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream_ooo, tvb, offset, 1, ENC_NA);

            len_offset = get_len_offset(frame_type);

            proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream_ss, tvb, offset, 1, ENC_NA);
            len_stream = get_len_stream(frame_type);
            offset += 1;

            ti_stream = proto_tree_add_item_ret_uint(ft_tree, hf_gquic_stream_id, tvb, offset, len_stream, gquic_info->encoding, &stream_id);
            offset += len_stream;

            proto_item_append_text(ti_ft, " Stream ID: %u", stream_id);

            if(len_offset) {
                proto_tree_add_item(ft_tree, hf_gquic_offset, tvb, offset, len_offset, gquic_info->encoding);
                offset += len_offset;
            }

            if(len_data) {
                proto_tree_add_item(ft_tree, hf_gquic_data_len, tvb, offset, len_data, gquic_info->encoding);
                offset += len_data;
            }

            /* Check if there is some reserved streams (Chapiter 6.1 of draft-shade-gquic-http2-mapping-00) */

            switch(stream_id) {
                case 1: { /* Reserved (G)QUIC (handshake, crypto, config updates...) */
                    message_tag = tvb_get_ntohl(tvb, offset);
                    ti = proto_tree_add_item_ret_string(ft_tree, hf_gquic_tag, tvb, offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &message_tag_str);

                    proto_item_append_text(ti_stream, " (Reserved for (G)QUIC handshake, crypto, config updates...)");
                    proto_item_append_text(ti, " (%s)", val_to_str_const(message_tag, message_tag_vals, "Unknown Tag"));
                    proto_item_append_text(ti_ft, ", Type: %s (%s)", message_tag_str, val_to_str_const(message_tag, message_tag_vals, "Unknown Tag"));
                    col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const(message_tag, message_tag_vals, "Unknown"));
                    offset += 4;

                    offset = dissect_gquic_tags(tvb, pinfo, ft_tree, offset);
                break;
                }
                case 3: { /* Reserved H2 HEADERS (or PUSH_PROMISE..) */
                    tvbuff_t* tvb_h2;

                    proto_item_append_text(ti_stream, " (Reserved for H2 HEADERS)");

                    col_add_str(pinfo->cinfo, COL_INFO, "H2");

                    tvb_h2 = tvb_new_subset_remaining(tvb, offset);

                    offset += dissect_http2_pdu(tvb_h2, pinfo, ft_tree, NULL);
                }
                break;
                default: { /* Data... */
                    int data_len = tvb_reported_length_remaining(tvb, offset);

                    col_add_str(pinfo->cinfo, COL_INFO, "DATA");

                    proto_tree_add_item(ft_tree, hf_gquic_stream_data, tvb, offset, data_len, ENC_NA);
                    offset += data_len;
                }
                break;
            }
        } else if (frame_type & FTFLAGS_ACK) {     /* ACK Flags */

            proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_ack, tvb, offset, 1, ENC_NA);

            proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_ack_n, tvb, offset, 1, ENC_NA);

            if(gquic_info->version_valid && gquic_info->version < 34){ /* No longer NACK after Q034 */
                proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_ack_t, tvb, offset, 1, ENC_NA);
            } else {
                proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_ack_u, tvb, offset, 1, ENC_NA);
            }
            proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_ack_ll, tvb, offset, 1, ENC_NA);

            len_largest_observed = get_len_largest_observed(frame_type);

            proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_ack_mm, tvb, offset, 1, ENC_NA);
            len_missing_packet = get_len_missing_packet(frame_type);
            offset += 1;

            if(gquic_info->version_valid && gquic_info->version < 34){ /* Big change after Q034 */
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_received_entropy, tvb, offset, 1, ENC_NA);
                offset += 1;

                proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_largest_observed, tvb, offset, len_largest_observed, gquic_info->encoding);
                offset += len_largest_observed;

                proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_ack_delay_time, tvb, offset, 2, gquic_info->encoding);
                offset += 2;

                proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_num_timestamp, tvb, offset, 1, ENC_NA);
                num_timestamp = tvb_get_guint8(tvb, offset);
                offset += 1;

                if(num_timestamp){

                    /* Delta Largest Observed */
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_delta_largest_observed, tvb, offset, 1, ENC_NA);
                    offset += 1;

                    /* First Timestamp */
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_first_timestamp, tvb, offset, 4, gquic_info->encoding);
                    offset += 4;

                    num_timestamp -= 1;
                    /* Num Timestamp (-1) x (Delta Largest Observed + Time Since Previous Timestamp) */
                    while(num_timestamp){
                        proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_delta_largest_observed, tvb, offset, 1, ENC_NA);
                        offset += 1;

                        proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_time_since_previous_timestamp, tvb, offset, 2, gquic_info->encoding);
                        offset += 2;

                        num_timestamp--;
                    }
                }

                if(frame_type & FTFLAGS_ACK_N){
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_num_ranges, tvb, offset, 1, ENC_NA);
                    num_ranges = tvb_get_guint8(tvb, offset);
                    offset += 1;
                    while(num_ranges){

                        proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_missing_packet, tvb, offset, len_missing_packet, gquic_info->encoding);
                        offset += len_missing_packet;

                        proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_range_length, tvb, offset, 1, ENC_NA);
                        offset += 1;
                        num_ranges--;
                    }

                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_num_revived, tvb, offset, 1, ENC_NA);
                    num_revived = tvb_get_guint8(tvb, offset);
                    offset += 1;
                    while(num_revived){

                        proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_revived_packet, tvb, offset, len_largest_observed, gquic_info->encoding);
                        offset += len_largest_observed;
                        num_revived--;

                    }

                }

            } else {

                /* Largest Acked */
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_largest_acked, tvb, offset, len_largest_observed, gquic_info->encoding);
                offset += len_largest_observed;

                /* Largest Acked Delta Time*/
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_largest_acked_delta_time, tvb, offset, 2, gquic_info->encoding);
                offset += 2;

                /* Ack Block */
                if(frame_type & FTFLAGS_ACK_N){
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_num_blocks, tvb, offset, 1, ENC_NA);
                    num_blocks = tvb_get_guint8(tvb, offset);
                    offset += 1;
                }

                /* First Ack Block Length */
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_first_ack_block_length, tvb, offset, len_missing_packet, gquic_info->encoding);
                offset += len_missing_packet;

                while(num_blocks){
                    /* Gap to next block */
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_gap_to_next_block, tvb, offset, 1, ENC_NA);
                    offset += 1;

                    /* Ack Block Length */
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_ack_block_length, tvb, offset, len_missing_packet, gquic_info->encoding);
                    offset += len_missing_packet;

                    num_blocks--;
                }

                /* Timestamp */
                proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_num_timestamp, tvb, offset, 1, ENC_NA);
                num_timestamp = tvb_get_guint8(tvb, offset);
                offset += 1;

                if(num_timestamp){

                    /* Delta Largest Acked */
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_delta_largest_acked, tvb, offset, 1, ENC_NA);
                    offset += 1;

                    /* Time Since Largest Acked */
                    proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_time_since_largest_acked, tvb, offset, 4, gquic_info->encoding);
                    offset += 4;

                    num_timestamp -= 1;
                    /* Num Timestamp x (Delta Largest Acked + Time Since Previous Timestamp) */
                    while(num_timestamp){
                        proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_delta_largest_acked, tvb, offset, 1, ENC_NA);
                        offset += 1;

                        proto_tree_add_item(ft_tree, hf_gquic_frame_type_ack_time_since_previous_timestamp, tvb, offset, 2, gquic_info->encoding);
                        offset += 2;

                        num_timestamp--;
                    }
                }

            }

        } else { /* Other ...*/
            offset += 1;
        }
    }
    return offset;

}

static int
dissect_gquic_unencrypt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, guint offset, guint8 len_pkn, gquic_info_data_t *gquic_info){
    proto_item *ti_prflags;
    proto_tree *prflags_tree;

    /* Message Authentication Hash */
    proto_tree_add_item(gquic_tree, hf_gquic_message_authentication_hash, tvb, offset, 12, ENC_NA);
    offset += 12;

    if(gquic_info->version_valid && gquic_info->version < 34){ /* No longer Private Flags after Q034 */
        /* Private Flags */
        ti_prflags = proto_tree_add_item(gquic_tree, hf_gquic_prflags, tvb, offset, 1, ENC_NA);
        prflags_tree = proto_item_add_subtree(ti_prflags, ett_gquic_prflags);
        proto_tree_add_item(prflags_tree, hf_gquic_prflags_entropy, tvb, offset, 1, ENC_NA);
        proto_tree_add_item(prflags_tree, hf_gquic_prflags_fecg, tvb, offset, 1, ENC_NA);
        proto_tree_add_item(prflags_tree, hf_gquic_prflags_fec, tvb, offset, 1, ENC_NA);
        proto_tree_add_item(prflags_tree, hf_gquic_prflags_rsv, tvb, offset, 1, ENC_NA);
        offset += 1;
    }

    while(tvb_reported_length_remaining(tvb, offset) > 0){
        offset = dissect_gquic_frame_type(tvb, pinfo, gquic_tree, offset, len_pkn, gquic_info);
    }

    return offset;

}

static int
dissect_gquic_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        void *data _U_)
{
    proto_item *ti, *ti_puflags; /*, *expert_ti*/
    proto_tree *gquic_tree, *puflags_tree;
    guint offset = 0;
    guint8 puflags, len_cid = 0, len_pkn;
    guint64 cid = 0, pkn;
    conversation_t  *conv;
    gquic_info_data_t  *gquic_info;

    if (tvb_captured_length(tvb) < GQUIC_MIN_LENGTH)
        return 0;


    /* get conversation, create if necessary*/
    conv = find_or_create_conversation(pinfo);

    /* get associated state information, create if necessary */
    gquic_info = (gquic_info_data_t *)conversation_get_proto_data(conv, proto_gquic);

    if (!gquic_info) {
        gquic_info = wmem_new(wmem_file_scope(), gquic_info_data_t);
        gquic_info->version = 0;
        gquic_info->encoding = ENC_LITTLE_ENDIAN;
        gquic_info->version_valid = TRUE;
        gquic_info->server_port = 443;
        conversation_add_proto_data(conv, proto_gquic, gquic_info);
    }

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "GQUIC");

    ti = proto_tree_add_item(tree, proto_gquic, tvb, 0, -1, ENC_NA);
    gquic_tree = proto_item_add_subtree(ti, ett_gquic);

    /* Public Flags */
    puflags = tvb_get_guint8(tvb, offset);

    /* Get len of CID */
    if(puflags & PUFLAGS_CID){
        len_cid = 8;
    }
    /* check and get (and store) version */
    if(puflags & PUFLAGS_VRSN){
        gquic_info->version_valid = ws_strtou8(tvb_get_string_enc(pinfo->pool, tvb,
            offset + 1 + len_cid + 1, 3, ENC_ASCII), NULL, &gquic_info->version);
        if (!gquic_info->version_valid)
            expert_add_info(pinfo, gquic_tree, &ei_gquic_version_invalid);
    }

    if(gquic_info->version >= 39){ /* After Q039, Integers and floating numbers are written in big endian*/
        gquic_info->encoding = ENC_BIG_ENDIAN;
    }
    ti_puflags = proto_tree_add_item(gquic_tree, hf_gquic_puflags, tvb, offset, 1, ENC_NA);
    puflags_tree = proto_item_add_subtree(ti_puflags, ett_gquic_puflags);
    proto_tree_add_item(puflags_tree, hf_gquic_puflags_vrsn, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(puflags_tree, hf_gquic_puflags_rst, tvb, offset, 1, ENC_NA);
    if (gquic_info->version_valid) {
        if(gquic_info->version < 33){
            proto_tree_add_item(puflags_tree, hf_gquic_puflags_cid_old, tvb, offset, 1, ENC_NA);
        } else {
            proto_tree_add_item(puflags_tree, hf_gquic_puflags_dnonce, tvb, offset, 1, ENC_NA);
            proto_tree_add_item(puflags_tree, hf_gquic_puflags_cid, tvb, offset, 1, ENC_NA);
        }
    }
    proto_tree_add_item(puflags_tree, hf_gquic_puflags_pkn, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(puflags_tree, hf_gquic_puflags_mpth, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(puflags_tree, hf_gquic_puflags_rsv, tvb, offset, 1, ENC_NA);
    offset += 1;

    /* CID */
    if (len_cid) {
        cid = tvb_get_guint64(tvb, offset, gquic_info->encoding);
        proto_tree_add_item(gquic_tree, hf_gquic_cid, tvb, offset, len_cid, gquic_info->encoding);
        offset += len_cid;
    }

    /* Version */
    if(puflags & PUFLAGS_VRSN){
        if(pinfo->srcport == gquic_info->server_port){ /* Version Negotiation Packet */
            while(tvb_reported_length_remaining(tvb, offset) > 0){
                proto_tree_add_item(gquic_tree, hf_gquic_version, tvb, offset, 4, ENC_ASCII);
                offset += 4;
            }
            col_add_fstr(pinfo->cinfo, COL_INFO, "Version Negotiation, CID: %" PRIu64, cid);
            return offset;
        }
        else{
            proto_tree_add_item(gquic_tree, hf_gquic_version, tvb, offset, 4, ENC_ASCII);
            offset += 4;
        }
    }

    /* Public Reset Packet */
    if(puflags & PUFLAGS_RST){
        guint32 tag_number, message_tag;

        ti = proto_tree_add_item(gquic_tree, hf_gquic_tag, tvb, offset, 4, ENC_ASCII);
        message_tag = tvb_get_ntohl(tvb, offset);
        proto_item_append_text(ti, " (%s)", val_to_str_const(message_tag, message_tag_vals, "Unknown Tag"));
        offset += 4;

        proto_tree_add_item(gquic_tree, hf_gquic_tag_number, tvb, offset, 2, ENC_LITTLE_ENDIAN);
        tag_number = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
        offset += 2;

        proto_tree_add_item(gquic_tree, hf_gquic_padding, tvb, offset, 2, ENC_NA);
        offset += 2;

        offset = dissect_gquic_tag(tvb, pinfo, gquic_tree, offset, tag_number);

        col_add_fstr(pinfo->cinfo, COL_INFO, "Public Reset, CID: %" PRIu64, cid);

        return offset;
    }

    /* Diversification Nonce */
    if(gquic_info->version_valid && (puflags & PUFLAGS_DNONCE) && (gquic_info->version >= 33)){
        if(pinfo->srcport == gquic_info->server_port){ /* Diversification nonce is only present from server to client */
            proto_tree_add_item(gquic_tree, hf_gquic_diversification_nonce, tvb, offset, 32, ENC_NA);
            offset += 32;
        }
    }

    /* Packet Number */

    /* Get len of packet number */
    len_pkn = get_len_packet_number(puflags);
    proto_tree_add_item_ret_uint64(gquic_tree, hf_gquic_packet_number, tvb, offset, len_pkn, gquic_info->encoding, &pkn);
    offset += len_pkn;

    /* Unencrypt Message (Handshake or Connection Close...) */
    if (is_gquic_unencrypt(tvb, pinfo, offset, len_pkn, gquic_info) || g_gquic_debug){
        offset = dissect_gquic_unencrypt(tvb, pinfo, gquic_tree, offset, len_pkn, gquic_info);
    }else {     /* Payload... (encrypted... TODO FIX !) */
        col_add_str(pinfo->cinfo, COL_INFO, "Payload (Encrypted)");
        proto_tree_add_item(gquic_tree, hf_gquic_payload, tvb, offset, -1, ENC_NA);

    }

    col_append_fstr(pinfo->cinfo, COL_INFO, ", PKN: %" PRIu64, pkn);

    if(cid){
        col_append_fstr(pinfo->cinfo, COL_INFO, ", CID: %" PRIu64, cid);
    }


    return offset;
}

static int
dissect_gquic_q046(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        void *data _U_)
{
    proto_item *ti, *ti_firstbyte; /*, *expert_ti*/
    proto_tree *gquic_tree, *firstbyte_tree;
    guint offset = 0;
    guint8 first_byte, len_cid, cil, len_pkn;
    guint64 cid = 0, pkn = 0;
    conversation_t  *conv;
    gquic_info_data_t  *gquic_info;

    /* get conversation, create if necessary*/
    conv = find_or_create_conversation(pinfo);

    /* get associated state information, create if necessary */
    gquic_info = (gquic_info_data_t *)conversation_get_proto_data(conv, proto_gquic);

    if (!gquic_info) {
        gquic_info = wmem_new(wmem_file_scope(), gquic_info_data_t);
        gquic_info->version = 0;
        gquic_info->encoding = ENC_BIG_ENDIAN;
        gquic_info->version_valid = TRUE;
        gquic_info->server_port = 443;
        conversation_add_proto_data(conv, proto_gquic, gquic_info);
    }

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "GQUIC");

    ti = proto_tree_add_item(tree, proto_gquic, tvb, 0, -1, ENC_NA);
    gquic_tree = proto_item_add_subtree(ti, ett_gquic);

    /* First byte */
    first_byte = tvb_get_guint8(tvb, offset);
    len_pkn = (first_byte & 0x03) + 1;

    ti_firstbyte = proto_tree_add_item(gquic_tree, hf_gquic_puflags, tvb, offset, 1, ENC_NA);
    firstbyte_tree = proto_item_add_subtree(ti_firstbyte, ett_gquic_puflags);
    proto_tree_add_item(firstbyte_tree, hf_gquic_header_form, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(firstbyte_tree, hf_gquic_fixed_bit, tvb, offset, 1, ENC_NA);

    if((first_byte & PUFLAGS_MPTH) && (first_byte & PUFLAGS_RSV)) {
        /* Long Header. We handle only Q046 */

	gquic_info->version_valid = ws_strtou8(tvb_get_string_enc(pinfo->pool, tvb,
            offset + 2, 3, ENC_ASCII), NULL, &gquic_info->version);
        if (!gquic_info->version_valid) {
            expert_add_info(pinfo, gquic_tree, &ei_gquic_version_invalid);
        }

	cil = tvb_get_guint8(tvb, offset + 5);
	if(pinfo->srcport == gquic_info->server_port) { /* Server to client */
	    len_cid = (cil & 0x0F) + 3;
	} else {
	    len_cid = ((cil & 0xF0) >> 4) + 3;
	}
	if (len_cid != 8) {
            expert_add_info(pinfo, gquic_tree, &ei_gquic_invalid_parameter);
        }

        proto_tree_add_item(firstbyte_tree, hf_gquic_long_packet_type, tvb, offset, 1, ENC_NA);
        proto_tree_add_item(firstbyte_tree, hf_gquic_long_reserved, tvb, offset, 1, ENC_NA);
        proto_tree_add_item(firstbyte_tree, hf_gquic_packet_number_length, tvb, offset, 1, ENC_NA);
        offset += 1;

        proto_tree_add_item(gquic_tree, hf_gquic_version, tvb, offset, 4, ENC_ASCII);
        offset += 4;

        proto_tree_add_item(gquic_tree, hf_gquic_dcil, tvb, offset, 1, ENC_NA);
        proto_tree_add_item(gquic_tree, hf_gquic_scil, tvb, offset, 1, ENC_NA);
        offset += 1;

        /* CID */
        if (len_cid > 0) {
            cid = tvb_get_guint64(tvb, offset, gquic_info->encoding);
            proto_tree_add_item(gquic_tree, hf_gquic_cid, tvb, offset, len_cid, gquic_info->encoding);
        }
        offset += len_cid;

    } else {
        /* Short Header. We handle only Q046 */

        proto_tree_add_uint(firstbyte_tree, hf_gquic_packet_number_length, tvb, offset, 1, first_byte);

        offset += 1;

        if(pinfo->srcport == gquic_info->server_port) { /* Server to client */
            len_cid = 0;
        } else {
            len_cid = 8;
            cid = tvb_get_guint64(tvb, offset, gquic_info->encoding);
            proto_tree_add_item(gquic_tree, hf_gquic_cid, tvb, offset, len_cid, gquic_info->encoding);
        }
        offset += len_cid;
    }

    /* Packet Number */
    proto_tree_add_item_ret_uint64(gquic_tree, hf_gquic_packet_number, tvb, offset, len_pkn, gquic_info->encoding, &pkn);
    offset += len_pkn;

    /* Unencrypt Message (Handshake or Connection Close...) */
    if (is_gquic_unencrypt(tvb, pinfo, offset, len_pkn, gquic_info) || g_gquic_debug){
        offset = dissect_gquic_unencrypt(tvb, pinfo, gquic_tree, offset, len_pkn, gquic_info);
    }else {     /* Payload... (encrypted... TODO FIX !) */
        col_add_str(pinfo->cinfo, COL_INFO, "Payload (Encrypted)");
        proto_tree_add_item(gquic_tree, hf_gquic_payload, tvb, offset, -1, ENC_NA);

    }

    col_append_fstr(pinfo->cinfo, COL_INFO, ", PKN: %" PRIu64, pkn);

    if(cid){
        col_append_fstr(pinfo->cinfo, COL_INFO, ", CID: %" PRIu64, cid);
    }

    return offset;
}

static int
dissect_gquic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
              void *data _U_)
{
    guint8 flags;

    flags = tvb_get_guint8(tvb, 0);
    if((flags & PUFLAGS_RSV) == 0 && (flags & PUFLAGS_MPTH) == 0)
        return dissect_gquic_common(tvb, pinfo, tree, NULL);
    return dissect_gquic_q046(tvb, pinfo, tree, NULL);
}

static gboolean dissect_gquic_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{

    conversation_t *conversation = NULL;
    int offset = 0;
    guint8 flags;
    guint32 version;

    if (tvb_captured_length(tvb) < 1) {
        return FALSE;
    }
    flags = tvb_get_guint8(tvb, offset);
    offset += 1;

    if((flags & PUFLAGS_RSV) == 0 && (flags & PUFLAGS_MPTH) == 0) {
        /* It may be <= Q043 */

        /* Verify packet size  (Flag (1 byte) + Connection ID (8 bytes) + Version (4 bytes)) */
        if (tvb_captured_length(tvb) < 13) {
            return FALSE;
        }

        /* Check if flags version is set */
        if((flags & PUFLAGS_VRSN) == 0) {
            return FALSE;
        }

        /* Connection ID is always set to "long" (8bytes) too */
        if((flags & PUFLAGS_CID) == 0){
            return FALSE;
        }
        offset += 8;

        /* Check if version start with Q02... (0x51 0x30 0x32), Q03... (0x51 0x30 0x33) or Q04... (0x51 0x30 0x34) */
        version = tvb_get_ntoh24(tvb, offset);
        if ( version == GQUIC_MAGIC2 || version == GQUIC_MAGIC3 || version == GQUIC_MAGIC4) {
            conversation = find_or_create_conversation(pinfo);
            conversation_set_dissector(conversation, gquic_handle);
            dissect_gquic(tvb, pinfo, tree, data);
            return TRUE;
        }
    } else if((flags & PUFLAGS_MPTH) && (flags & PUFLAGS_RSV)) {
        /* It may be > Q043, Long Header. We handle only Q046 */

        /* Verify packet size  (Flag (1 byte) + Version (4) + DCIL/SCIL (1) + Dest Connection ID (8 bytes)) */
        if (tvb_captured_length(tvb) < 14) {
            return FALSE;
        }

        version = tvb_get_ntohl(tvb, offset);
        if (version != GQUIC_VERSION_Q046) {
            return FALSE;
        }

        conversation = find_or_create_conversation(pinfo);
        conversation_set_dissector(conversation, gquic_handle);
        dissect_gquic(tvb, pinfo, tree, data);
        return TRUE;
    }

    return FALSE;
}

void
proto_register_gquic(void)
{
    module_t *gquic_module;

    static hf_register_info hf[] = {
        /* Long/Short header for Q046 */
        { &hf_gquic_header_form,
          { "Header Form", "gquic.header_form",
            FT_UINT8, BASE_DEC, VALS(gquic_short_long_header_vals), 0x80,
            "The most significant bit (0x80) of the first octet is set to 1 for long headers and 0 for short headers.", HFILL }
        },
        { &hf_gquic_fixed_bit,
          { "Fixed Bit", "gquic.fixed_bit",
            FT_BOOLEAN, 8, NULL, 0x40,
            "Must be 1", HFILL }
        },
        { &hf_gquic_long_packet_type,
          { "Packet Type", "gquic.long.packet_type",
            FT_UINT8, BASE_DEC, VALS(gquic_long_packet_type_vals), 0x30,
            "Long Header Packet Type", HFILL }
        },
        { &hf_gquic_long_reserved,
          { "Reserved", "gquic.long.reserved",
            FT_UINT8, BASE_DEC, NULL, 0x0c,
            "Reserved bits", HFILL }
        },
        { &hf_gquic_packet_number_length,
          { "Packet Number Length", "gquic.packet_number_length",
            FT_UINT8, BASE_DEC, VALS(gquic_packet_number_lengths), 0x03,
            "Packet Number field length", HFILL }
	},
        { &hf_gquic_dcil,
          { "Destination Connection ID Length", "gquic.dcil",
            FT_UINT8, BASE_DEC, VALS(quic_cid_lengths), 0xF0,
            NULL, HFILL }
        },
        { &hf_gquic_scil,
          { "Source Connection ID Length", "gquic.scil",
            FT_UINT8, BASE_DEC, VALS(quic_cid_lengths), 0x0F,
            NULL, HFILL }
        },

        /* Public header for < Q046 */
        { &hf_gquic_puflags,
            { "Public Flags", "gquic.puflags",
              FT_UINT8, BASE_HEX, NULL, 0x0,
              "Specifying per-packet public flags", HFILL }
        },
        { &hf_gquic_puflags_vrsn,
            { "Version", "gquic.puflags.version",
              FT_BOOLEAN, 8, TFS(&tfs_yes_no), PUFLAGS_VRSN,
              "Signifies that this packet also contains the version of the (Google)QUIC protocol", HFILL }
        },
        { &hf_gquic_puflags_rst,
            { "Reset", "gquic.puflags.reset",
              FT_BOOLEAN, 8, TFS(&tfs_yes_no), PUFLAGS_RST,
              "Signifies that this packet is a public reset packet", HFILL }
        },
        { &hf_gquic_puflags_dnonce,
            { "Diversification nonce", "gquic.puflags.nonce",
              FT_BOOLEAN, 8, TFS(&tfs_yes_no), PUFLAGS_DNONCE,
              "Indicates the presence of a 32 byte diversification nonce", HFILL }
        },
        { &hf_gquic_puflags_cid,
            { "CID Length", "gquic.puflags.cid",
              FT_BOOLEAN, 8, TFS(&puflags_cid_tfs), PUFLAGS_CID,
              "Indicates the full 8 byte Connection ID is present", HFILL }
        },
        { &hf_gquic_puflags_cid_old,
            { "CID Length", "gquic.puflags.cid.old",
              FT_UINT8, BASE_HEX, VALS(puflags_cid_old_vals), PUFLAGS_CID_OLD,
              "Signifies the Length of CID", HFILL }
        },
        { &hf_gquic_puflags_pkn,
            { "Packet Number Length", "gquic.puflags.pkn",
              FT_UINT8, BASE_HEX, VALS(puflags_pkn_vals), PUFLAGS_PKN,
              "Signifies the Length of packet number", HFILL }
        },
        { &hf_gquic_puflags_mpth,
            { "Multipath", "gquic.puflags.mpth",
              FT_BOOLEAN, 8, TFS(&tfs_yes_no), PUFLAGS_MPTH,
              "Reserved for multipath use", HFILL }
        },
        { &hf_gquic_puflags_rsv,
            { "Reserved", "gquic.puflags.rsv",
              FT_UINT8, BASE_HEX, NULL, PUFLAGS_RSV,
              "Must be Zero", HFILL }
        },
        { &hf_gquic_cid,
            { "CID", "gquic.cid",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "Connection ID 64 bit pseudo random number", HFILL }
        },
        { &hf_gquic_version,
            { "Version", "gquic.version",
              FT_STRING, BASE_NONE, NULL, 0x0,
              "32 bit opaque tag that represents the version of the (Google)QUIC", HFILL }
        },
        { &hf_gquic_diversification_nonce,
            { "Diversification nonce", "gquic.diversification_nonce",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_packet_number,
            { "Packet Number", "gquic.packet_number",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "The lower 8, 16, 32, or 48 bits of the packet number", HFILL }
        },

        { &hf_gquic_prflags,
            { "Private Flags", "gquic.prflags",
              FT_UINT8, BASE_HEX, NULL, 0x0,
              "Specifying per-packet Private flags", HFILL }
        },

        { &hf_gquic_prflags_entropy,
            { "Entropy", "gquic.prflags.entropy",
              FT_BOOLEAN, 8, TFS(&tfs_yes_no), PRFLAGS_ENTROPY,
              "For data packets, signifies that this packet contains the 1 bit of entropy, for fec packets, contains the xor of the entropy of protected packets", HFILL }
        },
        { &hf_gquic_prflags_fecg,
            { "FEC Group", "gquic.prflags.fecg",
              FT_BOOLEAN, 8, TFS(&tfs_yes_no), PRFLAGS_FECG,
              "Indicates whether the fec byte is present.", HFILL }
        },
        { &hf_gquic_prflags_fec,
            { "FEC", "gquic.prflags.fec",
              FT_BOOLEAN, 8, TFS(&tfs_yes_no), PRFLAGS_FEC,
              "Signifies that this packet represents an FEC packet", HFILL }
        },
        { &hf_gquic_prflags_rsv,
            { "Reserved", "gquic.prflags.rsv",
              FT_UINT8, BASE_HEX, NULL, PRFLAGS_RSV,
              "Must be Zero", HFILL }
        },

        { &hf_gquic_message_authentication_hash,
            { "Message Authentication Hash", "gquic.message_authentication_hash",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              "The hash is an FNV1a-128 hash, serialized in little endian order", HFILL }
        },
        { &hf_gquic_frame,
            { "Frame", "gquic.frame",
              FT_NONE, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type,
            { "Frame Type", "gquic.frame_type",
              FT_UINT8 ,BASE_RANGE_STRING | BASE_HEX, RVALS(frame_type_vals), 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_padding_length,
            { "Padding Length", "gquic.frame_type.padding.length",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_padding,
            { "Padding", "gquic.frame_type.padding",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              "Must be zero", HFILL }
        },
        { &hf_gquic_frame_type_rsts_stream_id,
            { "Stream ID", "gquic.frame_type.rsts.stream_id",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              "Stream ID of the stream being terminated", HFILL }
        },
        { &hf_gquic_frame_type_rsts_byte_offset,
            { "Byte offset", "gquic.frame_type.rsts.byte_offset",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "Indicating the absolute byte offset of the end of data for this stream", HFILL }
        },
        { &hf_gquic_frame_type_rsts_error_code,
            { "Error code", "gquic.frame_type.rsts.error_code",
              FT_UINT32, BASE_DEC|BASE_EXT_STRING, &rststream_error_code_vals_ext, 0x0,
              "Indicates why the stream is being closed", HFILL }
        },
        { &hf_gquic_frame_type_cc_error_code,
            { "Error code", "gquic.frame_type.cc.error_code",
              FT_UINT32, BASE_DEC|BASE_EXT_STRING, &error_code_vals_ext, 0x0,
              "Indicates the reason for closing this connection", HFILL }
        },
        { &hf_gquic_frame_type_cc_reason_phrase_length,
            { "Reason phrase Length", "gquic.frame_type.cc.reason_phrase.length",
              FT_UINT16, BASE_DEC, NULL, 0x0,
              "Specifying the length of the reason phrase", HFILL }
        },
        { &hf_gquic_frame_type_cc_reason_phrase,
            { "Reason phrase", "gquic.frame_type.cc.reason_phrase",
              FT_STRING, BASE_NONE, NULL, 0x0,
              "An optional human-readable explanation for why the connection was closed", HFILL }
        },
        { &hf_gquic_frame_type_goaway_error_code,
            { "Error code", "gquic.frame_type.goaway.error_code",
              FT_UINT32, BASE_DEC|BASE_EXT_STRING, &error_code_vals_ext, 0x0,
              "Indicates the reason for closing this connection", HFILL }
        },
        { &hf_gquic_frame_type_goaway_last_good_stream_id,
            { "Last Good Stream ID", "gquic.frame_type.goaway.last_good_stream_id",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              "last Stream ID which was accepted by the sender of the GOAWAY message", HFILL }
        },
        { &hf_gquic_frame_type_goaway_reason_phrase_length,
            { "Reason phrase Length", "gquic.frame_type.goaway.reason_phrase.length",
              FT_UINT16, BASE_DEC, NULL, 0x0,
              "Specifying the length of the reason phrase", HFILL }
        },
        { &hf_gquic_frame_type_goaway_reason_phrase,
            { "Reason phrase", "gquic.frame_type.goaway.reason_phrase",
              FT_STRING, BASE_NONE, NULL, 0x0,
              "An optional human-readable explanation for why the connection was closed", HFILL }
        },
        { &hf_gquic_frame_type_wu_stream_id,
            { "Stream ID", "gquic.frame_type.wu.stream_id",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              "ID of the stream whose flow control windows is begin updated, or 0 to specify the connection-level flow control window", HFILL }
        },
        { &hf_gquic_frame_type_wu_byte_offset,
            { "Byte offset", "gquic.frame_type.wu.byte_offset",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "Indicating the absolute byte offset of data which can be sent on the given stream", HFILL }
        },
        { &hf_gquic_frame_type_blocked_stream_id,
            { "Stream ID", "gquic.frame_type.blocked.stream_id",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              "Indicating the stream which is flow control blocked", HFILL }
        },
        { &hf_gquic_frame_type_sw_send_entropy,
            { "Send Entropy", "gquic.frame_type.sw.send_entropy",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying the cumulative hash of entropy in all sent packets up to the packet with packet number one less than the least unacked packet", HFILL }
        },
        { &hf_gquic_frame_type_sw_least_unacked_delta,
            { "Least unacked delta", "gquic.frame_type.sw.least_unacked_delta",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "A variable length packet number delta with the same length as the packet header's packet number", HFILL }
        },
        { &hf_gquic_crypto_offset,
            { "Offset", "gquic.crypto.offset",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "Byte offset into the stream", HFILL }
        },
        { &hf_gquic_crypto_length,
            { "Length", "gquic.crypto.length",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "Length of the Crypto Data field", HFILL }
        },
        { &hf_gquic_crypto_crypto_data,
            { "Crypto Data", "gquic.crypto.crypto_data",
              FT_NONE, BASE_NONE, NULL, 0x0,
              "The cryptographic message data", HFILL }
        },
        { &hf_gquic_frame_type_stream,
            { "Stream", "gquic.frame_type.stream",
              FT_BOOLEAN, 8, NULL, FTFLAGS_STREAM,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_stream_f,
            { "FIN", "gquic.frame_type.stream.f",
              FT_BOOLEAN, 8, NULL, FTFLAGS_STREAM_F,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_stream_d,
            { "Data Length", "gquic.frame_type.stream.d",
              FT_BOOLEAN, 8, TFS(&len_data_vals), FTFLAGS_STREAM_D,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_stream_ooo,
            { "Offset Length", "gquic.frame_type.stream.ooo",
              FT_UINT8, BASE_DEC, VALS(len_offset_vals), FTFLAGS_STREAM_OOO,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_stream_ss,
            { "Stream Length", "gquic.frame_type.stream.ss",
              FT_UINT8, BASE_DEC, VALS(len_stream_vals), FTFLAGS_STREAM_SS,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_ack,
            { "ACK", "gquic.frame_type.ack",
              FT_BOOLEAN, 8, NULL, FTFLAGS_ACK,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_ack_n,
            { "NACK", "gquic.frame_type.ack.n",
              FT_BOOLEAN, 8, NULL, FTFLAGS_ACK_N,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_ack_u,
            { "Unused", "gquic.frame_type.ack.u",
              FT_BOOLEAN, 8, NULL, FTFLAGS_ACK_U,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_ack_t,
            { "Truncated", "gquic.frame_type.ack.t",
              FT_BOOLEAN, 8, NULL, FTFLAGS_ACK_T,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_ack_ll,
            { "Largest Observed Length", "gquic.frame_type.ack.ll",
              FT_UINT8, BASE_DEC, VALS(len_largest_observed_vals), FTFLAGS_ACK_LL,
              "Length of the Largest Observed field as 1, 2, 4, or 6 bytes long", HFILL }
        },
        { &hf_gquic_frame_type_ack_mm,
            { "Missing Packet Length", "gquic.frame_type.ack.mm",
              FT_UINT8, BASE_DEC, VALS(len_missing_packet_vals), FTFLAGS_ACK_MM,
              "Length of the Missing Packet Number Delta field as 1, 2, 4, or 6 bytes long", HFILL }
        },
        /* ACK before Q034 */
        { &hf_gquic_frame_type_ack_received_entropy,
            { "Received Entropy", "gquic.frame_type.ack.received_entropy",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying the cumulative hash of entropy in all received packets up to the largest observed packet", HFILL }
        },
        { &hf_gquic_frame_type_ack_largest_observed,
            { "Largest Observed", "gquic.frame_type.ack.largest_observed",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "Representing the largest packet number the peer has observed", HFILL }
        },
        { &hf_gquic_frame_type_ack_ack_delay_time,
            { "Ack Delay time", "gquic.frame_type.ack.ack_delay_time",
              FT_UINT16, BASE_DEC, NULL, 0x0,
              "Specifying the time elapsed in microseconds from when largest observed was received until this Ack frame was sent", HFILL }
        },
        { &hf_gquic_frame_type_ack_num_timestamp,
            { "Num Timestamp", "gquic.frame_type.ack.num_timestamp",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying the number of TCP timestamps that are included in this frame", HFILL }
        },
        { &hf_gquic_frame_type_ack_delta_largest_observed,
            { "Delta Largest Observed", "gquic.frame_type.ack.delta_largest_observed",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying the packet number delta from the first timestamp to the largest observed", HFILL }
        },
        { &hf_gquic_frame_type_ack_first_timestamp,
            { "First Timestamp", "gquic.frame_type.ack.first_timestamp",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              "Specifying the time delta in microseconds, from the beginning of the connection of the arrival of the packet specified by Largest Observed minus Delta Largest Observed", HFILL }
        },
        { &hf_gquic_frame_type_ack_time_since_previous_timestamp,
            { "Time since Previous timestamp", "gquic.frame_type.ack.time_since_previous_timestamp",
              FT_UINT16, BASE_DEC, NULL, 0x0,
              "This is the time delta from the previous timestamp", HFILL }
        },
        { &hf_gquic_frame_type_ack_num_ranges,
            { "Num Ranges", "gquic.frame_type.ack.num_ranges",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying the number of missing packet ranges between largest observed and least unacked", HFILL }
        },
        { &hf_gquic_frame_type_ack_missing_packet,
            { "Missing Packet Number Delta", "gquic.frame_type.ack.missing_packet",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_ack_range_length,
            { "Range Length", "gquic.frame_type.ack.range_length",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying one less than the number of sequential nacks in the range", HFILL }
        },
        { &hf_gquic_frame_type_ack_num_revived,
            { "Num Revived", "gquic.frame_type.ack.num_revived",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying the number of revived packets, recovered via FEC", HFILL }
        },
        { &hf_gquic_frame_type_ack_revived_packet,
            { "Revived Packet Number", "gquic.frame_type.ack.revived_packet",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "Representing a packet the peer has revived via FEC", HFILL }
        },
        /* ACK after Q034 */
        { &hf_gquic_frame_type_ack_largest_acked,
            { "Largest Acked", "gquic.frame_type.ack.largest_acked",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "Representing the largest packet number the peer has observed", HFILL }
        },
        { &hf_gquic_frame_type_ack_largest_acked_delta_time,
            { "Largest Acked Delta Time", "gquic.frame_type.ack.largest_acked_delta_time",
              FT_UINT16, BASE_DEC, NULL, 0x0,
              "Specifying the time elapsed in microseconds from when largest acked was received until this Ack frame was sent", HFILL }
        },
        { &hf_gquic_frame_type_ack_num_blocks,
            { "Num blocks", "gquic.frame_type.ack.num_blocks",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying one less than the number of ack blocks", HFILL }
        },
        { &hf_gquic_frame_type_ack_first_ack_block_length,
            { "First Ack block length", "gquic.frame_type.ack.first_ack_block_length",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_ack_gap_to_next_block,
            { "Gap to next block", "gquic.frame_type.ack.gap_to_next_block",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying the number of packets between ack blocks", HFILL }
        },
        { &hf_gquic_frame_type_ack_ack_block_length,
            { "Ack block length", "gquic.frame_type.ack.ack_block_length",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_frame_type_ack_delta_largest_acked,
            { "Delta Largest Observed", "gquic.frame_type.ack.delta_largest_acked",
              FT_UINT8, BASE_DEC, NULL, 0x0,
              "Specifying the packet number delta from the first timestamp to the largest observed", HFILL }
        },
        { &hf_gquic_frame_type_ack_time_since_largest_acked,
            { "Time Since Largest Acked", "gquic.frame_type.ack.time_since_largest_acked",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              "Specifying the time delta in microseconds, from the beginning of the connection of the arrival of the packet specified by Largest Observed minus Delta Largest Observed", HFILL }
        },



        { &hf_gquic_stream_id,
            { "Stream ID", "gquic.stream_id",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_offset,
            { "Offset", "gquic.offset",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_data_len,
            { "Data Length", "gquic.data_len",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag,
            { "Tag", "gquic.tag",
              FT_STRING, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_number,
            { "Tag Number", "gquic.tag_number",
              FT_UINT16, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tags,
            { "Tag/value", "gquic.tags",
              FT_NONE, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_type,
            { "Tag Type", "gquic.tag_type",
              FT_STRING, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_offset_end,
            { "Tag offset end", "gquic.tag_offset_end",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_length,
            { "Tag length", "gquic.tag_offset_length",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_value,
            { "Tag/value", "gquic.tag_value",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_sni,
            { "Server Name Indication", "gquic.tag.sni",
              FT_STRING, BASE_NONE, NULL, 0x0,
              "The fully qualified DNS name of the server, canonicalised to lowercase with no trailing period", HFILL }
        },
        { &hf_gquic_tag_pad,
            { "Padding", "gquic.tag.pad",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              "Pad.....", HFILL }
        },
        { &hf_gquic_tag_ver,
            { "Version", "gquic.tag.version",
              FT_STRING, BASE_NONE, NULL, 0x0,
              "Version of gquic supported", HFILL }
        },
        { &hf_gquic_tag_pdmd,
            { "Proof demand", "gquic.tag.pdmd",
              FT_STRING, BASE_NONE, NULL, 0x0,
              "a list of tags describing the types of proof acceptable to the client, in preference order", HFILL }
        },
        { &hf_gquic_tag_ccs,
            { "Common certificate sets", "gquic.tag.ccs",
              FT_UINT64, BASE_HEX, NULL, 0x0,
              "A series of 64-bit, FNV-1a hashes of sets of common certificates that the client possesses", HFILL }
        },
        { &hf_gquic_tag_uaid,
            { "Client's User Agent ID", "gquic.tag.uaid",
              FT_STRING, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_stk,
            { "Source-address token", "gquic.tag.stk",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_sno,
            { "Server nonce", "gquic.tag.sno",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_prof,
            { "Proof (Signature)", "gquic.tag.prof",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_scfg,
            { "Server Config Tag", "gquic.tag.scfg",
              FT_STRING, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_scfg_number,
            { "Number Server Config Tag", "gquic.tag.scfg.number",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_rrej,
            { "Reasons for server sending", "gquic.tag.rrej",
              FT_UINT32, BASE_DEC|BASE_EXT_STRING, &handshake_failure_reason_vals_ext, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_crt,
            { "Certificate chain", "gquic.tag.crt",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_aead,
            { "Authenticated encryption algorithms", "gquic.tag.aead",
              FT_STRING, BASE_NONE, NULL, 0x0,
              "A list of tags, in preference order, specifying the AEAD primitives supported by the server", HFILL }
        },
        { &hf_gquic_tag_scid,
            { "Server Config ID", "gquic.tag.scid",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              "An opaque, 16-byte identifier for this server config", HFILL }
        },
        { &hf_gquic_tag_pubs,
            { "Public value", "gquic.tag.pubs",
              FT_UINT24, BASE_DEC_HEX, NULL, 0x0,
              "A list of public values, 24-bit, little-endian length prefixed", HFILL }
        },
        { &hf_gquic_tag_kexs,
            { "Key exchange algorithms", "gquic.tag.kexs",
              FT_STRING, BASE_NONE, NULL, 0x0,
              "A list of tags, in preference order, specifying the key exchange algorithms that the server supports", HFILL }
        },
        { &hf_gquic_tag_obit,
            { "Server orbit", "gquic.tag.obit",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_expy,
            { "Expiry", "gquic.tag.expy",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "a 64-bit expiry time for the server config in UNIX epoch seconds", HFILL }
        },
        { &hf_gquic_tag_nonc,
            { "Client nonce", "gquic.tag.nonc",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              "32 bytes consisting of 4 bytes of timestamp (big-endian, UNIX epoch seconds), 8 bytes of server orbit and 20 bytes of random data", HFILL }
        },
        { &hf_gquic_tag_mspc,
            { "Max streams per connection", "gquic.tag.mspc",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_tcid,
            { "Connection ID truncation", "gquic.tag.tcid",
              FT_UINT32, BASE_DEC_HEX, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_srbf,
            { "Socket receive buffer", "gquic.tag.srbf",
              FT_UINT32, BASE_DEC_HEX, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_icsl,
            { "Idle connection state", "gquic.tag.icsl",
              FT_UINT32, BASE_DEC_HEX, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_scls,
            { "Silently close on timeout", "gquic.tag.scls",
              FT_UINT32, BASE_DEC_HEX, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_copt,
            { "Connection options", "gquic.tag.copt",
              FT_STRING, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_ccrt,
            { "Cached certificates", "gquic.tag.ccrt",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_irtt,
            { "Estimated initial RTT", "gquic.tag.irtt",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              "in us", HFILL }
        },
        { &hf_gquic_tag_cfcw,
            { "Initial session/connection", "gquic.tag.cfcw",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_sfcw,
            { "Initial stream flow control", "gquic.tag.sfcw",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_cetv,
            { "Client encrypted tag-value", "gquic.tag.cetv",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_xlct,
            { "Expected leaf certificate", "gquic.tag.xlct",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_nonp,
            { "Client Proof nonce", "gquic.tag.nonp",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_csct,
            { "Signed cert timestamp", "gquic.tag.csct",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_ctim,
            { "Client Timestamp", "gquic.tag.ctim",
              FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_rnon,
            { "Public reset nonce proof", "gquic.tag.rnon",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_rseq,
            { "Rejected Packet Number", "gquic.tag.rseq",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              "a 64-bit packet number", HFILL }
        },
        { &hf_gquic_tag_cadr_addr_type,
            { "Client IP Address Type", "gquic.tag.caddr.addr.type",
              FT_UINT16, BASE_DEC, VALS(cadr_type_vals), 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_cadr_addr_ipv4,
            { "Client IP Address", "gquic.tag.caddr.addr.ipv4",
              FT_IPv4, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_cadr_addr_ipv6,
            { "Client IP Address", "gquic.tag.caddr.addr.ipv6",
              FT_IPv6, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_cadr_addr,
            { "Client IP Address", "gquic.tag.caddr.addr",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_cadr_port,
            { "Client Port (Source)", "gquic.tag.caddr.port",
              FT_UINT16, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_mids,
            { "Max incoming dynamic streams", "gquic.tag.mids",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_fhol,
            { "Force Head Of Line blocking", "gquic.tag.fhol",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_sttl,
            { "Server Config TTL", "gquic.tag.sttl",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_smhl,
            { "Support Max Header List (size)", "gquic.tag.smhl",
              FT_UINT64, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_tbkp,
            { "Token Binding Key Params.", "gquic.tag.tbkp",
              FT_STRING, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_mad0,
            { "Max Ack Delay", "gquic.tag.mad0",
              FT_UINT32, BASE_DEC, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_qlve,
            { "Legacy Version Encapsulation", "gquic.tag.qlve",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_cgst,
            { "Congestion Control Feedback Type", "gquic.tag.cgst",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_epid,
            { "Endpoint identifier", "gquic.tag.epid",
              FT_STRING, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_tag_srst,
            { "Stateless Reset Token", "gquic.tag.srst",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },

        { &hf_gquic_tag_unknown,
            { "Unknown tag", "gquic.tag.unknown",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_padding,
            { "Padding", "gquic.padding",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_stream_data,
            { "Stream Data", "gquic.stream_data",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              NULL, HFILL }
        },
        { &hf_gquic_payload,
            { "Payload", "gquic.payload",
              FT_BYTES, BASE_NONE, NULL, 0x0,
              "(Google) QUIC Payload..", HFILL }
        },
    };


    static gint *ett[] = {
        &ett_gquic,
        &ett_gquic_puflags,
        &ett_gquic_prflags,
        &ett_gquic_ft,
        &ett_gquic_ftflags,
        &ett_gquic_tag_value
    };

    static ei_register_info ei[] = {
        { &ei_gquic_tag_undecoded, { "gquic.tag.undecoded", PI_UNDECODED, PI_NOTE, "Dissector for (Google)QUIC Tag code not implemented, Contact Wireshark developers if you want this supported", EXPFILL }},
        { &ei_gquic_tag_length, { "gquic.tag.length.truncated", PI_MALFORMED, PI_NOTE, "Truncated Tag Length...", EXPFILL }},
        { &ei_gquic_tag_unknown, { "gquic.tag.unknown.data", PI_UNDECODED, PI_NOTE, "Unknown Data", EXPFILL }},
        { &ei_gquic_version_invalid, { "gquic.version.invalid", PI_MALFORMED, PI_ERROR, "Invalid Version", EXPFILL }},
        { &ei_gquic_invalid_parameter, { "gquic.invalid.parameter", PI_MALFORMED, PI_ERROR, "Invalid Parameter", EXPFILL }},
        { &ei_gquic_length_invalid, { "gquic.length.invalid", PI_PROTOCOL, PI_WARN, "Invalid Length", EXPFILL }},
        { &ei_gquic_data_invalid, { "gquic.data.invalid", PI_PROTOCOL, PI_WARN, "Invalid Data", EXPFILL }},
    };

    expert_module_t *expert_gquic;

    proto_gquic = proto_register_protocol("GQUIC (Google Quick UDP Internet Connections)", "GQUIC", "gquic");

    proto_register_field_array(proto_gquic, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    gquic_module = prefs_register_protocol(proto_gquic, NULL);

    prefs_register_bool_preference(gquic_module, "debug.quic",
                       "Force decode of all (Google) QUIC Payload",
                       "Help for debug...",
                       &g_gquic_debug);

    expert_gquic = expert_register_protocol(proto_gquic);
    expert_register_field_array(expert_gquic, ei, array_length(ei));

    gquic_handle = register_dissector("gquic", dissect_gquic, proto_gquic);
}

void
proto_reg_handoff_gquic(void)
{
    tls13_handshake_handle = find_dissector("tls13-handshake");
    quic_handle = find_dissector("quic");
    dissector_add_uint_range_with_preference("udp.port", "", gquic_handle);
    heur_dissector_add("udp", dissect_gquic_heur, "Google QUIC", "gquic", proto_gquic, HEURISTIC_ENABLE);
}


/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */
