summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
author李佳 <[email protected]>2024-07-10 06:58:33 +0000
committerlijia <[email protected]>2024-07-17 16:29:42 +0800
commit60b96b10eb985d13ef6df0f4fc525074e7128325 (patch)
tree5b5b833295c37ae52c02eb9ccccc4dfa5598a377 /src
Initial commitv1.0.2
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt15
-rw-r--r--src/quic_deprotection.cpp1129
-rw-r--r--src/quic_deprotection.h116
-rw-r--r--src/quic_entry.cpp233
-rw-r--r--src/quic_entry.h58
-rw-r--r--src/quic_header.h35
-rw-r--r--src/quic_process.cpp1010
-rw-r--r--src/quic_process.h346
-rw-r--r--src/quic_util.h5
-rw-r--r--src/quic_version.cpp81
-rw-r--r--src/version.map10
11 files changed, 3038 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..db4cca3
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required (VERSION 2.8...3.10)
+
+file(GLOB SRC "*.cpp")
+set(DEPEND_DYN_LIB MESA_handle_logger)
+
+include_directories(/opt/tsg/framework/include/)
+
+# Shared Library Output
+add_library(${lib_name} SHARED ${SRC})
+set_target_properties(${lib_name} PROPERTIES LINK_FLAGS "-Wl,--version-script=${PROJECT_SOURCE_DIR}/src/version.map")
+set_target_properties(${lib_name} PROPERTIES PREFIX "")
+target_link_libraries(${lib_name} ${DNS_DEPEND_DYN_LIB} pthread -Wl,--no-whole-archive openssl-crypto-static -Wl,--no-whole-archive openssl-ssl-static)
+set_target_properties(${lib_name} PROPERTIES OUTPUT_NAME ${lib_name})
+
+install(TARGETS ${lib_name} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/sapp/stellar_plugin/quic_decoder COMPONENT LIBRARIES)
diff --git a/src/quic_deprotection.cpp b/src/quic_deprotection.cpp
new file mode 100644
index 0000000..923a395
--- /dev/null
+++ b/src/quic_deprotection.cpp
@@ -0,0 +1,1129 @@
+#include "quic_deprotection.h"
+
+#include <inttypes.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/sha.h>
+
+#define QUIC_IV_LEN 12 /* RFC 5116, 5.1 and RFC 8439, 2.3 for all supported ciphers */
+#define QUIC_HP_LEN 5 /* RFC 9001, 5.4.1. Header Protection Application: 5-byte mask */
+
+#define QUIC_MAX_CID_LEN 20
+#define QUIC_AES_128_KEY_LEN 16
+#define QUIC_MIN_INITIAL_SIZE 1200
+#define QUIC_UNSET_PN (uint64_t) - 1
+
+/*
+ * RFC 9000, 17.2. Long Header Packets
+ * 17.3. Short Header Packets
+ * QUIC flags in first byte
+ */
+#define QUIC_PKT_LONG 0x80 /* header form */
+#define QUIC_PKT_FIXED_BIT 0x40
+#define QUIC_PKT_TYPE 0x30 /* in long packet */
+#define QUIC_PKT_KPHASE 0x04 /* in short packet */
+
+#define quic_pkt_header_is_long(flags) ((flags)&QUIC_PKT_LONG)
+#define quic_pkt_header_is_short(flags) (((flags)&QUIC_PKT_LONG) == 0)
+#define quic_pkt_hp_mask(flags) (quic_pkt_header_is_long(flags) ? 0x0F : 0x1F)
+#define quic_pkt_rb_mask(flags) (quic_pkt_header_is_long(flags) ? 0x0C : 0x18)
+
+/* Long packet types */
+#define quic_pkt_level_is_initial(flags) (((flags)&QUIC_PKT_TYPE) == 0x00)
+#define quic_pkt_level_is_zrtt(flags) (((flags)&QUIC_PKT_TYPE) == 0x10)
+#define quic_pkt_level_is_handshake(flags) (((flags)&QUIC_PKT_TYPE) == 0x20)
+
+/* MAX MIN */
+#define MAX(val1, val2) ((val1 < val2) ? (val2) : (val1))
+#define MIN(val1, val2) ((val1 > val2) ? (val2) : (val1))
+
+#define quic_pkt_level_to_name(lvl) \
+ (lvl == ssl_encryption_application) ? "application" \
+ : (lvl == ssl_encryption_initial) ? "initial" \
+ : (lvl == ssl_encryption_handshake) ? "handshake" \
+ : "early"
+
+typedef struct
+{
+ const EVP_CIPHER *c;
+ const EVP_CIPHER *hp;
+ const EVP_MD *d;
+} quic_ciphers_t;
+
+///////////////////////////////////////////////////////////////////////////////
+// QUIC version
+///////////////////////////////////////////////////////////////////////////////
+
+#define QUIC_NVERSIONS (sizeof(quic_version_vals) / sizeof(quic_version_vals[0]))
+
+const quic_str_t quic_version_vals[] = {
+ {0x00000000, (u_char *)"Version Negotiation"},
+ {0x00000001, (u_char *)"1"},
+ /* Versions QXXX < Q050 are dissected by Wireshark as GQUIC and not as QUIC.
+ Nonetheless, some implementations report these values in "Version Negotiation"
+ packets, so decode these fields */
+ {0x51303433, (u_char *)"Google Q043"},
+ {0x51303434, (u_char *)"Google Q044"},
+ {0x51303436, (u_char *)"Google Q046"},
+ {0x51303530, (u_char *)"Google Q050"},
+ {0x54303530, (u_char *)"Google T050"},
+ {0x54303531, (u_char *)"Google T051"},
+ {0xfaceb001, (u_char *)"Facebook mvfst (draft-22)"},
+ {0xfaceb002, (u_char *)"Facebook mvfst (draft-27)"},
+ {0xfaceb00e, (u_char *)"Facebook mvfst (Experimental)"},
+ {0xff000004, (u_char *)"draft-04"},
+ {0xff000005, (u_char *)"draft-05"},
+ {0xff000006, (u_char *)"draft-06"},
+ {0xff000007, (u_char *)"draft-07"},
+ {0xff000008, (u_char *)"draft-08"},
+ {0xff000009, (u_char *)"draft-09"},
+ {0xff00000a, (u_char *)"draft-10"},
+ {0xff00000b, (u_char *)"draft-11"},
+ {0xff00000c, (u_char *)"draft-12"},
+ {0xff00000d, (u_char *)"draft-13"},
+ {0xff00000e, (u_char *)"draft-14"},
+ {0xff00000f, (u_char *)"draft-15"},
+ {0xff000010, (u_char *)"draft-16"},
+ {0xff000011, (u_char *)"draft-17"},
+ {0xff000012, (u_char *)"draft-18"},
+ {0xff000013, (u_char *)"draft-19"},
+ {0xff000014, (u_char *)"draft-20"},
+ {0xff000015, (u_char *)"draft-21"},
+ {0xff000016, (u_char *)"draft-22"},
+ {0xff000017, (u_char *)"draft-23"},
+ {0xff000018, (u_char *)"draft-24"},
+ {0xff000019, (u_char *)"draft-25"},
+ {0xff00001a, (u_char *)"draft-26"},
+ {0xff00001b, (u_char *)"draft-27"},
+ {0xff00001c, (u_char *)"draft-28"},
+ {0xff00001d, (u_char *)"draft-29"},
+ {0xff00001e, (u_char *)"draft-30"},
+ {0xff00001f, (u_char *)"draft-31"},
+ {0xff000020, (u_char *)"draft-32"},
+ {0, NULL}};
+
+static int quic_version_is_supported(uint32_t version)
+{
+ unsigned int i = 0;
+ for (i = 0; i < QUIC_NVERSIONS; i++)
+ {
+ if (quic_version_vals[i].len == version)
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// Returns the QUIC draft version or 0 if not applicable.
+static inline uint8_t quic_draft_version(uint32_t version)
+{
+ if ((version >> 8) == 0xff0000)
+ return (uint8_t)version;
+
+ // Facebook mvfst, based on draft -22.
+ if (version == 0xfaceb001)
+ return 22;
+
+ // Facebook mvfst, based on draft -27.
+ if (version == 0xfaceb002 || version == 0xfaceb00e)
+ return 27;
+
+ // GQUIC Q050, T050 and T051: they are not really based on any drafts,
+ // but we must return a sensible value
+ if (version == 0x51303530 || version == 0x54303530 || version == 0x54303531)
+ return 27;
+
+ /*
+ * https://tools.ietf.org/html/draft-ietf-quic-transport-32#section-15
+ * "Versions that follow the pattern 0x?a?a?a?a are reserved for use in
+ * forcing version negotiation to be exercised"
+ * It is tricky to return a correct draft version: such number is primarly
+ * used to select a proper salt (which depends on the version itself), but
+ * we don't have a real version here! Let's hope that we need to handle
+ * only latest drafts...
+ */
+ if ((version & 0x0F0F0F0F) == 0x0a0a0a0a)
+ return 29;
+
+ return 0;
+}
+
+static inline uint8_t quic_draft_is_max(uint32_t version, uint8_t max_version)
+{
+ uint8_t draft_version = quic_draft_version(version);
+ return draft_version && draft_version <= max_version;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// quic_parse_packet_header()
+///////////////////////////////////////////////////////////////////////////////
+
+static inline u_char *quic_parse_uint8(const u_char *pos, const u_char *end, uint8_t *out)
+{
+ if ((size_t)(end - pos) < 1)
+ {
+ return NULL;
+ }
+
+ *out = *pos;
+ return (u_char *)pos + 1;
+}
+
+static inline u_char *quic_parse_int(const u_char *pos, const u_char *end, uint64_t *out)
+{
+ u_char *p;
+ uint64_t value;
+ int len;
+
+ if (pos >= end)
+ {
+ return NULL;
+ }
+
+ p = (u_char *)pos;
+ len = 1 << (*p >> 6);
+
+ value = *p++ & 0x3f;
+
+ if ((size_t)(end - p) < (size_t)(len - 1))
+ {
+ return NULL;
+ }
+
+ while (--len)
+ {
+ value = (value << 8) + *p++;
+ }
+
+ *out = value;
+ return p;
+}
+
+static inline u_char *quic_parse_uint32(const u_char *pos, const u_char *end, uint32_t *out)
+{
+ if ((size_t)(end - pos) < sizeof(uint32_t))
+ {
+ return NULL;
+ }
+
+ *out = ((uint32_t)(pos)[0] << 24 | (pos)[1] << 16 | (pos)[2] << 8 | (pos)[3]);
+ return (u_char *)pos + sizeof(uint32_t);
+}
+
+static inline u_char *quic_parse_nbytes(const u_char *pos, const u_char *end, size_t len, u_char **out)
+{
+ if ((size_t)(end - pos) < len)
+ {
+ return NULL;
+ }
+
+ *out = (u_char *)pos;
+ return (u_char *)pos + len;
+}
+
+static int quic_parse_short_header(quic_dpt_t *dpt, size_t dcid_len)
+{
+ u_char *p = dpt->pos;
+ u_char *end = dpt->data + dpt->len;
+
+ if (!(dpt->flags & QUIC_PKT_FIXED_BIT))
+ {
+ LOG_WARN("QUIC fixed bit is not set");
+ return -1;
+ }
+
+ p = quic_parse_nbytes(p, end, dcid_len, &dpt->dcid.data);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC packet is too small to read dcid");
+ return -1;
+ }
+ dpt->dcid.len = dcid_len;
+
+ dpt->pos = p;
+ return 0;
+}
+
+static int quic_parse_long_header(quic_dpt_t *dpt)
+{
+ uint8_t idlen;
+
+ u_char *p = dpt->pos;
+ u_char *end = dpt->data + dpt->len;
+
+ // Parser Version
+ p = quic_parse_uint32(p, end, &dpt->version);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC packet is too small to read version");
+ return -1;
+ }
+
+ if (!(dpt->flags & QUIC_PKT_FIXED_BIT))
+ {
+ LOG_WARN("QUIC fixed bit is not set");
+ return -1;
+ }
+
+ // Parser DCID
+ p = quic_parse_uint8(p, end, &idlen);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC packet is too small to read dcid len");
+ return -1;
+ }
+ if (idlen > QUIC_MAX_CID_LEN)
+ {
+ LOG_WARN("QUIC packet dcid is too long");
+ return -1;
+ }
+ dpt->dcid.len = idlen;
+
+ p = quic_parse_nbytes(p, end, idlen, &dpt->dcid.data);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC packet is too small to read dcid");
+ return -1;
+ }
+
+ // Parser SCID
+ p = quic_parse_uint8(p, end, &idlen);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC packet is too small to read scid len");
+ return -1;
+ }
+ if (idlen > QUIC_MAX_CID_LEN)
+ {
+ LOG_WARN("QUIC packet scid is too long");
+ return -1;
+ }
+
+ dpt->scid.len = idlen;
+
+ p = quic_parse_nbytes(p, end, idlen, &dpt->scid.data);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC packet is too small to read scid");
+ return -1;
+ }
+
+ dpt->pos = p;
+ return 0;
+}
+
+static int quic_parse_long_header_v1(quic_dpt_t *dpt)
+{
+ uint64_t varint;
+
+ u_char *p = dpt->pos;
+ u_char *end = (u_char *)dpt->data + dpt->len;
+
+ // ssl_encryption_initial
+ if (quic_pkt_level_is_initial(dpt->flags))
+ {
+ dpt->level = ssl_encryption_initial;
+ if (dpt->len < QUIC_MIN_INITIAL_SIZE)
+ {
+ LOG_WARN("QUIC UDP datagram is too small for initial packet");
+ return -1;
+ }
+
+ // Parse Token
+ p = quic_parse_int(p, end, &varint);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC failed to parse token length");
+ return -1;
+ }
+ dpt->token.len = varint;
+
+ p = quic_parse_nbytes(p, end, dpt->token.len, &dpt->token.data);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC packet too small to read token data");
+ return -1;
+ }
+ }
+ // ssl_encryption_early_data
+ else if (quic_pkt_level_is_zrtt(dpt->flags))
+ {
+ dpt->level = ssl_encryption_early_data;
+ }
+ // ssl_encryption_handshake
+ else if (quic_pkt_level_is_handshake(dpt->flags))
+ {
+ dpt->level = ssl_encryption_handshake;
+ }
+ else
+ {
+ LOG_WARN("QUIC bad packet type");
+ return -1;
+ }
+
+ // Parse Packet Length
+ p = quic_parse_int(p, end, &varint);
+ if (p == NULL)
+ {
+ LOG_WARN("QUIC bad packet length");
+ return -1;
+ }
+ dpt->pkt_len = varint;
+
+ if (varint > (uint64_t)((dpt->data + dpt->len) - p))
+ {
+ LOG_WARN("QUIC truncated packet with level %s", quic_pkt_level_to_name(dpt->level));
+ return -1;
+ }
+
+ dpt->pos = p;
+ dpt->len = p + varint - dpt->data;
+
+ return 0;
+}
+
+static int quic_parse_packet_header(quic_dpt_t *dpt)
+{
+ if (quic_pkt_header_is_short(dpt->flags))
+ {
+ dpt->header_type = SHORT;
+ dpt->level = ssl_encryption_application;
+
+ // Parser DCID
+ if (quic_parse_short_header(dpt, QUIC_MAX_CID_LEN) != 0)
+ {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ dpt->header_type = LONG;
+
+ // Parser Version, DCID, SCID
+ if (quic_parse_long_header(dpt) != 0)
+ {
+ return -1;
+ }
+
+ // Check QUIC Version
+ if (!quic_version_is_supported(dpt->version))
+ {
+ LOG_WARN("QUIC version %08" PRIx32 "unsupport", dpt->version);
+ return -1;
+ }
+
+ // Parser Level, Token, Packet Length
+ if (quic_parse_long_header_v1(dpt) != 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// quic_keys_set_initial_secret()
+///////////////////////////////////////////////////////////////////////////////
+
+static int hkdf_expand(quic_str_t *out, const EVP_MD *digest, const quic_str_t *prk, const quic_str_t *info)
+{
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_new_id() failed");
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(pctx) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_derive_init() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_hkdf_mode() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_set_hkdf_md() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_key(pctx, prk->data, prk->len) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_set1_hkdf_key() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info->data, info->len) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_add1_hkdf_info() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_derive(pctx, out->data, &(out->len)) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_derive() failed");
+ goto failed;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+ return 0;
+
+failed:
+
+ EVP_PKEY_CTX_free(pctx);
+ return -1;
+}
+
+static int quic_hkdf_expand(const EVP_MD *digest, quic_str_t *out, const quic_str_t *label, const quic_str_t *prk)
+{
+ uint8_t info_buf[20];
+ info_buf[0] = 0;
+ info_buf[1] = out->len;
+ info_buf[2] = label->len;
+
+ uint8_t *p = (u_char *)memcpy(&info_buf[3], label->data, label->len) + label->len;
+ *p = '\0';
+
+ quic_str_t info;
+ info.len = 2 + 1 + label->len + 1;
+ info.data = info_buf;
+
+ if (hkdf_expand(out, digest, prk, &info) != 0)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int hkdf_extract(quic_str_t *out, const EVP_MD *digest, const quic_str_t *secret, const quic_str_t *initial_salt)
+{
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_new_id() failed");
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(pctx) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_derive_init() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_hkdf_mode() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_set_hkdf_md() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret->data, secret->len) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_set1_hkdf_key() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, initial_salt->data, initial_salt->len) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_CTX_set1_hkdf_salt() failed");
+ goto failed;
+ }
+
+ if (EVP_PKEY_derive(pctx, out->data, &(out->len)) <= 0)
+ {
+ LOG_ERROR("EVP_PKEY_derive() failed");
+ goto failed;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+ return 0;
+
+failed:
+
+ EVP_PKEY_CTX_free(pctx);
+ return -1;
+}
+
+static int quic_keys_set_initial_secret(quic_secret_t *client_secret, const quic_str_t *dcid, uint32_t version)
+{
+ unsigned int i;
+ const quic_str_t initial_salt_v1 = quic_string(
+ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a");
+ const quic_str_t initial_salt_draft_22 = quic_string(
+ "\x7f\xbc\xdb\x0e\x7c\x66\xbb\xe9\x19\x3a\x96\xcd\x21\x51\x9e\xbd\x7a\x02\x64\x4a");
+ const quic_str_t initial_salt_draft_23 = quic_string(
+ "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02");
+ const quic_str_t initial_salt_draft_29 = quic_string(
+ "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90\xa8\x99");
+ const quic_str_t initial_salt_draft_q50 = quic_string(
+ "\x50\x45\x74\xEF\xD0\x66\xFE\x2F\x9D\x94\x5C\xFC\xDB\xD3\xA7\xF0\xD3\xB5\x6B\x45");
+ const quic_str_t initial_salt_draft_t50 = quic_string(
+ "\x7f\xf5\x79\xe5\xac\xd0\x72\x91\x55\x80\x30\x4c\x43\xa2\x36\x7c\x60\x48\x83\x10");
+ const quic_str_t initial_salt_draft_t51 = quic_string(
+ "\x7a\x4e\xde\xf4\xe7\xcc\xee\x5f\xa4\x50\x6c\x19\x12\x4f\xc8\xcc\xda\x6e\x03\x3d");
+
+ const quic_str_t *initial_salt;
+ if (version == 0x51303530)
+ {
+ initial_salt = &initial_salt_draft_q50;
+ }
+ else if (version == 0x54303530)
+ {
+ initial_salt = &initial_salt_draft_t50;
+ }
+ else if (version == 0x54303531)
+ {
+ initial_salt = &initial_salt_draft_t51;
+ }
+ else if (quic_draft_is_max(version, 22))
+ {
+ initial_salt = &initial_salt_draft_22;
+ }
+ else if (quic_draft_is_max(version, 28))
+ {
+ initial_salt = &initial_salt_draft_23;
+ }
+ else if (quic_draft_is_max(version, 32))
+ {
+ initial_salt = &initial_salt_draft_29;
+ }
+ else
+ {
+ initial_salt = &initial_salt_v1;
+ }
+
+ /*
+ * RFC 9001, section 5. Packet Protection
+ *
+ * Initial packets use AEAD_AES_128_GCM. The hash function
+ * for HKDF when deriving initial secrets and keys is SHA-256.
+ */
+ const EVP_MD *digest = EVP_sha256();
+
+ uint8_t is[SHA256_DIGEST_LENGTH] = {0};
+ quic_str_t initial_secret;
+ initial_secret.data = is;
+ initial_secret.len = SHA256_DIGEST_LENGTH;
+
+ // Use dcid and initial_salt get initial_secret
+ if (hkdf_extract(&initial_secret, digest, dcid, initial_salt) != 0)
+ {
+ return -1;
+ }
+
+ struct
+ {
+ quic_str_t label;
+ quic_str_t *key;
+ quic_str_t *prk;
+ } seq[] = {
+ /* labels per RFC 9001, 5.1. Packet Protection Keys */
+ {quic_string("tls13 client in"), &client_secret->secret, &initial_secret},
+ {quic_string("tls13 quic key"), &client_secret->key, &client_secret->secret},
+ {quic_string("tls13 quic iv"), &client_secret->iv, &client_secret->secret},
+ {quic_string("tls13 quic hp"), &client_secret->hp, &client_secret->secret},
+ };
+
+ for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++)
+ {
+ if (quic_hkdf_expand(digest, seq[i].key, &seq[i].label, seq[i].prk) != 0)
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// quic_deprotection_packet()
+///////////////////////////////////////////////////////////////////////////////
+
+static int quic_deprotection_header(const EVP_CIPHER *cipher, const quic_secret_t *secret, u_char *out, const u_char *in)
+{
+ int outlen;
+ u_char zero[QUIC_HP_LEN] = {0};
+
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL)
+ {
+ return -1;
+ }
+
+ if (EVP_EncryptInit_ex(ctx, cipher, NULL, secret->hp.data, in) != 1)
+ {
+ LOG_ERROR("EVP_EncryptInit_ex() failed");
+ goto failed;
+ }
+
+ if (!EVP_EncryptUpdate(ctx, out, &outlen, zero, QUIC_HP_LEN))
+ {
+ LOG_ERROR("EVP_EncryptUpdate() failed");
+ goto failed;
+ }
+
+ if (!EVP_EncryptFinal_ex(ctx, out + QUIC_HP_LEN, &outlen))
+ {
+ LOG_ERROR("EVP_EncryptFinal_ex() failed");
+ goto failed;
+ }
+
+ EVP_CIPHER_CTX_free(ctx);
+ return 0;
+
+failed:
+
+ EVP_CIPHER_CTX_free(ctx);
+ return -1;
+}
+
+static uint64_t quic_deprotection_pktnum(u_char **pos, int len, const u_char *mask, uint64_t *largest_pkt_num)
+{
+ u_char *p;
+ uint64_t truncated_pn, expected_pn, candidate_pn;
+ uint64_t pn_nbits, pn_win, pn_hwin, pn_mask;
+
+ pn_nbits = MIN(len * 8, 62);
+
+ p = *pos;
+ truncated_pn = *p++ ^ *mask++;
+
+ while (--len)
+ {
+ truncated_pn = (truncated_pn << 8) + (*p++ ^ *mask++);
+ }
+
+ *pos = p;
+
+ expected_pn = *largest_pkt_num + 1;
+ pn_win = 1ULL << pn_nbits;
+ pn_hwin = pn_win / 2;
+ pn_mask = pn_win - 1;
+
+ candidate_pn = (expected_pn & ~pn_mask) | truncated_pn;
+
+ if ((int64_t)candidate_pn <= (int64_t)(expected_pn - pn_hwin) && candidate_pn < (1ULL << 62) - pn_win)
+ {
+ candidate_pn += pn_win;
+ }
+ else if (candidate_pn > expected_pn + pn_hwin && candidate_pn >= pn_win)
+ {
+ candidate_pn -= pn_win;
+ }
+
+ *largest_pkt_num = MAX((int64_t)*largest_pkt_num, (int64_t)candidate_pn);
+
+ return candidate_pn;
+}
+
+static void quic_deprotection_nonce(u_char *nonce, size_t len, uint64_t pkt_num)
+{
+ nonce[len - 4] ^= (pkt_num & 0xff000000) >> 24;
+ nonce[len - 3] ^= (pkt_num & 0x00ff0000) >> 16;
+ nonce[len - 2] ^= (pkt_num & 0x0000ff00) >> 8;
+ nonce[len - 1] ^= (pkt_num & 0x000000ff);
+}
+
+static int quic_deprotection_payload(const EVP_CIPHER *cipher, const quic_secret_t *secret, quic_str_t *out, const u_char *nonce, const quic_str_t *in, const quic_str_t *ad)
+{
+ int len;
+ u_char *tag;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL)
+ {
+ LOG_ERROR("EVP_CIPHER_CTX_new() failed");
+ return -1;
+ }
+
+ if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_DecryptInit_ex() failed");
+ return -1;
+ }
+
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, secret->iv.len, NULL) == 0)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed");
+ return -1;
+ }
+
+ if (EVP_DecryptInit_ex(ctx, NULL, NULL, secret->key.data, nonce) != 1)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_DecryptInit_ex() failed");
+ return -1;
+ }
+
+ if (EVP_DecryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_DecryptUpdate() failed");
+ return -1;
+ }
+
+ if (EVP_DecryptUpdate(ctx, out->data, &len, in->data, in->len - EVP_GCM_TLS_TAG_LEN) != 1)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_DecryptUpdate() failed");
+ return -1;
+ }
+
+ out->len = len;
+ tag = in->data + in->len - EVP_GCM_TLS_TAG_LEN;
+
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, tag) == 0)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed");
+ return -1;
+ }
+
+ if (EVP_DecryptFinal_ex(ctx, out->data + len, &len) <= 0)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_DecryptFinal_ex failed");
+ return -1;
+ }
+
+ out->len += len;
+ EVP_CIPHER_CTX_free(ctx);
+
+ return 0;
+}
+
+static int quic_deprotection_packet(quic_dpt_t *dpt)
+{
+ u_char *p, *sample;
+ size_t len;
+ uint64_t pkt_num, lpn;
+ int pnl, rc, key_phase;
+ quic_str_t in, ad;
+ uint8_t nonce[QUIC_IV_LEN] = {0}, mask[QUIC_HP_LEN] = {0};
+
+ // Init ciphers, only process the quic package whose level is init, so only use AES_128_GCM_SHA256
+ quic_ciphers_t ciphers;
+ ciphers.c = EVP_aes_128_gcm();
+ ciphers.hp = EVP_aes_128_ctr();
+ ciphers.d = EVP_sha256();
+
+ // 使用 client_secret secrets
+ quic_secret_t *secret = &dpt->client_secret;
+
+ p = dpt->pos;
+ len = dpt->data + dpt->len - p;
+
+ /*
+ * RFC 9001, 5.4.2. Header Protection Sample
+ * 5.4.3. AES-Based Header Protection
+ * 5.4.4. ChaCha20-Based Header Protection
+ *
+ * the Packet Number field is assumed to be 4 bytes long
+ * AES and ChaCha20 algorithms sample 16 bytes
+ */
+
+ if (len < EVP_GCM_TLS_TAG_LEN + 4)
+ {
+ LOG_WARN("QUIC payload length %zu too small", len);
+ return -1;
+ }
+
+ sample = p + 4;
+
+ /******************************************************
+ * header protection
+ ******************************************************/
+
+ // Use the ciphers.hp algorithm and the secret.HP to encrypt the data in the sample and store it in the mask
+ if (quic_deprotection_header(ciphers.hp, secret, mask, sample) != 0)
+ {
+ return -1;
+ }
+
+ // Parse Flags After Decode
+ dpt->flags ^= mask[0] & quic_pkt_hp_mask(dpt->flags);
+
+ if (quic_pkt_header_is_short(dpt->flags))
+ {
+ key_phase = (dpt->flags & QUIC_PKT_KPHASE) != 0;
+
+ if (key_phase != dpt->key_phase)
+ {
+ // TODO
+ LOG_WARN("key need update !!!!");
+ }
+ }
+
+ lpn = dpt->largest_pkt_num;
+
+ // RFC pn_length = (packet[0] & 0x03) + 1
+ // Parser Packet Number length
+ pnl = (dpt->flags & 0x03) + 1;
+
+ // Parser Packet number
+ pkt_num = quic_deprotection_pktnum(&p, pnl, &mask[1], &lpn);
+ dpt->pkt_num = pkt_num;
+
+ /******************************************************
+ * packet protection
+ ******************************************************/
+
+ in.data = p;
+ in.len = len - pnl;
+
+ ad.len = p - dpt->data;
+ ad.data = dpt->plaintext;
+
+ memcpy(ad.data, dpt->data, ad.len);
+ ad.data[0] = dpt->flags;
+
+ do
+ {
+ ad.data[ad.len - pnl] = pkt_num >> (8 * (pnl - 1)) % 256;
+ } while (--pnl);
+
+ memcpy(nonce, secret->iv.data, secret->iv.len);
+ quic_deprotection_nonce(nonce, sizeof(nonce), pkt_num);
+
+ dpt->payload.len = in.len - EVP_GCM_TLS_TAG_LEN;
+ dpt->payload.data = dpt->plaintext + ad.len;
+
+ // Parser payload
+ rc = quic_deprotection_payload(ciphers.c, secret, &dpt->payload, nonce, &in, &ad);
+ if (rc != 0)
+ {
+ return -1;
+ }
+
+ if (dpt->payload.len == 0)
+ {
+ /*
+ * RFC 9000, 12.4. Frames and Frame Types
+ *
+ * An endpoint MUST treat receipt of a packet containing no
+ * frames as a connection error of type PROTOCOL_VIOLATION.
+ */
+ LOG_WARN("QUIC zero-length packet");
+ return -1;
+ }
+
+ if (dpt->flags & quic_pkt_rb_mask(dpt->flags))
+ {
+ /*
+ * RFC 9000, Reserved Bits
+ *
+ * An endpoint MUST treat receipt of a packet that has
+ * a non-zero value for these bits, after removing both
+ * packet and header protection, as a connection error
+ * of type PROTOCOL_VIOLATION.
+ */
+ LOG_WARN("QUIC reserved bit set in packet");
+ return -1;
+ }
+
+ // Set Largest Packet Number
+ dpt->largest_pkt_num = lpn;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// debug
+///////////////////////////////////////////////////////////////////////////////
+
+static void quic_str_to_hex(u_char *data, size_t len)
+{
+ for (unsigned int i = 0; i < len; i++)
+ {
+ printf("%02x", data[i]);
+ }
+ printf("\n");
+}
+
+static u_char *quic_version_to_str(uint32_t version)
+{
+ unsigned int i = 0;
+ for (i = 0; i < QUIC_NVERSIONS; i++)
+ {
+ if (quic_version_vals[i].len == version)
+ {
+ return quic_version_vals[i].data;
+ }
+ }
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PUB API
+///////////////////////////////////////////////////////////////////////////////
+
+quic_dpt_t *quic_deprotection_new(void)
+{
+ quic_dpt_t *dpt = (quic_dpt_t *)calloc(1, sizeof(quic_dpt_t));
+
+ // Allocate the space of length len + 1, instead of allocating the space of length len, mainly for the convenience of debugging printf
+ dpt->client_secret.secret.len = SHA256_DIGEST_LENGTH;
+ dpt->client_secret.secret.data = (u_char *)calloc(SHA256_DIGEST_LENGTH + 1, sizeof(u_char));
+
+ dpt->client_secret.key.len = QUIC_AES_128_KEY_LEN;
+ dpt->client_secret.key.data = (u_char *)calloc(QUIC_AES_128_KEY_LEN + 1, sizeof(u_char));
+
+ dpt->client_secret.hp.len = QUIC_AES_128_KEY_LEN;
+ dpt->client_secret.hp.data = (u_char *)calloc(QUIC_AES_128_KEY_LEN + 1, sizeof(u_char));
+
+ dpt->client_secret.iv.len = QUIC_IV_LEN;
+ dpt->client_secret.iv.data = (u_char *)calloc(QUIC_IV_LEN + 1, sizeof(u_char));
+
+ // NOTE: QUIC_MAX_UDP_PAYLOAD_SIZE is 65527(form Nginx-quice offical)
+ dpt->plaintext = (u_char *)calloc(QUIC_MAX_UDP_PAYLOAD_SIZE + 1, sizeof(u_char));
+
+ return dpt;
+}
+
+void quic_deprotection_free(quic_dpt_t *dpt)
+{
+ if (dpt == NULL)
+ {
+ return;
+ }
+
+ if (dpt->client_secret.secret.data)
+ {
+ free(dpt->client_secret.secret.data);
+ dpt->client_secret.secret.data = NULL;
+ dpt->client_secret.secret.len = 0;
+ }
+
+ if (dpt->client_secret.key.data)
+ {
+ free(dpt->client_secret.key.data);
+ dpt->client_secret.key.data = NULL;
+ dpt->client_secret.key.len = 0;
+ }
+
+ if (dpt->client_secret.hp.data)
+ {
+ free(dpt->client_secret.hp.data);
+ dpt->client_secret.hp.data = NULL;
+ dpt->client_secret.hp.len = 0;
+ }
+
+ if (dpt->client_secret.iv.data)
+ {
+ free(dpt->client_secret.iv.data);
+ dpt->client_secret.iv.data = NULL;
+ dpt->client_secret.iv.len = 0;
+ }
+
+ if (dpt->plaintext)
+ {
+ free(dpt->plaintext);
+ dpt->plaintext = NULL;
+ }
+
+ free(dpt);
+ dpt = NULL;
+}
+
+void quic_deprotection_dump(quic_dpt_t *dpt)
+{
+ printf("QUIC version : %08" PRIx32 " %s\n", dpt->version, quic_version_to_str(dpt->version));
+ printf("QUIC type : %s\n", dpt->header_type == LONG ? "long" : "short");
+ printf("QUIC level : %s\n", quic_pkt_level_to_name(dpt->level));
+ printf("QUIC flags : %x\n", dpt->flags);
+ printf("QUIC num : %" PRIu64 "\n", dpt->pkt_num);
+ printf("QUIC len : %" PRIu64 "\n", dpt->pkt_len);
+ printf("QUIC larg_num : %" PRIu64 "\n", dpt->largest_pkt_num);
+
+ printf("QUIC dcid : %zu, ", dpt->dcid.len);
+ quic_str_to_hex(dpt->dcid.data, dpt->dcid.len);
+
+ printf("QUIC scid : %zu, ", dpt->scid.len);
+ quic_str_to_hex(dpt->scid.data, dpt->scid.len);
+
+ printf("QUIC token : %zu, ", dpt->token.len);
+ quic_str_to_hex(dpt->token.data, dpt->token.len);
+
+ printf("QUIC hp : %zu, ", dpt->client_secret.hp.len);
+ quic_str_to_hex(dpt->client_secret.hp.data, dpt->client_secret.hp.len);
+
+ printf("QUIC iv : %zu, ", dpt->client_secret.iv.len);
+ quic_str_to_hex(dpt->client_secret.iv.data, dpt->client_secret.iv.len);
+
+ printf("QUIC key : %zu, ", dpt->client_secret.key.len);
+ quic_str_to_hex(dpt->client_secret.key.data, dpt->client_secret.key.len);
+
+ printf("QUIC secretn : %zu, ", dpt->client_secret.secret.len);
+ quic_str_to_hex(dpt->client_secret.secret.data, dpt->client_secret.secret.len);
+
+ printf("QUIC plaintex : %zu, ", strlen((char *)dpt->plaintext));
+ quic_str_to_hex(dpt->plaintext, strlen((char *)dpt->plaintext));
+
+ printf("QUIC payload : %zu, ", dpt->payload.len);
+ quic_str_to_hex(dpt->payload.data, dpt->payload.len);
+}
+
+int quic_deprotection(quic_dpt_t *dpt, const u_char *payload, size_t payload_len)
+{
+ dpt->data = (u_char *)payload;
+ dpt->len = payload_len;
+ dpt->pos = (u_char *)dpt->data;
+ dpt->flags = payload[0];
+ dpt->pos++;
+ dpt->largest_pkt_num = QUIC_UNSET_PN;
+
+ // Parser Level, Version, DCID, SCID, Token, Packet Length
+ if (quic_parse_packet_header(dpt) == -1)
+ {
+ return -1;
+ }
+
+ if (dpt->level != ssl_encryption_initial)
+ {
+ LOG_WARN("QUIC level %s ignoring", quic_pkt_level_to_name(dpt->level))
+ return -1;
+ }
+
+ /*
+ * 1.Get the initial_salt to be used through pkt_version
+ * 2.DCID and initial_salt generate initial_secret through SHA-256
+ * 3.IN/Key/IV/HP lable of initial_secret and client_secret generates HP/Key/IV key of client_secret
+ */
+ if (quic_keys_set_initial_secret(&dpt->client_secret, &dpt->dcid, dpt->version) == -1)
+ {
+ goto failed;
+ }
+
+ if (dpt->client_secret.key.len == 0)
+ {
+ LOG_WARN("QUIC packet no client_secret, ignoring");
+ goto failed;
+ }
+
+ if (quic_deprotection_packet(dpt) == -1)
+ {
+ goto failed;
+ }
+
+ // deprotection data: dpt->payload.data
+
+ return 0;
+
+failed:
+
+ return -1;
+} \ No newline at end of file
diff --git a/src/quic_deprotection.h b/src/quic_deprotection.h
new file mode 100644
index 0000000..11c0d03
--- /dev/null
+++ b/src/quic_deprotection.h
@@ -0,0 +1,116 @@
+#ifndef _QUIC_DEPROTECTION_H
+#define _QUIC_DEPROTECTION_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_SWITCH
+
+#define LOG_DEBUG(format, ...) \
+ { \
+ fprintf(stdout, format "\n", ##__VA_ARGS__); \
+ fflush(stdout); \
+ }
+
+#define LOG_WARN(format, ...) \
+ { \
+ fprintf(stderr, format "\n", ##__VA_ARGS__); \
+ fflush(stderr); \
+ }
+
+#define LOG_ERROR(format, ...) \
+ { \
+ fprintf(stderr, format "\n", ##__VA_ARGS__); \
+ fflush(stderr); \
+ }
+
+#else
+
+#define LOG_DEBUG(format, ...)
+#define LOG_WARN(format, ...)
+#define LOG_ERROR(format, ...)
+
+#endif
+
+#define QUIC_MAX_UDP_PAYLOAD_SIZE 1460
+
+#define quic_string(str) \
+ { \
+ sizeof(str) - 1, (u_char *)str \
+ }
+
+typedef struct
+{
+ size_t len;
+ u_char *data;
+} quic_str_t;
+
+typedef struct quic_secret_s
+{
+ quic_str_t secret;
+ quic_str_t key;
+ quic_str_t iv;
+ quic_str_t hp;
+} quic_secret_t;
+
+typedef enum
+{
+ ssl_encryption_initial = 0,
+ ssl_encryption_early_data = 1,
+ ssl_encryption_handshake = 2,
+ ssl_encryption_application = 3,
+} ssl_encryption_level_t;
+
+typedef enum
+{
+ LONG = 0,
+ SHORT = 1,
+} quic_header_type;
+
+typedef struct
+{
+ quic_secret_t client_secret;
+ ssl_encryption_level_t level; // QUIC Packet Process Level
+ quic_header_type header_type; // QUIC Packet Header Type
+
+ uint32_t version; // QUIC Version
+ uint8_t flags; // QUIC Flags
+ u_char *data; // QUIC Packet Data
+ size_t len; // QUIC Packet Length
+ u_char *pos; // Process Ptr
+ uint64_t largest_pkt_num;
+
+ quic_str_t dcid; // QUIC DCID
+ quic_str_t scid; // QUIC SCID
+ quic_str_t token; // QUIC TOKEN
+
+ size_t pkt_len;
+ uint64_t pkt_num; // QUIC Packet Number
+ u_char *plaintext;
+ quic_str_t payload; // Decrypted data
+
+ unsigned key_phase : 1;
+} quic_dpt_t;
+
+quic_dpt_t *quic_deprotection_new(void);
+void quic_deprotection_free(quic_dpt_t *dpt);
+void quic_deprotection_dump(quic_dpt_t *dpt);
+int quic_deprotection(quic_dpt_t *dpt, const u_char *payload, size_t payload_len);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
diff --git a/src/quic_entry.cpp b/src/quic_entry.cpp
new file mode 100644
index 0000000..d8cae1b
--- /dev/null
+++ b/src/quic_entry.cpp
@@ -0,0 +1,233 @@
+#include <stdio.h>
+#include <assert.h>
+#include <MESA/MESA_prof_load.h>
+#include <MESA/MESA_handle_logger.h>
+
+#include "quic_decoder.h"
+#include "quic_entry.h"
+#include "quic_process.h"
+#include "quic_deprotection.h"
+#include "quic_util.h"
+
+static const char *g_quic_proto_conffile="./conf/quic_decoder/main.conf";
+// static const char *g_quic_regionname_conffile="./conf/quic/quic.conf";
+static const char *g_quic_log_path = "./log/quic_decoder/quic_decoder";
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <stellar/stellar.h>
+#include <stellar/session.h>
+#include <stellar/session_mq.h>
+#include <stellar/session_exdata.h>
+
+#define GIT_VERSION_CATTER(v) __attribute__((__used__)) const char * GIT_VERSION_##v = NULL
+#define GIT_VERSION_EXPEND(v) GIT_VERSION_CATTER(v)
+
+/* VERSION TAG */
+#ifdef GIT_VERSION
+GIT_VERSION_EXPEND(GIT_VERSION);
+#else
+static __attribute__((__used__)) const char * GIT_VERSION_UNKNOWN = NULL;
+#endif
+#undef GIT_VERSION_CATTER
+#undef GIT_VERSION_EXPEND
+
+#ifdef __cplusplus
+}
+#endif
+
+static int parse_quic_port(char *port_list, unsigned short *quic_port, int quic_port_num)
+{
+ int i=0,ret=0;
+ int port_num=0;
+ int range_len=0,used_len=0;
+ char buf[256]={0};
+ unsigned short s_port=0,e_port=0;
+ char *begin=NULL,*end=NULL,*pchr=NULL;
+
+ if(port_list==NULL)
+ {
+ return 0;
+ }
+
+ begin=port_list;
+ end=NULL;
+ range_len=strlen(port_list);
+
+ while(range_len>used_len)
+ {
+ end=index(begin, ';');
+ if(end==NULL)
+ {
+ end=begin+range_len-used_len;
+ }
+
+ if(end==begin)
+ {
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, begin, end-begin);
+ used_len+=end-begin+1;
+ if(range_len>used_len)
+ {
+ begin=end+1;
+ }
+
+ pchr=strchr(buf, '-');
+ if(pchr == NULL)
+ {
+ s_port=(unsigned short)atoi(buf);
+ e_port=s_port;
+
+ }
+ else
+ {
+ ret=sscanf(buf, "%hu-%hu", &s_port, &e_port);
+ if(ret!=2)
+ {
+ continue;
+ }
+ }
+
+ for(i=s_port; i<=e_port && port_num<quic_port_num; i++)
+ {
+ quic_port[port_num++]= htons((unsigned short)i);
+ }
+ }
+
+ return port_num;
+}
+
+static void free_quicinfo(struct quic_info *quic_info)
+{
+ if(quic_info->sni.iov_base)
+ FREE(quic_info->sni.iov_base);
+
+ if(quic_info->user_agent.iov_base)
+ FREE(quic_info->user_agent.iov_base);
+ return ;
+}
+
+extern "C" void quic_on_session_msg_cb(struct session *sess, int topic_id, const void *msg, void *per_session_ctx, void *plugin_env)
+{
+ struct quic_param *quic_plugin_env = (struct quic_param *)plugin_env;
+ struct quic_context *context = (struct quic_context *)per_session_ctx;
+
+ int thread_seq = session_get_current_thread_id(sess);
+ const char *payload = NULL;
+ size_t payload_len = -1;
+
+ enum session_state sstate = session_get_current_state(sess);
+ if(sstate == SESSION_STATE_CLOSING)
+ {
+ return;
+ }
+
+ payload = session_get0_current_payload(sess, &payload_len);
+ if(NULL == payload || payload_len <= 0)
+ {
+ return;
+ }
+ quic_analyze_entry(sess, quic_plugin_env, context, thread_seq, payload, payload_len);
+
+ return;
+}
+
+void *quic_session_ctx_new_cb(struct session *sess, void *plugin_env)
+{
+ struct quic_param *quic_plugin_env = (struct quic_param *)plugin_env;
+ if(0 == quic_protocol_identify(sess, quic_plugin_env))
+ {
+ stellar_session_plugin_dettach_current_session(sess);
+ return NULL;
+ }
+ size_t payload_len = 0;
+ const char * payload = session_get0_current_payload(sess, &payload_len);
+ if(NULL == payload || payload_len <= 0)
+ {
+ stellar_session_plugin_dettach_current_session(sess);
+ return NULL;
+ }
+ int payload_offset = 0;
+ enum QUIC_VERSION_T quic_ver = is_quic_protocol(payload, payload_len, &payload_offset);
+ if(QUIC_VERSION_UNKNOWN == quic_ver || (size_t)payload_offset > payload_len)
+ {
+ stellar_session_plugin_dettach_current_session(sess);
+ return NULL;
+ }
+ struct quic_context *qcontext = (struct quic_context *)CALLOC(1, sizeof(struct quic_context));
+ qcontext->quic_info.quic_version = (unsigned int )quic_ver;
+ return (void *)qcontext;
+}
+
+void quic_session_ctx_free_cb(struct session *sess, void *session_ctx, void *plugin_env)
+{
+ if(session_ctx){
+ struct quic_context *qcontext = (struct quic_context *)session_ctx;
+ free_quicinfo(&qcontext->quic_info);
+ if(qcontext->quic_buf.buffer){
+ free(qcontext->quic_buf.buffer);
+ }
+ FREE(session_ctx);
+ }
+ return;
+}
+
+void quic_session_exdata_free_cb(struct session *sess, int idx, void *ex_ptr, void *arg)
+{
+ return;
+}
+
+void quic_msg_free_cb(struct session *sess, void *msg, void *msg_free_arg)
+{
+ FREE(msg);
+}
+
+extern "C" void *QUIC_ONLOAD(struct stellar *st)
+{
+ char buff[2048]={0};
+
+ struct quic_param *quic_plugin_env = (struct quic_param *)CALLOC(1, sizeof(struct quic_param));
+ quic_plugin_env->st = st;
+
+ MESA_load_profile_int_def(g_quic_proto_conffile, "QUIC", "LOG_LEVEL", &quic_plugin_env->level, RLOG_LV_FATAL);
+ MESA_load_profile_string_def(g_quic_proto_conffile, "QUIC", "LOG_PATH", quic_plugin_env->log_path, sizeof(quic_plugin_env->log_path), g_quic_log_path);
+
+ MESA_load_profile_int_def(g_quic_proto_conffile, "QUIC", "DECRYPTED_SWITCH", &quic_plugin_env->decrypted_switch, 2);
+ // MESA_load_profile_int_def(g_quic_proto_conffile, "QUIC", "MAX_PARSE_PKT_NUM", &quic_plugin_env->max_parse_pkt_num, 3);
+ MESA_load_profile_int_def(g_quic_proto_conffile, "QUIC", "MAX_CHLO_SIZE", &quic_plugin_env->max_chlo_size, 4096);
+
+ MESA_load_profile_string_def(g_quic_proto_conffile, "QUIC", "QUIC_PORT_LIST", buff, sizeof(buff), "443;8443;");
+ quic_plugin_env->quic_port_num=parse_quic_port(buff, quic_plugin_env->quic_port_list, SUPPORT_QUIC_PORT_NUM);
+
+ quic_plugin_env->logger=MESA_create_runtime_log_handle(quic_plugin_env->log_path, quic_plugin_env->level);
+ if(quic_plugin_env->logger==NULL)
+ {
+ fprintf(stderr, "MESA_create_runtime_log_handle failed, level: %d log_path: %s", quic_plugin_env->level, quic_plugin_env->log_path);
+ return NULL;
+ }
+
+ quic_plugin_env->quic_plugid = stellar_session_plugin_register(st, quic_session_ctx_new_cb, quic_session_ctx_free_cb, quic_plugin_env);
+ assert(quic_plugin_env->quic_plugid >= 0);
+
+ quic_plugin_env->exdata_id=stellar_session_exdata_new_index(st, "QUIC_EXDATA", quic_session_exdata_free_cb, quic_plugin_env);
+ assert(quic_plugin_env->exdata_id >= 0);
+ quic_plugin_env->udp_topic_id=stellar_session_mq_get_topic_id(st, TOPIC_UDP);
+ assert(quic_plugin_env->udp_topic_id >= 0);
+ stellar_session_mq_subscribe(st, quic_plugin_env->udp_topic_id, quic_on_session_msg_cb, quic_plugin_env->quic_plugid);
+
+ quic_plugin_env->quic_topic_id=stellar_session_mq_create_topic(st, QUIC_DECODER_TOPIC, quic_msg_free_cb, quic_plugin_env);
+ assert(quic_plugin_env->quic_topic_id >= 0);
+ return (void *)quic_plugin_env;
+}
+
+extern "C" void QUIC_UNLOAD(void *plugin_env)
+{
+ struct quic_param *quic_plugin_env = (struct quic_param *)plugin_env;
+ MESA_destroy_runtime_log_handle(quic_plugin_env->logger);
+ FREE(plugin_env);
+ return;
+} \ No newline at end of file
diff --git a/src/quic_entry.h b/src/quic_entry.h
new file mode 100644
index 0000000..478b2d9
--- /dev/null
+++ b/src/quic_entry.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "quic_decoder.h"
+#include <bits/types/struct_iovec.h>
+typedef struct iovec qstring;
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define FALSE 0x00
+#define TRUE 0x01
+#define MAYBE 0x02
+
+#define QUIC_VERSION_STRING_MAX_SIZE (32)
+
+#define SUPPORT_QUIC_PORT_NUM 128
+
+#define QUIC_HALF_CLOSE 0x01
+#define QUIC_WHOLE_CLOSE 0x02
+#define QUIC_DATA 0x03
+#define QUIC_KEY 1
+#define QUIC_RETURN_NORM 0x60
+#define QUIC_RETURN_UNNORM 0x61
+#define QUIC_RETURN_RESET_BUFFER 0x62
+#define QUIC_RETURN_DROPME 0x63
+#define MAX_REGION_NUM 15
+#define REGION_NAME_LEN 32
+
+struct quic_param
+{
+ struct stellar *st;
+ int quic_plugid;
+ int level;
+ int quic_port_num;
+ int decrypted_switch;
+ // int max_parse_pkt_num;
+ int max_chlo_size;
+ // int context_bridge_id;
+ int exdata_id;
+ int udp_topic_id; //as subscriber
+ int quic_topic_id; //as publisher
+ unsigned short quic_port_list[SUPPORT_QUIC_PORT_NUM]; //network order
+ char quic_conf_regionname[MAX_REGION_NUM][REGION_NAME_LEN];
+ char log_path[128];
+ void *logger;
+};
+
+enum quic_mes_type{
+ VER_NEGO = 0, //vertion negotiation packet
+ PUB_RST, //public reset packet
+ FRAME, //frame packet
+ FEC, //FEC packet
+ Initial, //iquic
+ Retey, //iquic
+ Handshake, //iquic
+ MSG_UNKNOWN = 255
+};
diff --git a/src/quic_header.h b/src/quic_header.h
new file mode 100644
index 0000000..34a7cd1
--- /dev/null
+++ b/src/quic_header.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#define QUIC_IS_LONG_HEADER(c) (c & 0x80)
+#define QUIC_IS_FIXED_BIT(c) (c & 0x40)
+
+#define QUIC_PKT_TYPE_INITIAL 0x00
+#define QUIC_PKT_TYPE_0RTT 0x01
+#define QUIC_PKT_TYPE_HANDSHAKE 0x02
+#define QUIC_PKT_TYPE_RETRY 0x03
+
+// https://www.rfc-editor.org/rfc/rfc9000.html#name-long-header-packets
+struct quic_long_header{
+ unsigned char type_specific_bit:4;
+ unsigned char packet_type:2;
+ unsigned char fixed_bit:1;
+ unsigned char header_form:1;
+ unsigned int version;
+}__attribute__((packed));
+
+// https://www.rfc-editor.org/rfc/rfc9000.html#name-initial-packet
+struct quic_initial_pkt_header{
+ unsigned char packet_number_length:2;
+ unsigned char reserved_bits:2;
+ unsigned char packet_type:2;
+ unsigned char fixed_bit:1;
+ unsigned char header_form:1;
+ unsigned int version;
+}__attribute__((packed));
+
+struct quic_short_header{
+ unsigned char toto:5;
+ unsigned char spin_bit:1;
+ unsigned char fixed_bit:1;
+ unsigned char header_form:1;
+}__attribute__((packed)); \ No newline at end of file
diff --git a/src/quic_process.cpp b/src/quic_process.cpp
new file mode 100644
index 0000000..3f94ad7
--- /dev/null
+++ b/src/quic_process.cpp
@@ -0,0 +1,1010 @@
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <MESA/MESA_handle_logger.h>
+#include "quic_entry.h"
+#include "quic_process.h"
+#include "quic_deprotection.h"
+#include "quic_header.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "quic_util.h"
+#include <stellar/stellar.h>
+#include <stellar/session.h>
+#include <stellar/session_mq.h>
+#include <stellar/session_exdata.h>
+#ifdef __cplusplus
+}
+#endif
+
+struct quic_client_hello_msg_hdr
+{
+ uint8_t handshake_type;
+ uint8_t client_hello_len[3];
+ uint16_t tls_version;
+ uint8_t random[32];
+};
+
+static int check_port(const struct quic_param *quic_plugin_env, unsigned short port)
+{
+ for (int i = 0; i < quic_plugin_env->quic_port_num; i++)
+ {
+ if (quic_plugin_env->quic_port_list[i] == port)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int quic_protocol_identify(struct session *sess, struct quic_param *quic_plugin_env)
+{
+ enum session_addr_type addr_type;
+ struct session_addr *saddr = session_get0_addr(sess, &addr_type);
+ unsigned short sport, dport;
+ if (addr_type == SESSION_ADDR_TYPE_IPV4_UDP)
+ {
+ sport = saddr->ipv4.sport;
+ dport = saddr->ipv4.dport;
+ }
+ else if (addr_type == SESSION_ADDR_TYPE_IPV6_UDP)
+ {
+ sport = saddr->ipv6.sport;
+ dport = saddr->ipv6.dport;
+ }
+ else
+ {
+ return 0;
+ }
+ if (0 == (check_port(quic_plugin_env, sport) || check_port(quic_plugin_env, dport)))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+static int gquic_pkn_bit2length(unsigned char bit_value)
+{
+ switch(bit_value)
+ {
+ case 0x30: return 6;
+ case 0x20: return 4;
+ case 0x10: return 2;
+ default:
+ return 1;
+ }
+
+ return 1;
+}
+
+/*
+ for quic , the decrypted payload store in quic_dpt_t, will be freed after quic_decrypting_payload(),
+ so we need to malloc memory and copy them to quic_info.
+*/
+#if 1
+static int copy_extension_tag(const unsigned char *tag_start_pos, int tag_len, qstring *out)
+{
+ if(tag_start_pos!=NULL && tag_len>0)
+ {
+ if(out->iov_base!=NULL)
+ {
+ FREE(out->iov_base);
+ }
+
+ out->iov_base=CALLOC(1, tag_len+1);
+ memcpy((void *)out->iov_base, tag_start_pos, tag_len);
+ out->iov_len = tag_len;
+
+ return tag_len;
+ }
+
+ return 0;
+}
+#else
+static int quic_extension_tag_get0(const unsigned char *tag_start_pos, int tag_len, qstring *out)
+{
+ if(tag_start_pos!=NULL && tag_len>0)
+ {
+ out->iov_len = tag_len;
+ out->iov_base = (void *)tag_start_pos;
+ return tag_len;
+ }
+ return 0;
+}
+#endif
+
+//Source: https://wise2.ipac.caltech.edu/staff/slw/docs/html/varint_8h_source.html
+static int msb2_varint_decode(const unsigned char *buf, long *out)
+{
+ unsigned long val = buf[0] & 0x3f;
+ unsigned int nfollow = 1<<(buf[0]>>6);
+ switch (nfollow-1)
+ {
+ case 7: val = (val << 8) | buf[nfollow - 7]; /*fail through*/
+ case 6: val = (val << 8) | buf[nfollow - 6]; /*fail through*/
+ case 5: val = (val << 8) | buf[nfollow - 5]; /*fail through*/
+ case 4: val = (val << 8) | buf[nfollow - 4]; /*fail through*/
+ case 3: val = (val << 8) | buf[nfollow - 3]; /*fail through*/
+ case 2: val = (val << 8) | buf[nfollow - 2]; /*fail through*/
+ case 1: val = (val << 8) | buf[nfollow-1];
+ case 0: break;
+ }
+ *out=val;
+
+ return nfollow;
+}
+
+/*
+
+//https://docs.google.com/document/d/1FcpCJGTDEMblAs-Bm5TYuqhHyUqeWpqrItw2vkMFsdY/edit
+
+Long Header (used for packets that are sent prior to the completion of version negotiation and establishment of 1-RTT keys):
++-+-+-+-+-+-+-+-+
+|1|1|T|T|R|R|P|P|
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Version (32) |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|DCIL(4)|SCIL(4)|
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Destination Connection ID (0/64) ...
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Source Connection ID (0/64) ...
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Packet Number (8/16/24/32) ...
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+*/
+
+
+static int join_client_hello_frames(const unsigned char *payload, int payload_len, unsigned char *join_payload, int *join_payload_len)
+{
+ int joined_length=0;
+ int payload_offset=0;
+ long frame_type=0, frame_offset=0, frame_length=0;
+
+ for(; payload_offset<payload_len; )
+ {
+ frame_type=payload[payload_offset++]; // Frame Type=1
+ if(frame_type==IQUIC_FRAME_PADDING || frame_type==IQUIC_FRAME_PING)
+ {
+ continue;
+ }
+
+ payload_offset+=msb2_varint_decode((const unsigned char *)(payload+payload_offset), &frame_offset);
+ payload_offset+=msb2_varint_decode((const unsigned char *)(payload+payload_offset), &frame_length);
+
+ if(joined_length+frame_length>(*join_payload_len) || frame_offset<0 || frame_offset+frame_length>payload_len)
+ {
+ return -1;
+ }
+ memcpy(join_payload+frame_offset, payload+payload_offset, frame_length);
+ joined_length+=frame_length;
+ payload_offset+=frame_length;
+ }
+
+ (*join_payload_len)=joined_length;
+
+ return joined_length;
+}
+
+static int parse_gquic_version_44to48_header(const char *payload, int payload_len, int *payload_offset)
+{
+ unsigned pkn_length=0;
+ unsigned char client_CID_len=0;
+ unsigned char server_CID_len=0;
+ unsigned char public_flags=payload[*payload_offset];
+ *payload_offset+=1; //skip public flags
+
+ *payload_offset+=sizeof(int); // skip version
+
+ if((payload[*payload_offset])&GQUIC_VERSION_44to48_CID_MASK)
+ {
+ client_CID_len=(payload[*payload_offset]&GQUIC_VERSION_44to48_CID_MASK) + 3;
+ }
+
+ if((payload[*payload_offset]>>4)&GQUIC_VERSION_44to48_CID_MASK)
+ {
+ server_CID_len=((payload[*payload_offset]>>4)&GQUIC_VERSION_44to48_CID_MASK) + 3;
+ }
+
+ *payload_offset+=1; // both connection_id length
+ *payload_offset+=server_CID_len; // Destination connection id length
+ *payload_offset+=client_CID_len; // source connection id length
+
+ pkn_length=(public_flags&GQUIC_VERSION_44to48_PKN_LEN_MASK)+1;
+ *payload_offset+=pkn_length;
+
+ *payload_offset+=12; //message authentication hash
+
+ return 1;
+}
+
+int parse_special_frame_stream(struct quic_info* quic_info, const unsigned char *payload, int payload_len)
+{
+ int tag_num = 0;
+ int payload_offset=0;
+ unsigned int message_tag;
+ int pass_tag_num=0;
+ int ext_tag_type=0;
+ int tag_value_start_offset=0;
+ int total_tag_len=0,one_tag_len=0;
+ int parse_result=PARSE_RESULT_VERSION;
+ int one_tag_offset_end=0,pre_one_tag_offset_end=0;
+
+ if(payload_len-payload_offset<=8)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+
+ switch(quic_info->quic_version)
+ {
+ case GQUIC_VERSION_Q041:
+ payload_offset+=1; // unknown
+ case GQUIC_VERSION_Q044:
+ message_tag=(unsigned int)ntohl(*(unsigned int *)(payload+payload_offset));
+ payload_offset+=4;
+
+ tag_num=*(int *)(payload+payload_offset);
+ payload_offset+=4; //tag_num
+ break;
+ default:
+ message_tag=(unsigned int)ntohl(*(unsigned int *)(payload+payload_offset));
+ payload_offset+=4;
+
+ tag_num=*(unsigned short *)(payload+payload_offset);
+ payload_offset+=2; //tag_num
+ payload_offset+=2; //padding
+ break;
+ }
+
+ if(message_tag!=CHLO || tag_num>64 || tag_num<=0)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+
+ // if(quic_info->client_hello==NULL)
+ // {
+ // quic_info->client_hello=(struct quic_client_hello *)CALLOC(1, sizeof(struct quic_client_hello));
+ // memset(quic_info->client_hello, 0, sizeof(struct quic_client_hello));
+ // }
+
+ tag_value_start_offset=payload_offset+tag_num*4*2; // skip length of type and offset, type(offset)=szieof(int)
+
+ while(tag_num>pass_tag_num)
+ {
+ ext_tag_type=ntohl(*(unsigned int *)(payload+payload_offset));
+ payload_offset+=sizeof(int);
+
+ one_tag_offset_end=*(unsigned int *)(payload+payload_offset);
+ payload_offset+=sizeof(int);
+
+ one_tag_len=one_tag_offset_end-pre_one_tag_offset_end;
+ if(one_tag_len<=0 || (one_tag_offset_end>=payload_len) || (one_tag_len+tag_value_start_offset)>payload_len)
+ {
+ break;
+ }
+
+ switch(ext_tag_type)
+ {
+ case TAG_UAID:
+ copy_extension_tag(payload+tag_value_start_offset, one_tag_len, &quic_info->user_agent);
+ parse_result=PARSE_RESULT_CLIENT_HELLO;
+ break;
+ case TAG_SNI:
+ copy_extension_tag(payload+tag_value_start_offset, one_tag_len, &quic_info->sni);
+ parse_result=PARSE_RESULT_CLIENT_HELLO;
+ break;
+ default:
+ break;
+ }
+
+ pass_tag_num++;
+ tag_value_start_offset+=one_tag_len;
+ total_tag_len+=one_tag_len;
+ pre_one_tag_offset_end=one_tag_offset_end;
+ }
+
+ return parse_result;
+}
+
+int parse_quic_transport_parameter(struct quic_info *quic_info, const unsigned char *quic_para, int quic_para_len)
+{
+ int one_para_length=0;
+ int para_offset=0;
+ long one_para_type=0;
+
+ while(quic_para_len > para_offset)
+ {
+ para_offset+=msb2_varint_decode((const unsigned char *)(quic_para+para_offset), &one_para_type);
+ switch(one_para_type)
+ {
+ case EXT_QUIC_PARAM_USER_AGENT: // 2021-10-20 deprecated
+ one_para_length=quic_para[para_offset++]; // length=1
+ if(one_para_length+para_offset>quic_para_len)
+ {
+ return 0;
+ }
+ para_offset+=copy_extension_tag(quic_para+para_offset, one_para_length, &quic_info->user_agent);
+ return 1;
+ default:
+ one_para_length=(int)(quic_para[para_offset++]); // length=1
+ if(one_para_length<0 || one_para_length>quic_para_len)
+ {
+ break;
+ }
+ para_offset+=one_para_length;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int parse_extension_server_name(struct quic_info *quic_info, const unsigned char *ext_server_name, int ext_server_name_length)
+{
+ unsigned short sni_type=0;
+ unsigned short sni_length=0;
+ unsigned short extension_offset=0;
+ unsigned short server_name_list_len=0;
+
+ server_name_list_len=ntohs(*(unsigned short *)(ext_server_name+extension_offset)); //Server Name List length
+ if(server_name_list_len<=0 || server_name_list_len>ext_server_name_length)
+ {
+ return 0;
+ }
+ extension_offset+=2; //Server Name List length
+
+ sni_type=ext_server_name[extension_offset++]; //Server Name type
+ if(sni_type!=EXTENSION_SERVER_NAME)
+ {
+ return 0;
+ }
+
+ sni_length=ntohs(*(unsigned short*)(ext_server_name+extension_offset)); //Server Name length
+ if(sni_length<=0 || sni_length>ext_server_name_length)
+ {
+ return 0;
+ }
+ extension_offset+=2;
+
+ copy_extension_tag(ext_server_name+extension_offset, sni_length, &quic_info->sni);
+
+ return 1;
+}
+
+int parse_tls_client_hello(struct quic_info *quic_info, const unsigned char *payload, int payload_len)
+{
+ int ret=0,skip_len=0;
+ int payload_offset=0;
+ int extension_offset=0;
+ const unsigned char *extension_start_pos=NULL;
+ int parse_result=PARSE_RESULT_VERSION;
+ unsigned short one_ext_type=0, one_ext_len=0, extension_total_len=0;
+
+ if(payload_len-payload_offset<=(int)sizeof(struct quic_client_hello_msg_hdr))
+ {
+ return PARSE_RESULT_VERSION;
+ }
+
+ // handshake type(1), client hello length(3), ssl_version(2), Random(32)
+ payload_offset+=sizeof(struct quic_client_hello_msg_hdr);
+
+ skip_len=payload[payload_offset++]; //Session ID length
+ if(payload_len-payload_offset<=skip_len)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+ payload_offset+=skip_len;
+
+ skip_len=ntohs(*(unsigned short *)(payload+payload_offset)); //Ciper Suites length
+ if(payload_len-payload_offset<=skip_len+2)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+ payload_offset+=skip_len+2;
+
+ skip_len=payload[payload_offset++]; //Compression Methods
+ if(payload_len-payload_offset<skip_len)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+ payload_offset+=skip_len;
+
+ extension_total_len=ntohs(*(unsigned short *)(payload+payload_offset)); //Extension length
+ if(payload_len-payload_offset<extension_total_len)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+
+ payload_offset+=2;
+
+ // if(*client_hello==NULL)
+ // {
+ // *client_hello=(struct quic_client_hello *)CALLOC(1, sizeof(struct quic_client_hello));
+ // // memset(*client_hello, 0, sizeof(struct quic_client_hello));
+ // }
+
+ extension_start_pos=payload+payload_offset;
+
+ while(extension_total_len > extension_offset)
+ {
+ one_ext_type=ntohs(*(unsigned short *)(extension_start_pos+extension_offset)); //Extension type
+ extension_offset+=2;
+
+ one_ext_len=ntohs(*(unsigned short *)(extension_start_pos+extension_offset)); //length
+ extension_offset+=2;
+
+ if(extension_total_len-extension_offset<one_ext_len)
+ {
+ break;
+ }
+
+ switch(one_ext_type)
+ {
+ case EXTENSION_SERVER_NAME:
+ ret=parse_extension_server_name(quic_info, extension_start_pos+extension_offset, one_ext_len);
+ break;
+ case EXTENSION_QUIC_PARAM_TLS_13:
+ case EXTENSION_QUIC_PARAM_TLS_33:
+ ret=parse_quic_transport_parameter(quic_info, extension_start_pos+extension_offset, one_ext_len);
+ break;
+ default:
+ break;
+ }
+
+ if(ret==1)
+ {
+ ret=0;
+ parse_result=PARSE_RESULT_CLIENT_HELLO;
+ }
+
+ extension_offset+=one_ext_len;
+ }
+
+ return parse_result;
+}
+
+int get_chlo_total_length(const unsigned char *payload, int payload_len)
+{
+ const struct quic_client_hello_msg_hdr *chlo_hdr=(const struct quic_client_hello_msg_hdr *)payload;
+ int chlo_len=0;
+ uint8_t *p = (uint8_t *)&chlo_len;
+ p[2] = chlo_hdr->client_hello_len[0];
+ p[1] = chlo_hdr->client_hello_len[1];
+ p[0] = chlo_hdr->client_hello_len[2];
+ return chlo_len;
+}
+
+int parse_quic_decrypted_payload(struct quic_info *quic_info, const unsigned char * payload, int payload_len)
+{
+ unsigned char join_payload[MAX_CLIENT_HELLO_CHUNK_SIZE]={0};
+ int join_payload_len=sizeof(join_payload);
+ unsigned int quic_version=quic_info->quic_version;
+
+ if( (quic_version>=MVFST_VERSION_00 && quic_version<=MVFST_VERSION_0F) ||
+ (quic_version>=GQUIC_VERSION_T050 && quic_version<=GQUIC_VERSION_T059) ||
+ (quic_version>=IQUIC_VERSION_I022 && quic_version<=IQUIC_VERSION_I029) ||
+ (quic_version==IQUIC_VERSION_RFC9000)
+ )
+ {
+ join_payload_len=join_client_hello_frames(payload, payload_len, join_payload, &join_payload_len);
+ if(join_payload_len<=0)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+
+ if(join_payload[0] == QUIC_HANDSHAKE_TYPE_CLIENTHELLO)
+ {
+ return parse_tls_client_hello(quic_info, join_payload, join_payload_len);
+ }
+ }
+ else //if(quic_version>=GQUIC_VERSION_Q047 && quic_version<=GQUIC_VERSION_Q059)
+ {
+ return parse_special_frame_stream(quic_info, payload+4, payload_len-4); // Frame type=1,offset=1,length=2
+ }
+
+ return PARSE_RESULT_VERSION;
+}
+
+int parse_quic_uncryption_payload(struct quic_info *quic_info, const unsigned char * payload, int payload_len)
+{
+ int stream_id_len=0;
+ int offset_len=0;
+ unsigned char frame_type;
+ int payload_offset=0;
+
+ frame_type=payload[payload_offset];
+ payload_offset+=1; //skip frame_type
+
+ //https://docs.google.com/document/d/1WJvyZflAO2pq77yOLbp9NsGjC1CHetAXV8I0fQe-B_U/edit#
+ //Frame Type: The Frame Type byte is an 8-bit value containing various flags (1fdooossB):
+ if(frame_type&GQUIC_SPECIAL_FRAME_STREAM || (frame_type&0xC0)==GQUIC_SPECIAL_FRAME_ACK)
+ {
+ stream_id_len=(frame_type&GQUIC_SPECIAL_FRAME_STREAM_ID)+1;
+ if(payload_len-payload_offset<=stream_id_len)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+
+ payload_offset+=stream_id_len; // stream ID length
+
+ if(frame_type&GQUIC_SPECIAL_FRAME_STREAM_DLEN)
+ {
+ if(payload_len-payload_offset<2)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+ payload_offset+=2; //data length
+ }
+
+ if(frame_type&GQUIC_SPECIAL_FRAME_STREAM_OFFSET)
+ {//The next three 'ooo' bits encode the length of the Offset header field as 0, 16, 24, 32, 40, 48, 56, or 64 bits long.
+ offset_len= (((frame_type&GQUIC_SPECIAL_FRAME_STREAM_OFFSET))>>2)+1;
+ if(payload_len-payload_offset<=offset_len)
+ {
+ return PARSE_RESULT_VERSION;
+ }
+ payload_offset+=offset_len; //data length
+ }
+
+ return parse_special_frame_stream(quic_info, payload+payload_offset, payload_len-payload_offset);
+ }
+
+ return PARSE_RESULT_VERSION;
+}
+
+/*
+//https://docs.google.com/document/d/1WJvyZflAO2pq77yOLbp9NsGjC1CHetAXV8I0fQe-B_U/edit
+
+--- src
+ 0 1 2 3 4 8
++--------+--------+--------+--------+--------+--- ---+
+| Public | Connection ID (64) ... | ->
+|Flags(8)| (optional) |
++--------+--------+--------+--------+--------+--- ---+
+
+ 9 10 11 12
++--------+--------+--------+--------+
+| QUIC Version (32) | ->
+| (optional) |
++--------+--------+--------+--------+
+
+
+ 13 14 15 16 17 18 19 20
++--------+--------+--------+--------+--------+--------+--------+--------+
+| Diversification Nonce | ->
+| (optional) |
++--------+--------+--------+--------+--------+--------+--------+--------+
+
+ 21 22 23 24 25 26 27 28
++--------+--------+--------+--------+--------+--------+--------+--------+
+| Diversification Nonce Continued | ->
+| (optional) |
++--------+--------+--------+--------+--------+--------+--------+--------+
+
+ 29 30 31 32 33 34 35 36
++--------+--------+--------+--------+--------+--------+--------+--------+
+| Diversification Nonce Continued | ->
+| (optional) |
++--------+--------+--------+--------+--------+--------+--------+--------+
+
+ 37 38 39 40 41 42 43 44
++--------+--------+--------+--------+--------+--------+--------+--------+
+| Diversification Nonce Continued | ->
+| (optional) |
++--------+--------+--------+--------+--------+--------+--------+--------+
+
+
+ 45 46 47 48 49 50
++--------+--------+--------+--------+--------+--------+
+| Packet Number (8, 16, 32, or 48) |
+| (variable length) |
++--------+--------+--------+--------+--------+--------+
+
+*/
+enum QUIC_VERSION_T identify_gquic_version0to43(const char *payload, int payload_len, int *payload_offset)
+{
+ unsigned char pkn_length=0;
+ unsigned char public_flags=0;
+ enum QUIC_VERSION_T quic_version=QUIC_VERSION_UNKNOWN;
+
+ public_flags=payload[*payload_offset];
+ *payload_offset+=1;
+
+ if(!(public_flags&GQUIC_PUBLIC_FLAG_VERSION))
+ {
+ return QUIC_VERSION_UNKNOWN;
+ }
+
+ /*
+ 0x08 = Indicates the full 8 byte Connection ID is present in the packet.
+ This must be set in all packets until negotiated to a different value for a given direction
+ (e.g., client may request fewer bytes of the Connection ID be presented)
+ */
+ if(public_flags&GQUIC_PUBLIC_FLAG_CID)
+ {
+ *payload_offset+=8; // CID length
+ }
+
+ if(public_flags&GQUIC_PUBLIC_FLAG_VERSION && (*(unsigned char *)(payload+*payload_offset)==0x51))
+ {
+ quic_version=(enum QUIC_VERSION_T)ntohl(*(unsigned int *)(payload+*payload_offset));
+ *payload_offset+=sizeof(int); // skip version
+ }
+
+ if(quic_version<GQUIC_VERSION_Q001 || quic_version>GQUIC_VERSION_Q043)
+ {
+ return QUIC_VERSION_UNKNOWN;
+ }
+
+ pkn_length=gquic_pkn_bit2length(public_flags&GQUIC_VERSION_0to43_PKN_LEN_MASK);
+ *payload_offset+=pkn_length; // packet number length
+
+ if(public_flags==GQUIC_PUBLIC_FLAG_NONCE)
+ {
+ *payload_offset+=32; //diversification nonce
+ }
+
+ // Version 11 reduced the length of null encryption authentication tag from 16 to 12 bytes
+ if(quic_version > GQUIC_VERSION_Q010)
+ {
+ *payload_offset+=12;
+ }
+ else
+ {
+ *payload_offset+=16;
+ }
+
+ // Version 34 removed entropy bits from packets and ACK frames,
+ // removed private flag from packet header and changed the ACK format to specify ranges of packets acknowledged rather than missing ranges.
+ if(quic_version < GQUIC_VERSION_Q034)
+ {
+ *payload_offset+=1; //private flags
+ }
+
+ return quic_version;
+}
+
+enum QUIC_VERSION_T identify_quic_version(const char *payload, int payload_len, int *payload_offset)
+{
+ enum QUIC_VERSION_T quic_version=(enum QUIC_VERSION_T)ntohl(*(unsigned int *)(payload+(*payload_offset+1)));
+ if(quic_version>=GQUIC_VERSION_Q044 && quic_version<=GQUIC_VERSION_Q048)
+ {
+ parse_gquic_version_44to48_header(payload, payload_len, payload_offset);
+ return quic_version;
+ }
+ else if(
+ (quic_version==GQUIC_VERSION_Q099) ||
+ (quic_version==PICOQUIC_VERSION_30) ||
+ (quic_version==PQUIC_VERSION_PROX) ||
+ (quic_version==GQUIC_VERSION_T099) ||
+ (quic_version>=GQUIC_VERSION_Q049 && quic_version<=GQUIC_VERSION_Q050) ||
+ (quic_version>=GQUIC_VERSION_Q051 && quic_version<=GQUIC_VERSION_Q059) ||
+ (quic_version>=GQUIC_VERSION_T048 && quic_version<=GQUIC_VERSION_T049) ||
+ (quic_version>=GQUIC_VERSION_T050 && quic_version<=GQUIC_VERSION_T059) ||
+ (quic_version>=QUANT_VERSION_00 && quic_version<=QUANT_VERSION_FF) ||
+ (quic_version>=QUIC_GO_VERSION_00 && quic_version<=QUIC_GO_VERSION_FF) ||
+ (quic_version>=QUICLY_VERSION_00 && quic_version<=QUICLY_VERSION_FF) ||
+ (quic_version>=MSQUIC_VERSION_00 && quic_version<=MSQUIC_VERSION_0F) ||
+ (quic_version>=MOZQUIC_VERSION_00 && quic_version<=MOZQUIC_VERSION_0F) ||
+ (quic_version>=MVFST_VERSION_00 && quic_version<=MVFST_VERSION_0F) ||
+ (quic_version>=IQUIC_VERSION_I001 && quic_version<=IQUIC_VERSION_I032) ||
+ (quic_version==IQUIC_VERSION_RFC9000)
+ )
+ {
+ return quic_version;
+ }
+
+ return QUIC_VERSION_UNKNOWN;
+}
+
+enum QUIC_VERSION_T is_quic_protocol(const char *payload, int payload_len, int *payload_offset)
+{
+ enum QUIC_VERSION_T quic_version=QUIC_VERSION_UNKNOWN;
+ unsigned char frame_type=(unsigned char)(payload[0]);
+
+ if(payload_len<=4)
+ {
+ return QUIC_VERSION_UNKNOWN;
+ }
+
+ if(frame_type&QUIC_LONG_HEADER_MASK)
+ {
+ quic_version=identify_quic_version(payload, payload_len, payload_offset);
+ }
+ else if(frame_type<QUIC_LONG_HEADER_MASK)
+ {
+ quic_version=identify_gquic_version0to43(payload, payload_len, payload_offset);
+ }
+ else
+ {
+ return QUIC_VERSION_UNKNOWN;
+ }
+
+ return quic_version;
+}
+
+//
+static void quic_chlo_chunk_assemble(struct quic_buffer *qbuf, unsigned char *new_payload, int new_len)
+{
+ if(NULL == new_payload || new_len <= 0){
+ return;
+ }
+ if(NULL == qbuf->buffer){
+ qbuf->buffer = (unsigned char *)calloc(1, MAX_CLIENT_HELLO_CHUNK_SIZE);
+ qbuf->max_size = MAX_CLIENT_HELLO_CHUNK_SIZE;
+ }
+ int max_copy_len = MIN((int)(qbuf->max_size - qbuf->datalen), new_len);
+ memcpy(qbuf->buffer + qbuf->datalen, new_payload, max_copy_len);
+ qbuf->datalen += max_copy_len;
+}
+
+/*
+ 1: is chlo, is complete.
+ 0: is chlo, is fragment;
+ -1: not support
+*/
+static int quic_chlo_is_complete(enum QUIC_VERSION_T quic_version, const unsigned char *payload, int payload_len)
+{
+ if(!( (quic_version>=MVFST_VERSION_00 && quic_version<=MVFST_VERSION_0F) ||
+ (quic_version>=GQUIC_VERSION_T050 && quic_version<=GQUIC_VERSION_T059) ||
+ (quic_version>=IQUIC_VERSION_I022 && quic_version<=IQUIC_VERSION_I029) ||
+ (quic_version==IQUIC_VERSION_RFC9000)))
+ {
+ return -1;
+ }
+
+ if((payload[0] != IQUIC_FRAME_CRYPTO) && (payload[0] != 0x08))
+ {
+ return -1;
+ }
+ int payload_offset = 0;
+ long frame_offset, frame_length;
+ payload_offset = 1; //skip frame type
+ payload_offset+=msb2_varint_decode(payload+payload_offset, &frame_offset);
+ payload_offset+=msb2_varint_decode(payload+payload_offset, &frame_length);
+
+ if(payload[payload_offset] != QUIC_HANDSHAKE_TYPE_CLIENTHELLO)
+ {
+ return -1;
+ }
+ int expect_len = get_chlo_total_length(payload + payload_offset, payload_len-payload_offset);
+ if(payload_len-payload_offset >= expect_len){
+ return 1;
+ }
+ return 0;
+}
+
+static enum PARSE_RESULT quic_decrypting_payload(struct quic_context *qcontext, unsigned char *udp_payload, int udp_payload_len)
+{
+ struct quic_buffer *qbuf = &qcontext->quic_buf;
+ unsigned char *quic_decrypted_payload;
+ int ret, quic_decrypted_payload_len;
+ quic_dpt_t *dpt = quic_deprotection_new();
+
+ if(quic_deprotection(dpt, (const u_char *)udp_payload, udp_payload_len) < 0){
+ goto err_exit;
+ }
+
+ if(qbuf->datalen > 0){ //some fragment exist
+ quic_chlo_chunk_assemble(qbuf, dpt->payload.data, dpt->payload.len);
+ quic_decrypted_payload = qbuf->buffer;
+ quic_decrypted_payload_len = qbuf->datalen;
+ }else{
+ quic_decrypted_payload = dpt->payload.data;
+ quic_decrypted_payload_len = dpt->payload.len;
+ }
+
+ ret = quic_chlo_is_complete((enum QUIC_VERSION_T)qcontext->quic_info.quic_version, quic_decrypted_payload, quic_decrypted_payload_len);
+ if(0 == ret){
+ if(NULL == qbuf->buffer || 0 == qbuf->datalen){
+ quic_chlo_chunk_assemble(qbuf, dpt->payload.data, dpt->payload.len);
+ }
+ goto fun_exit;
+ }else{
+ qbuf->is_completed = 1;
+ }
+ parse_quic_decrypted_payload(&qcontext->quic_info, quic_decrypted_payload, quic_decrypted_payload_len);
+
+fun_exit:
+ quic_deprotection_free(dpt);
+ return PARSE_RESULT_VERSION;
+
+err_exit:
+ quic_deprotection_free(dpt);
+ return PARSE_RESULT_VERSION;
+}
+
+enum PARSE_RESULT parse_quic_all_version(const struct quic_param *g_quic_plugin_env, struct quic_context *qcontext, const char *payload, int payload_len)
+{
+ struct quic_info *quic_info = &qcontext->quic_info;
+ int payload_offset = 0;
+ enum QUIC_VERSION_T quic_version = QUIC_VERSION_UNKNOWN;
+
+ if (payload == NULL || payload_len <= 0)
+ {
+ return PARSE_RESULT_UNKNOWN;
+ }
+ quic_version = is_quic_protocol(payload, payload_len, &payload_offset);
+ if (quic_version == QUIC_VERSION_UNKNOWN)
+ {
+ if(QUIC_IS_LONG_HEADER(payload[0]) == 0){
+ return PARSE_RESULT_PAYLOAD;
+ }
+ return PARSE_RESULT_UNKNOWN;
+ }
+
+ if (quic_version >= GQUIC_VERSION_Q001 && quic_version <= GQUIC_VERSION_Q048)
+ {
+ if (payload_len > payload_offset)
+ {
+ return (enum PARSE_RESULT)parse_quic_uncryption_payload(quic_info, (const unsigned char *)payload + payload_offset, payload_len - payload_offset);
+ }
+ return PARSE_RESULT_VERSION;
+ }
+ else if (((quic_version >= MVFST_VERSION_00 && quic_version <= MVFST_VERSION_0F) ||
+ (quic_version >= GQUIC_VERSION_Q049 && quic_version <= GQUIC_VERSION_Q059) ||
+ (quic_version >= GQUIC_VERSION_T050 && quic_version <= GQUIC_VERSION_T059) ||
+ (quic_version >= GQUIC_VERSION_T050 && quic_version <= GQUIC_VERSION_T059) ||
+ (quic_version >= IQUIC_VERSION_I022 && quic_version <= IQUIC_VERSION_I029) ||
+ (quic_version == IQUIC_VERSION_RFC9000)) &&
+ g_quic_plugin_env->decrypted_switch > 0)
+ {
+ return quic_decrypting_payload(qcontext, (unsigned char *)payload, payload_len);
+ }
+ else
+ {
+ return PARSE_RESULT_VERSION;
+ }
+
+ return PARSE_RESULT_VERSION;
+}
+
+static void quic_info_clear(struct quic_info *qinfo)
+{
+ if(qinfo->sni.iov_base){
+ free(qinfo->sni.iov_base);
+ qinfo->sni.iov_base = NULL;
+ qinfo->sni.iov_len = 0;
+ }
+ if(qinfo->user_agent.iov_base){
+ free(qinfo->user_agent.iov_base);
+ qinfo->user_agent.iov_base = NULL;
+ qinfo->user_agent.iov_len = 0;
+ }
+ if(qinfo->payload.iov_base){
+ free(qinfo->payload.iov_base);
+ qinfo->payload.iov_base = NULL;
+ qinfo->payload.iov_len = 0;
+ }
+}
+
+static void quic_buffer_clear(struct quic_buffer *qbuf)
+{
+ if(qbuf->buffer != NULL && qbuf->is_completed == 1){
+ free(qbuf->buffer);
+ qbuf->buffer = NULL;
+ qbuf->datalen = 0;
+ qbuf->max_size = 0;
+ qbuf->is_completed = 0;
+ }
+}
+
+void quic_analyze_entry(struct session *sess, const struct quic_param *quic_plugin_env, struct quic_context *qcontext, int thread_seq, const char *payload, size_t payload_len)
+{
+ struct quic_message *qmsg;
+ enum PARSE_RESULT res;
+
+ quic_info_clear(&qcontext->quic_info);
+ quic_buffer_clear(&qcontext->quic_buf);
+
+ res = parse_quic_all_version(quic_plugin_env, qcontext, payload, payload_len);
+ if (res == PARSE_RESULT_UNKNOWN){
+ stellar_session_plugin_dettach_current_session(sess);
+ return;
+ }
+
+ if(res == PARSE_RESULT_PAYLOAD){
+ qmsg = quic_create_message(QUIC_PROTECTED_PAYLOAD, qcontext);
+ quic_session_mq_publish_message_safe(sess, quic_plugin_env->quic_topic_id, qmsg);
+ return;
+ }
+
+ if(qcontext->quic_info.sni.iov_base || qcontext->quic_info.user_agent.iov_base){
+ qmsg = quic_create_message(QUIC_CLIENT_HELLO, qcontext);
+ quic_session_mq_publish_message_safe(sess, quic_plugin_env->quic_topic_id, qmsg);
+ }
+ return;
+}
+
+void quic_session_mq_publish_message_safe(struct session *sess, int topic_id, void *msg)
+{
+ int ret = session_mq_publish_message(sess, topic_id, msg);
+ if(ret < 0){
+ FREE(msg);
+ }
+ return;
+}
+struct quic_message *quic_create_message(enum quic_message_type mtype, struct quic_context *context)
+{
+ struct quic_message *msg = (struct quic_message *)CALLOC(1, sizeof(struct quic_message));
+ msg->magic = QUIC_MSG_HDR_MAGIC;
+ msg->type = mtype;
+ msg->qctx = context;
+ return msg;
+}
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+enum quic_message_type quic_message_type_get(const struct quic_message *msg)
+{
+ assert(QUIC_MSG_HDR_MAGIC == msg->magic);
+ if(QUIC_MSG_HDR_MAGIC != msg->magic){
+ return QUIC_MSG_MAX;
+ }
+ return msg->type;
+}
+
+unsigned int quic_message_version_get(const struct quic_message *msg)
+{
+ assert(QUIC_MSG_HDR_MAGIC == msg->magic);
+ return msg->qctx->quic_info.quic_version;
+}
+
+const char *quic_message_readable_version_get0(const struct quic_message *msg)
+{
+ assert(QUIC_MSG_HDR_MAGIC == msg->magic);
+ quic_version_int2string(msg->qctx->quic_info.quic_version, msg->qctx->quic_info.quic_version_str, QUIC_VERSION_STRING_MAX_SIZE);
+ return msg->qctx->quic_info.quic_version_str;
+}
+
+const char *quic_message_readable_ja3hash_get0(const struct quic_message *msg)
+{
+ //todo
+ return NULL;
+}
+
+void quic_message_sni_get0(const struct quic_message *msg, char **result, size_t *len)
+{
+ assert(QUIC_MSG_HDR_MAGIC == msg->magic);
+ if(result){
+ *result = (char *)msg->qctx->quic_info.sni.iov_base;
+ }
+ if(len){
+ *len = msg->qctx->quic_info.sni.iov_len;
+ }
+}
+void quic_message_user_agent_get0(const struct quic_message *msg, char **result, size_t *len)
+{
+ assert(QUIC_MSG_HDR_MAGIC == msg->magic);
+ if(result){
+ *result = (char *)msg->qctx->quic_info.user_agent.iov_base;
+ }
+ if(len){
+ *len = msg->qctx->quic_info.user_agent.iov_len;
+ }
+}
+
+void quic_message_protected_payload_get0(const struct quic_message *msg, char **result, size_t *len)
+{
+ assert(QUIC_MSG_HDR_MAGIC == msg->magic);
+ if(result){
+ *result = (char *)msg->qctx->quic_info.payload.iov_base;
+ }
+ if(len){
+ *len = msg->qctx->quic_info.payload.iov_len;
+ }
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/quic_process.h b/src/quic_process.h
new file mode 100644
index 0000000..4bb66c8
--- /dev/null
+++ b/src/quic_process.h
@@ -0,0 +1,346 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "quic_decoder.h"
+#include "quic_entry.h"
+
+#define QUIC_LONG_HEADER_MASK 0x80
+
+#define GQUIC_VERSION_44to48_CID_MASK 0x0F
+#define GQUIC_VERSION_44to48_PKN_LEN_MASK 0x03
+#define GQUIC_VERSION_0to43_PKN_LEN_MASK 0x30
+
+#define GQUIC_PUBLIC_FLAG_VERSION 0x01
+#define GQUIC_PUBLIC_FLAG_RST 0x02
+#define GQUIC_PUBLIC_FLAG_NONCE 0x04
+#define GQUIC_PUBLIC_FLAG_CID 0x08
+#define GQUIC_PUBLIC_FLAG_PKT_NUM 0x30
+
+//GQIIC Frame type
+#define GQUIC_SPECIAL_FRAME_FLAG 0xE0 // Special Frame Types
+#define GQUIC_SPECIAL_FRAME_STREAM 0x80
+#define GQUIC_SPECIAL_FRAME_ACK 0x40
+#define GQUIC_SPECIAL_FRAME_CONGEST_FB 0x20
+
+#define GQUIC_SPECIAL_FRAME_STREAM_FIN 0x40 // FIN
+#define GQUIC_SPECIAL_FRAME_STREAM_DLEN 0x20 //stream length
+#define GQUIC_SPECIAL_FRAME_STREAM_OFFSET 0x1C //offset header field
+#define GQUIC_SPECIAL_FRAME_STREAM_ID 0x03 //offset header field
+
+#define GQUIC_REGULAR_FRAME_PADDING 0x00
+#define GQUIC_REGULAR_FRAME_RST_STREAM 0x01
+#define GQUIC_REGULAR_FRAME_CONNECTION_CLOSE 0x02
+#define GQUIC_REGULAR_FRAME_GOAWAY 0x03
+#define GQUIC_REGULAR_FRAME_WINDOW_UPDATE 0x04
+#define GQUIC_REGULAR_FRAME_BLOCKED 0x05
+#define GQUIC_REGULAR_FRAME_STOP_WAITING 0x06
+#define GQUIC_REGULAR_FRAME_PING 0x07
+
+// https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types
+#define IQUIC_FRAME_PADDING 0x00
+#define IQUIC_FRAME_PING 0x01
+#define IQUIC_FRAME_ACK 0x02
+#define IQUIC_FRAME_RST_STREAM 0x03
+#define IQUIC_FRAME_STOP_WAITING 0x04
+#define IQUIC_FRAME_CRYPTO 0x06
+#define IQUIC_FRAME_NEW_TOKEN 0x07
+#define IQUIC_FRAME_STREAM 0x08
+#define IQUIC_FRAME_MAX_DATA 0x10
+#define IQUIC_FRAME_MAX_STREAM_DATA 0x11
+#define IQUIC_FRAME_MAX_STREAMS 0x12
+#define IQUIC_FRAME_DATA_BLOCKED 0x13
+#define IQUIC_FRAME_STREAM_DATA_BLOCKED 0x14
+#define IQUIC_FRAME_STREAMS_BLOCKED 0x15
+#define IQUIC_FRAME_NEW_CONNECTION_ID 0x18
+#define IQUIC_FRAME_RETIRE_CONNECTION_ID 0x19
+#define IQUIC_FRAME_PATH_CHALLENGE 0x1A
+#define IQUIC_FRAME_PATH_RESPONSE 0x1B
+#define IQUIC_FRAME_CONNECTION_CLOSE 0x1C
+#define IQUIC_FRAME_APPLICATION_CLOSE 0x1D
+#define IQUIC_FRAME_HANDSHAKE_DONE 0x1E
+
+#define QUIC_HANDSHAKE_TYPE_CLIENTHELLO 0x01
+
+//https://datatracker.ietf.org/doc/html/draft-ietf-quic-transport-27#section-12.4
+//IQIIC Frame type (GQUIC_Q046 is iQUIC 17)
+
+
+/**************************************************************************/
+/* Message tag */
+/**************************************************************************/
+#define CHLO 0x43484C4F
+#define SHLO 0x53484C4F
+#define REJ 0x52454A00
+#define PRST 0x50525354
+
+
+/**************************************************************************/
+/* Tag */
+/**************************************************************************/
+#define TAG_SNI 0x534E4900
+#define TAG_VER 0x56455200
+#define TAG_UAID 0x55414944
+
+
+#define EXTENSION_SERVER_NAME 0x0000
+#define EXTENSION_SUPPORT_GROUP 0x000A
+#define EXTENSION_APP_PROT_NEGO 0x0010 //application layer protocol negotiation
+#define EXTENSION_SIG_ALGORITHM 0x000D
+#define EXTENSION_KEY_SHARE 0x0033
+#define EXTENSION_PSK_EXCHANGE 0x002D
+#define EXTENSION_SUPP_SSL_VER 0x002B
+#define EXTENSION_QUIC_PARAM_TLS_33 0x0039 /* draft-ietf-quic-tls-33 */
+#define EXTENSION_QUIC_PARAM_TLS_13 0xFFA5 /* 0xffa5 draft-ietf-quic-tls-13 */
+#define EXTENSION_COMPRESS_CERT 0x001B
+#define EXTENTION_UNKNOWN 0x4469
+
+// https://www.iana.org/assignments/quic/quic.xhtml
+#define EXT_QUIC_PARAM_ORIGINAL_DST_CONN_ID 0x00
+#define EXT_QUIC_PARAM_MAX_IDLE_TIMEOUT 0x01
+#define EXT_QUIC_PARAM_STATELESS_RST_TOKEN 0x02
+#define EXT_QUIC_PARAM_MAX_UDP_PAYLOAD 0x03
+#define EXT_QUIC_PARAM_MAX_INIT_DATA 0x04
+#define EXT_QUIC_PARAM_MAX_STREAM_BIDI_LOCAL 0x05
+#define EXT_QUIC_PARAM_MAX_STREAM_BIDI_REMOTE 0x06
+#define EXT_QUIC_PARAM_MAX_STREAM_UNI 0x07
+#define EXT_QUIC_PARAM_MAX_STREAMS_BIDI 0x08
+#define EXT_QUIC_PARAM_MAX_STREAMS_UNI 0x09
+#define EXT_QUIC_PARAM_ACK_DELAY_EXPONENT 0x0A
+#define EXT_QUIC_PARAM_MAX_ACK_DELAY 0x0B
+#define EXT_QUIC_PARAM_DISABLE_ACTIVE_MIGRATION 0x0C
+#define EXT_QUIC_PARAM_PREFERRED_ADDRESS 0x0D
+#define EXT_QUIC_PARAM_ACTIVE_CONN_ID_LINIT 0x0E
+#define EXT_QUIC_PARAM_INIT_SRC_CONN_ID 0x0F
+#define EXT_QUIC_PARAM_RETRY_SRC_CONN_ID 0x10
+#define EXT_QUIC_PARAM_MAX_DATAGRAM_FRAME_SIZE 0x20
+#define EXT_QUIC_PARAM_INIT_RTT 0x3127
+#define EXT_QUIC_PARAM_GOOGLE_CONN_OPTIONS 0x3128
+#define EXT_QUIC_PARAM_USER_AGENT 0x3129 // 2021-10-20 deprecated
+#define EXT_QUIC_PARAM_QUIC_VERSION 0x4752
+
+//https://github.com/quicwg/base-drafts/wiki/QUIC-Versions
+/*
+ https://www.chromium.org/quic/quic-faq/
+ In June of 2021, Chromium defaults to supporting IETF QUIC draft29 and gQUIC Q050.
+*/
+enum QUIC_VERSION_T
+{
+ QUIC_VERSION_UNKNOWN=0,
+ //NetApp
+ QUANT_VERSION_00=0x45474700,
+ QUANT_VERSION_FF=0x454747FF,
+
+ //Private Octopus
+ PICOQUIC_VERSION_30=0x50435130,
+
+ //google
+ GQUIC_VERSION_Q001=0x51303031,
+ GQUIC_VERSION_Q002=0x51303032,
+ GQUIC_VERSION_Q003=0x51303033,
+ GQUIC_VERSION_Q004=0x51303034,
+ GQUIC_VERSION_Q005=0x51303035,
+ GQUIC_VERSION_Q006=0x51303036,
+ GQUIC_VERSION_Q007=0x51303037,
+ GQUIC_VERSION_Q008=0x51303038,
+ GQUIC_VERSION_Q009=0x51303039,
+
+ GQUIC_VERSION_Q010=0x51303130,
+ GQUIC_VERSION_Q011=0x51303131,
+ GQUIC_VERSION_Q012=0x51303132,
+ GQUIC_VERSION_Q013=0x51303133,
+ GQUIC_VERSION_Q014=0x51303134,
+ GQUIC_VERSION_Q015=0x51303135,
+ GQUIC_VERSION_Q016=0x51303136,
+ GQUIC_VERSION_Q017=0x51303137,
+ GQUIC_VERSION_Q018=0x51303138,
+ GQUIC_VERSION_Q019=0x51303139,
+
+ GQUIC_VERSION_Q020=0x51303230,
+ GQUIC_VERSION_Q021=0x51303231,
+ GQUIC_VERSION_Q022=0x51303232,
+ GQUIC_VERSION_Q023=0x51303233,
+ GQUIC_VERSION_Q024=0x51303234,
+ GQUIC_VERSION_Q025=0x51303235,
+ GQUIC_VERSION_Q026=0x51303236,
+ GQUIC_VERSION_Q027=0x51303237,
+ GQUIC_VERSION_Q028=0x51303238,
+ GQUIC_VERSION_Q029=0x51303239,
+
+ GQUIC_VERSION_Q030=0x51303330,
+ GQUIC_VERSION_Q031=0x51303331,
+ GQUIC_VERSION_Q032=0x51303332,
+ GQUIC_VERSION_Q033=0x51303333,
+ GQUIC_VERSION_Q034=0x51303334,
+ GQUIC_VERSION_Q035=0x51303335,
+ GQUIC_VERSION_Q036=0x51303336,
+ GQUIC_VERSION_Q037=0x51303337,
+ GQUIC_VERSION_Q038=0x51303338,
+ GQUIC_VERSION_Q039=0x51303339,
+
+ GQUIC_VERSION_Q040=0x51303430,
+ GQUIC_VERSION_Q041=0x51303431,
+ GQUIC_VERSION_Q042=0x51303432,
+ GQUIC_VERSION_Q043=0x51303433,
+ GQUIC_VERSION_Q044=0x51303434,
+ GQUIC_VERSION_Q045=0x51303435,
+ GQUIC_VERSION_Q046=0x51303436,
+ GQUIC_VERSION_Q047=0x51303437,
+ GQUIC_VERSION_Q048=0x51303438,
+ GQUIC_VERSION_Q049=0x51303439,
+
+ GQUIC_VERSION_Q050=0x51303530,
+ GQUIC_VERSION_Q051=0x51303531,
+ GQUIC_VERSION_Q052=0x51303532,
+ GQUIC_VERSION_Q053=0x51303533,
+ GQUIC_VERSION_Q054=0x51303534,
+ GQUIC_VERSION_Q055=0x51303535,
+ GQUIC_VERSION_Q056=0x51303536,
+ GQUIC_VERSION_Q057=0x51303537,
+ GQUIC_VERSION_Q058=0x51303538,
+ GQUIC_VERSION_Q059=0x51303539,
+
+ GQUIC_VERSION_Q099=0x51303939,
+
+ //Google QUIC with TLS 48 - 49 (T048 - T049)
+ GQUIC_VERSION_T048=0x54303438,
+ GQUIC_VERSION_T049=0x54303439,
+
+ //Google QUIC with TLS 50 - 59 (T050 - T059)
+ GQUIC_VERSION_T050=0x54303530,
+ GQUIC_VERSION_T051=0x54303531,
+ GQUIC_VERSION_T052=0x54303532,
+ GQUIC_VERSION_T053=0x54303533,
+ GQUIC_VERSION_T054=0x54303534,
+ GQUIC_VERSION_T055=0x54303535,
+ GQUIC_VERSION_T056=0x54303536,
+ GQUIC_VERSION_T057=0x54303537,
+ GQUIC_VERSION_T058=0x54303538,
+ GQUIC_VERSION_T059=0x54303539,
+
+ //Google QUIC with TLS 99 (T099)
+ GQUIC_VERSION_T099=0x54303939,
+
+ //Google Proxied QUIC
+ PQUIC_VERSION_PROX=0x50524f58,
+
+ //quic-go
+ QUIC_GO_VERSION_00=0x51474F00,
+ QUIC_GO_VERSION_FF=0x51474FFF,
+
+ //quicly
+ QUICLY_VERSION_00=0x91c17000,
+ QUICLY_VERSION_FF=0x91c170FF,
+
+ //Microsoft
+ MSQUIC_VERSION_00=0xabcd0000,
+ MSQUIC_VERSION_0F=0xabcd000F,
+
+ //Mozilla
+ MOZQUIC_VERSION_00=0xf123f0c0,
+ MOZQUIC_VERSION_0F=0xf123f0cF,
+
+ //Facebook
+ MVFST_VERSION_00=0xfaceb000,
+ MVFST_VERSION_01=0xfaceb001,
+ MVFST_VERSION_02=0xfaceb002,
+ MVFST_VERSION_03=0xfaceb003,
+ MVFST_VERSION_04=0xfaceb004,
+ MVFST_VERSION_05=0xfaceb005,
+ MVFST_VERSION_06=0xfaceb006,
+ MVFST_VERSION_07=0xfaceb007,
+ MVFST_VERSION_08=0xfaceb008,
+ MVFST_VERSION_09=0xfaceb009,
+ MVFST_VERSION_0A=0xfaceb00A,
+ MVFST_VERSION_0B=0xfaceb00B,
+ MVFST_VERSION_0C=0xfaceb00C,
+ MVFST_VERSION_0D=0xfaceb00D,
+ MVFST_VERSION_0E=0xfaceb00E,
+ MVFST_VERSION_0F=0xfaceb00F,
+
+ //IETF
+ IQUIC_VERSION_RFC9000=0x00000001,
+ IQUIC_VERSION_I001=0xFF000001,
+ IQUIC_VERSION_I002=0xFF000002,
+ IQUIC_VERSION_I003=0xFF000003,
+ IQUIC_VERSION_I004=0xFF000004,
+ IQUIC_VERSION_I005=0xFF000005,
+ IQUIC_VERSION_I006=0xFF000006,
+ IQUIC_VERSION_I007=0xFF000007,
+ IQUIC_VERSION_I008=0xFF000008,
+ IQUIC_VERSION_I009=0xFF000009,
+ IQUIC_VERSION_I010=0xFF00000A,
+ IQUIC_VERSION_I011=0xFF00000B,
+ IQUIC_VERSION_I012=0xFF00000C,
+ IQUIC_VERSION_I013=0xFF00000D,
+ IQUIC_VERSION_I014=0xFF00000E,
+ IQUIC_VERSION_I015=0xFF00000F,
+ IQUIC_VERSION_I016=0xFF000010,
+ IQUIC_VERSION_I017=0xFF000011,
+ IQUIC_VERSION_I018=0xFF000012,
+ IQUIC_VERSION_I019=0xFF000013,
+ IQUIC_VERSION_I020=0xFF000014,
+ IQUIC_VERSION_I021=0xFF000015,
+ IQUIC_VERSION_I022=0xFF000016,
+ IQUIC_VERSION_I023=0xFF000017,
+ IQUIC_VERSION_I024=0xFF000018,
+ IQUIC_VERSION_I025=0xFF000019,
+ IQUIC_VERSION_I026=0xFF00001A,
+ IQUIC_VERSION_I027=0xFF00001B,
+ IQUIC_VERSION_I028=0xFF00001C,
+ IQUIC_VERSION_I029=0xFF00001D,
+ IQUIC_VERSION_I030=0xFF00001E,
+ IQUIC_VERSION_I031=0xFF00001F,
+ IQUIC_VERSION_I032=0xFF000020
+};
+
+struct quic_info
+{
+ unsigned int quic_version;
+ char quic_version_str[QUIC_VERSION_STRING_MAX_SIZE];
+ qstring sni;
+ qstring user_agent;
+ qstring payload;
+};
+
+#define MAX_CLIENT_HELLO_CHUNK_SIZE (4096)
+struct quic_buffer{
+ int is_completed;
+ unsigned char *buffer;
+ size_t max_size;
+ size_t datalen;
+};
+
+struct quic_context
+{
+ unsigned int parse_payload_size;
+ struct quic_info quic_info;
+ struct quic_buffer quic_buf; //for client hello fragment
+};
+
+#define QUIC_MSG_HDR_MAGIC 0x51554943 // ASCII: "QUIC"
+struct quic_message
+{
+ unsigned int magic;
+ enum quic_message_type type;
+ struct quic_context *qctx;
+};
+
+enum PARSE_RESULT
+{
+ PARSE_RESULT_UNKNOWN,
+ PARSE_RESULT_VERSION,
+ PARSE_RESULT_CLIENT_HELLO,
+ PARSE_RESULT_PAYLOAD,
+ PARSE_RESULT_MAX
+};
+
+enum PARSE_RESULT parse_quic_all_version(const struct quic_param *g_quic_plugin_env, struct quic_info *quic_info, const char *payload, int payload_len, int thread_seq);
+void quic_analyze_entry(struct session *sess, const struct quic_param *g_quic_plug_env, struct quic_context* context, int thread_seq, const char *payload, size_t payload_len);
+// unsigned char quic_call_business_plug(const struct streaminfo *pstream, struct quic_context *context, void *buff, int buff_len, enum quic_interested_region region_mask, const void *a_packet);
+int quic_protocol_identify(struct session *sess, struct quic_param *g_quic_plug_env);
+struct quic_message *quic_create_message(enum quic_message_type mtype, struct quic_context *context);
+void quic_session_mq_publish_message_safe(struct session *sess, int topic_id, void *msg);
+enum QUIC_VERSION_T is_quic_protocol(const char *payload, int payload_len, int *payload_offset);
+int quic_version_int2string(unsigned int version, char *buff, int buff_len); \ No newline at end of file
diff --git a/src/quic_util.h b/src/quic_util.h
new file mode 100644
index 0000000..e147e23
--- /dev/null
+++ b/src/quic_util.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define CALLOC(nmemb, size) calloc(nmemb, size)
+#define REALLOC(ptr, newsize) realloc(ptr, newsize)
+#define FREE(p) {free((void *)p); p = NULL;} \ No newline at end of file
diff --git a/src/quic_version.cpp b/src/quic_version.cpp
new file mode 100644
index 0000000..1bec0bb
--- /dev/null
+++ b/src/quic_version.cpp
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#include "quic_process.h"
+
+int quic_version_int2string(unsigned int version, char *buff, int buff_len)
+{
+ if(version>=GQUIC_VERSION_Q001 && version<=GQUIC_VERSION_Q099)
+ {
+ snprintf(buff, buff_len, "Google QUIC %02d", (((version>>8)&0x0000000F)*10) + (version&0x0000000F));
+ return 1;
+ }
+
+ if(version>=GQUIC_VERSION_T048 && version<=GQUIC_VERSION_T099)
+ {
+ snprintf(buff, buff_len, "Google QUIC with TLS %02d", (((version>>8)&0x0000000F)*10) + (version&0x0000000F));
+ return 1;
+ }
+
+
+ if(version==IQUIC_VERSION_RFC9000)
+ {
+ snprintf(buff, buff_len, "IETF QUIC RFC9000");
+ return 1;
+ }
+
+ if(version>=IQUIC_VERSION_I001 && version<=IQUIC_VERSION_I032)
+ {
+ snprintf(buff, buff_len, "IETF QUIC %02d", (((version>>16)&0x000000FF)*10) + (((version>>8)&0x000000FF)*10) + (version&0x000000FF));
+ return 1;
+ }
+
+ if(version>=QUANT_VERSION_00 && version<=QUANT_VERSION_FF)
+ {
+ snprintf(buff, buff_len, "NetApp QUANT %02d", (version&0x000000FF));
+ return 1;
+ }
+
+ if(version==PICOQUIC_VERSION_30)
+ {
+ snprintf(buff, buff_len, "Private Octopus");
+ return 1;
+ }
+
+ if(version==PQUIC_VERSION_PROX)
+ {
+ snprintf(buff, buff_len, "Proxied QUIC");
+ return 1;
+ }
+
+ if(version>=QUIC_GO_VERSION_00 && version<=QUIC_GO_VERSION_FF)
+ {
+ snprintf(buff, buff_len, "quic-go QGO %02d", (version&0x000000FF));
+ return 1;
+ }
+
+ if(version>=QUIC_GO_VERSION_00 && version<=QUIC_GO_VERSION_FF)
+ {
+ snprintf(buff, buff_len, "quicly qicly0 %02d", (version&0x000000FF));
+ return 1;
+ }
+
+ if(version>=MSQUIC_VERSION_00 && version<=MSQUIC_VERSION_0F)
+ {
+ snprintf(buff, buff_len, "Microsoft MsQuic %02d", (version&0x0000000F));
+ return 1;
+ }
+
+ if(version>=MOZQUIC_VERSION_00 && version<=MOZQUIC_VERSION_0F)
+ {
+ snprintf(buff, buff_len, "Mozilla MozQuic %02d", (version&0x0000000F));
+ return 1;
+ }
+
+ if(version>=MVFST_VERSION_00&& version<=MVFST_VERSION_0F)
+ {
+ snprintf(buff, buff_len, "Facebook mvfst %02d", (version&0x0000000F));
+ return 1;
+ }
+
+ return 0;
+}
+ \ No newline at end of file
diff --git a/src/version.map b/src/version.map
new file mode 100644
index 0000000..db590f0
--- /dev/null
+++ b/src/version.map
@@ -0,0 +1,10 @@
+VERS_3.0{
+global:
+ extern "C" {
+ QUIC_ONLOAD;
+ QUIC_UNLOAD;
+ quic_version_int2string;
+ quic_message_*;
+ };
+ local: *;
+};