]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/quic: various smaller changes, improvements and fixes aggregated into a single...
authorFrantisek Tobias <frantisek.tobias@nic.cz>
Tue, 23 Jun 2026 13:05:26 +0000 (15:05 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 22 Jul 2026 04:50:45 +0000 (06:50 +0200)
list of changes:
- lower default concurrent stream limit 1024 to 16
- rename quic_idle_timeout to quic_handle_timeout
- use PTO for closing/draining state timeouts
- move kr_quic_table_rem2 to quic_common.c
- set_tls_error for tls alerts (in conn close messages with error type
  TRANSPORT)
- add some function documentation to quic_*.h
- improve/fix error messages in conn close packets
- fix and simplify tls alert transport when conn close is sent
- fix cid handling
- call uv_close on the timers in sub-sessions
- add QUIC_SEND_NONE for read errors which forbid/do not need a reply
- quic_bye for closing conns which are in states <= CLOSING (i.e. conn
  that can still send)
- unite handshake timer with ngtcp2 driver timer loop
- add a custom state for failed handshake - QUIC_STATE_HS_ABORT
- handle cid removal from the table for conns which fail before their
  ngtcp2_conn is created
- kr_quic_table_sweep is no longer used, termination handled complately
  by timers
- failed attempt to create ngtcp2_conn now causes the conn session to be
  destroyed immediately

daemon/quic_common.c
daemon/quic_common.h
daemon/quic_conn.c
daemon/quic_conn.h
daemon/quic_demux.c
daemon/quic_stream.c
daemon/quic_stream.h
daemon/session2.c
daemon/session2.h
doc/_static/config.schema.json
python/knot_resolver/datamodel/network_schema.py

index c5d15ee4524638826e23b0874114a9ba725d8c61..549ce80a5d1a6741bfa1af2fd15dd924e5805714 100644 (file)
@@ -30,19 +30,16 @@ int quic_configuration_set(void)
        the_network->quic_params = quic_params;
        /* Default values */
        the_network->quic_params->require_retry = false;
-       the_network->quic_params->max_streams = 1024;
+       the_network->quic_params->max_streams = 16;
        the_network->quic_params->max_conns = 1024;
        return kr_ok();
 }
 
 int quic_configuration_free(struct net_quic_params *quic_params)
 {
-       if (quic_params == NULL){
-               return kr_ok();
+       if (quic_params{
+               free(quic_params);
        }
-
-       free(quic_params);
-
        return kr_ok();
 }
 
@@ -66,6 +63,17 @@ bool kr_quic_conn_timeout(struct pl_quic_conn_sess_data *conn, uint64_t *now)
        return *now > ngtcp2_conn_get_expiry(conn->conn);
 }
 
+inline uint64_t quic_ns_to_ms_ceil(uint64_t ns)
+{
+       return (ns + NGTCP2_MILLISECONDS - 1) / NGTCP2_MILLISECONDS;
+}
+
+inline uint64_t quic_get_closing_timeout(uint64_t pto_ns)
+{
+       /* see: https://nghttp2.org/ngtcp2/programmers-guide.html#the-closing-and-draining-state */
+       return 3 * quic_ns_to_ms_ceil(pto_ns);
+}
+
 void quic_hs_timeout(uv_timer_t *timer)
 {
        struct session2 *s = timer->data;
@@ -76,11 +84,20 @@ void quic_hs_timeout(uv_timer_t *timer)
        session2_event(conn->h.session, PROTOLAYER_EVENT_CONNECT_TIMEOUT, NULL);
 }
 
-void quic_idle_timeout(uv_timer_t *timer)
+void quic_handle_timeout(uv_timer_t *timer)
 {
        struct session2 *s = timer->data;
        struct pl_quic_conn_sess_data *conn =
                protolayer_sess_data_get_proto(s, PROTOLAYER_TYPE_QUIC_CONN);
+
+       if (conn->state & QUIC_STATE_CLOSING) {
+               session2_close(conn->h.session);
+               return;
+       } else if (conn->state & QUIC_STATE_DRAINING) {
+               session2_force_close(conn->h.session);
+               return;
+       }
+
        uint64_t now = quic_timestamp();
        int ret = ngtcp2_conn_handle_expiry(conn->conn, now);
        if (ret != 0) {
@@ -88,9 +105,9 @@ void quic_idle_timeout(uv_timer_t *timer)
                                ngtcp2_strerror(ret), ret);
        }
        if (ret == NGTCP2_ERR_IDLE_CLOSE) {
-               /* idle equal max_idle_timeout, don't send CONNECTION_CLOSE */
                session2_force_close(s);
        } else if (ret < 0) {
+               QUIC_SET_CLOSING(conn);
                session2_close(s);
        } else {
                quic_flush_streams(conn);
@@ -113,7 +130,7 @@ int quic_set_hs_timeout(struct session2 *s, uint64_t ms)
 
 int quic_set_idle_timeout(struct session2 *s, uint64_t ms)
 {
-       return quic_set_timeout(s, ms, quic_idle_timeout);
+       return quic_set_timeout(s, ms, quic_handle_timeout);
 }
 
 void quic_reset_expiry(struct pl_quic_conn_sess_data *conn)
@@ -124,27 +141,35 @@ void quic_reset_expiry(struct pl_quic_conn_sess_data *conn)
 
        uv_timer_stop(&s->timer);
        if (expiry == UINT64_MAX) {
-               uv_timer_start(&s->timer, quic_idle_timeout,
+               uv_timer_start(&s->timer, quic_handle_timeout,
                                QUIC_MAX_IDLE_TIMEOUT, 0);
                return;
        }
        uint64_t now = quic_timestamp();
        if (expiry > now) {
-               uint64_t ceil_delay_ns = expiry - now + NGTCP2_MILLISECONDS - 1;
-               uint64_t delay_ms = ceil_delay_ns / NGTCP2_MILLISECONDS;
-               uv_timer_start(&s->timer, quic_idle_timeout, delay_ms, 0);
+               uv_timer_start(&s->timer, quic_handle_timeout,
+                               quic_ns_to_ms_ceil(expiry - now), 0);
        } else {
-               uv_timer_start(&s->timer, quic_idle_timeout, 0, 0);
+               uv_timer_start(&s->timer, quic_handle_timeout, 0, 0);
        }
 }
 
-void init_random_cid(ngtcp2_cid *cid, size_t len)
+int init_random_cid(ngtcp2_cid *cid, size_t len)
 {
        if (len == 0)
                len = SERVER_DEFAULT_SCIDLEN;
 
-       cid->datalen = dnssec_random_buffer(cid->data, len) ==
-               /* DNSSEC_EOK */0 ? len : 0;
+       uint8_t buf[32];
+       if (len > sizeof(buf)) {
+               len = sizeof(buf);
+       }
+
+       int ret;
+       if ((ret = dnssec_random_buffer(buf, len)) == 0) {
+               ngtcp2_cid_init(cid, buf, len);
+       }
+
+       return ret;
 }
 
 uint64_t cid2hash(const ngtcp2_cid *cid, kr_quic_table_t *table)
@@ -157,19 +182,22 @@ uint64_t cid2hash(const ngtcp2_cid *cid, kr_quic_table_t *table)
        return ret;
 }
 
-kr_quic_cid_t **kr_quic_table_lookup2(const ngtcp2_cid *cid, kr_quic_table_t *table)
+kr_quic_cid_t **kr_quic_table_lookup2(const ngtcp2_cid *cid,
+               kr_quic_table_t *table)
 {
        uint64_t hash = cid2hash(cid, table);
 
        kr_quic_cid_t **res = table->conns + (hash % table->size);
-       while (*res != NULL && !ngtcp2_cid_eq(cid, (const ngtcp2_cid *)(*res)->cid_placeholder)) {
+       while (*res != NULL && !ngtcp2_cid_eq(cid,
+                               (const ngtcp2_cid *)(*res)->cid_placeholder)) {
                res = &(*res)->next;
        }
 
        return res;
 }
 
-struct pl_quic_conn_sess_data *kr_quic_table_lookup(const ngtcp2_cid *cid, kr_quic_table_t *table)
+struct pl_quic_conn_sess_data *kr_quic_table_lookup(const ngtcp2_cid *cid,
+               kr_quic_table_t *table)
 {
        kr_quic_cid_t **pcid = kr_quic_table_lookup2(cid, table);
        return *pcid == NULL ? NULL : (*pcid)->conn_sess;
@@ -178,6 +206,10 @@ struct pl_quic_conn_sess_data *kr_quic_table_lookup(const ngtcp2_cid *cid, kr_qu
 kr_quic_cid_t **kr_quic_table_insert(struct pl_quic_conn_sess_data *conn,
                const ngtcp2_cid *cid, kr_quic_table_t *table)
 {
+       if (kr_fails_assert(conn && cid && table)) {
+               return NULL;
+       }
+
        uint64_t hash = cid2hash(cid, table);
 
        kr_quic_cid_t *cidobj = malloc(sizeof(*cidobj));
@@ -199,7 +231,7 @@ kr_quic_cid_t **kr_quic_table_insert(struct pl_quic_conn_sess_data *conn,
 int kr_quic_table_add(struct pl_quic_conn_sess_data *conn_sess,
                const ngtcp2_cid *cid, kr_quic_table_t *table)
 {
-       if (!conn_sess || !cid || !table) {
+       if (kr_fails_assert(conn_sess && cid && table)) {
                return kr_error(EINVAL);
        }
 
@@ -210,7 +242,8 @@ int kr_quic_table_add(struct pl_quic_conn_sess_data *conn_sess,
 
        kr_quic_cid_t **addto = kr_quic_table_insert(conn_sess, cid, table);
        if (addto == NULL) {
-               heap_delete(table->expiry_heap, heap_find(table->expiry_heap, (heap_val_t *)conn_sess));
+               heap_delete(table->expiry_heap, heap_find(table->expiry_heap,
+                                       (heap_val_t *)conn_sess));
                return kr_error(ENOMEM);
        }
 
@@ -218,46 +251,82 @@ int kr_quic_table_add(struct pl_quic_conn_sess_data *conn_sess,
        return kr_ok();
 }
 
+int kr_quic_table_rem2(kr_quic_cid_t **pcid, kr_quic_table_t *table)
+{
+       kr_quic_cid_t *cid = *pcid;
+       *pcid = cid->next;
+       cid->conn_sess->cid_pointers--;
+       free(cid);
+       table->pointers--;
+
+       return kr_ok();
+}
+
+int set_tls_error(struct pl_quic_conn_sess_data *conn,
+               quic_doq_error_t *error_code,
+               const uint8_t *msg, size_t msglen)
+{
+       if (kr_fails_assert(conn && msglen < 128))
+               return kr_error(EINVAL);
+
+       /* Not redundant! ngtcp2 doesn't memcpy the message.
+        * msgs on the stack could therefore cause stack use after return
+        * once ngtcp2_conn_writev_stream is called. */
+       if (msg && msglen > 0) {
+               memcpy(&conn->err_msg_buffer, msg, msglen);
+       }
+
+       *error_code = 0x100 | ngtcp2_conn_get_tls_alert(conn->conn);
+
+       ngtcp2_ccerr_set_tls_alert(&conn->ccerr,
+                       ngtcp2_conn_get_tls_alert(conn->conn),
+                       conn->err_msg_buffer, msglen);
+
+       return kr_ok();
+}
+
 int set_application_error(struct pl_quic_conn_sess_data *conn,
                quic_doq_error_t error_code, const uint8_t *msg, size_t msglen)
 {
        if (kr_fails_assert(conn && msglen < 128))
                return kr_error(EINVAL);
 
+       /* Not redundant! ngtcp2 doesn't memcpy the message.
+        * msgs on the stack could therefore cause stack use after return
+        * once ngtcp2_conn_writev_stream is called. */
        if (msg && msglen > 0) {
                memcpy(&conn->err_msg_buffer, msg, msglen);
        }
+
        ngtcp2_ccerr_set_application_error(&conn->ccerr, error_code,
                        conn->err_msg_buffer, msglen);
 
        return kr_ok();
 }
 
-bool init_unique_cid(ngtcp2_cid *cid, size_t len, kr_quic_table_t *table)
+int init_unique_cid(ngtcp2_cid *cid, size_t len, kr_quic_table_t *table)
 {
        do {
-               if (init_random_cid(cid, len), cid->datalen == 0)
-                       return false;
+               if (init_random_cid(cid, len) != 0)
+                       return -1;
 
        } while (kr_quic_table_lookup(cid, table) != NULL);
 
-       return true;
+       return 0;
 }
 
 int write_retry_packet(struct wire_buf *dest, kr_quic_table_t *table,
                ngtcp2_version_cid *dec_cids,
-               const struct sockaddr *src_addr,
-               uint8_t *secret, size_t secret_len)
+               const struct sockaddr *src_addr)
 {
-       ngtcp2_cid dcid;
-       ngtcp2_cid scid;
-       ngtcp2_cid new_dcid;
-
-       ngtcp2_cid_init(&dcid, dec_cids->dcid, dec_cids->dcidlen);
-       ngtcp2_cid_init(&scid, dec_cids->scid, dec_cids->scidlen);
-
-       init_random_cid(&new_dcid, 0);
-       if (!init_unique_cid(&new_dcid, 0, table)) {
+       ngtcp2_cid odcid;
+       ngtcp2_cid oscid;
+       ngtcp2_cid retry_scid;
+
+       ngtcp2_cid_init(&odcid, dec_cids->dcid, dec_cids->dcidlen);
+       ngtcp2_cid_init(&oscid, dec_cids->scid, dec_cids->scidlen);
+       init_random_cid(&retry_scid, 0);
+       if (init_unique_cid(&retry_scid, 0, table) != 0) {
                kr_log_debug(DOQ, "Failed to initialize unique cid for Retry packet\n");
                return -1;
        }
@@ -266,10 +335,10 @@ int write_retry_packet(struct wire_buf *dest, kr_quic_table_t *table,
        uint64_t now = quic_timestamp();
 
        int ret = ngtcp2_crypto_generate_retry_token2(
-               retry_token, (const uint8_t *)secret,
-               secret_len, dec_cids->version,
+               retry_token, (const uint8_t *)table->hash_secret,
+               sizeof(table->hash_secret), dec_cids->version,
                src_addr, kr_sockaddr_len(src_addr),
-               &new_dcid, &dcid, now);
+               &retry_scid, &odcid, now);
 
        if (ret < 0) {
                kr_log_debug(DOQ, "Failed to generate retry token\n");
@@ -279,8 +348,8 @@ int write_retry_packet(struct wire_buf *dest, kr_quic_table_t *table,
        ret = ngtcp2_crypto_write_retry(
                wire_buf_free_space(dest),
                wire_buf_free_space_length(dest),
-               dec_cids->version, &scid,
-               &new_dcid, &dcid,
+               dec_cids->version, &oscid,
+               &retry_scid, &odcid,
                retry_token, ret
        );
 
index 71a5696d430ec82b0b201d107994c3ad37d40214..350c615aaf5a5664139ad4b5417250ba1c12f98e 100644 (file)
@@ -20,6 +20,9 @@
 #include "session2.h"
 #include "network.h"
 
+/* option to turn of ngtcp2 log which is by default enabled for log_level = debug */
+#define DEBUG_NGTCP2 false
+
 /** RFC 9250 4.3 DoQ Error Codes for use as application protocol error codes */
 typedef enum {
        /*! No error.  This is used when the connection or stream needs to be
@@ -41,7 +44,7 @@ typedef enum {
             error code. */
        DOQ_UNSPECIFIED_ERROR = 0x5,
        /*! Alternative error code, can be used for tests. */
-       //DOQ_ERROR_RESERVED = 0xd098ea5e
+       // DOQ_ERROR_RESERVED = 0xd098ea5e
 } quic_doq_error_t;
 
 // Macros from knot quic impl
@@ -51,30 +54,30 @@ typedef enum {
 #define QUIC_SEND_RETRY                  NGTCP2_ERR_RETRY
 #define QUIC_SEND_STATELESS_RESET        (-NGTCP2_STATELESS_RESET_TOKENLEN)
 #define QUIC_SEND_CONN_CLOSE             (-2000)
+/* Returning this value from handle_packet has to be accompanied
+ * by timer startup (if not already running). Otherwise the connection
+ * might linger in the table until the DoQ endpoint terminates.
+ * see NGTCP2_ERR_DROP_CONN switch case in handle_packet */
+#define QUIC_SEND_NONE                   (-2001)
 
+/* maximum message length for messages used in ccerr */
 #define MAX_REASONLEN 128
-
 #define BUCKETS_PER_CONNS 8
-
 #define MAX_QUIC_PKT_SIZE 65536
 #define MAX_QUIC_FRAME_SIZE 65536
 #define QUIC_MAX_SEND_PER_RECV 4
-
-/* The connection can reach a state when the internal ngtcp2 state machine is
- * not waiting for any event. In such cases the ngtcp2_conn_get_expiry
- * returns UINT64_MAX => meaining no timer event. If the peer abruptly
- * stops communicating in such a state the connection would be active
- * untill a new connection prompts a conn table sweep. Instead of waiting
- * for a new connection we set the timer to the QUIC_MAX_TIMEOUT to assure
- * that every connection will terminate within a reasonable time period
- * regardless of the incoming traffic. This is just a cleanliness measure. */
+/* A connection can reach a state where the internal ngtcp2 state machine is
+ * not waiting for any event. In such a case the ngtcp2_conn_get_expiry
+ * returns UINT64_MAX => meaning no timer event. If the peer abruptly
+ * stops communicating in such a state the connection remain in the table/
+ * We set the timer to the QUIC_MAX_TIMEOUT to assure
+ * that every connection will terminate within a reasonable time period */
 #define QUIC_MAX_IDLE_TIMEOUT (15 * NGTCP2_SECONDS)
-#define QUIC_CONN_IDLE_TIMEOUT (2 * NGTCP2_SECONDS)
-#define QUIC_HS_IDLE_TIMEOUT   (3 * NGTCP2_SECONDS)
+#define QUIC_CONN_IDLE_TIMEOUT (8 * NGTCP2_SECONDS)
+#define QUIC_HS_IDLE_TIMEOUT   (5 * NGTCP2_SECONDS)
 
-#define container_of(ptr, type, member) ({ \
-       const typeof( ((type *)0)->member ) *__mptr = (ptr); \
-       (type *)((char *)__mptr - offsetof(type,member));})
+#define container_of(ptr, type, member) \
+       ((type *)((char *)(ptr) - offsetof(type, member)))
 
 typedef enum {
        KNOT_QUIC_TABLE_CLIENT_ONLY = (1 << 0),
@@ -121,36 +124,82 @@ struct kr_quic_stream_param {
 };
 
 int quic_configuration_set(void);
+
 int quic_configuration_free(struct net_quic_params *quic_params);
+
 bool kr_quic_conn_timeout(struct pl_quic_conn_sess_data *conn, uint64_t *now);
+
 uint64_t cid2hash(const ngtcp2_cid *cid, kr_quic_table_t *table);
+
+/* ngtcp2 operates with ns whilst libuv uses ms. Simple inline ceil converter. */
+uint64_t quic_ns_to_ms_ceil(uint64_t ns);
+
+/* Closing/draining state PTO based timeout recommended by ngtcp2. The returned
+ * value is the time in ms that the connection state should be available for.
+ * Combine with quic_set_idle_timeout to take care of conn state
+ * teardown once the closing/draining state is reached. */
+uint64_t quic_get_closing_timeout(uint64_t pto_ns);
+
 /* start uv_timer with timeout equal to ms, this timer is to be used
  * only during the handshake, after that the negotiated max_idle_timeout
  * will be used. Returns 0 or EINVAL */
 int quic_set_hs_timeout(struct session2 *s, uint64_t ms);
+
+/* Only used when the connection entered closing/draining state
+ * and we have to wait 3 * PTO before freeing the connection (e.g. session). */
 int quic_set_idle_timeout(struct session2 *s, uint64_t ms);
+
+/* Call this every time a bunch of writes are executed.
+ * The next expiry is retrieved and the connection timer is reset
+ * so ngtcp2 library can schedule next event */
 void quic_reset_expiry(struct pl_quic_conn_sess_data *conn);
+
+int kr_quic_table_rem2(kr_quic_cid_t **pcid, kr_quic_table_t *table);
+
+/* Set the connection ccerr to type TRANSPORT with the error code
+ * containing the tls alert. The error code is calculated as
+ * 0x100 | <the tls alert>  (see RFC 9001 4.8. TLS Errors). */
+int set_tls_error(struct pl_quic_conn_sess_data *conn,
+               quic_doq_error_t *error_code,
+               const uint8_t *msg, size_t msglen);
+
+/* This function sets the ccerr type to APPLICATION and the error_code to
+ * the provided error_code. msg can be NULL, if so set mgslen to 0.
+ * see RFC 9250 4.3 DoQ Error Codes */
 int set_application_error(struct pl_quic_conn_sess_data *conn,
                quic_doq_error_t error_code, const uint8_t *msg, size_t msglen);
+
+/* Inserts another cid into the table. This cid will point
+ * to the connection provided via the conn parameter. Can be called
+ * multiple times on the same connection (i.e. use when new cid for an
+ * existing connection is required). */
 kr_quic_cid_t **kr_quic_table_insert(struct pl_quic_conn_sess_data *conn,
                const ngtcp2_cid *cid, kr_quic_table_t *table);
+
+/* Registeres a new connection in the table and expiry heap.
+ * Only call once when the connection is created. Calls kr_quic_table_insert
+ * to add a reference from cid to the connection. */
 int kr_quic_table_add(struct pl_quic_conn_sess_data *conn_sess,
                const ngtcp2_cid *cid, kr_quic_table_t *table);
-bool init_unique_cid(ngtcp2_cid *cid, size_t len, kr_quic_table_t *table);
-void init_random_cid(ngtcp2_cid *cid, size_t len);
+
+int init_unique_cid(ngtcp2_cid *cid, size_t len, kr_quic_table_t *table);
+
+int init_random_cid(ngtcp2_cid *cid, size_t len);
+
 uint64_t quic_timestamp(void);
 
 void quic_hs_timeout(uv_timer_t *timer);
-void quic_idle_timeout(uv_timer_t *timer);
-void quic_reset_expiry(struct pl_quic_conn_sess_data *conn);
+
+void quic_handle_timeout(uv_timer_t *timer);
 
 kr_quic_cid_t **kr_quic_table_lookup2(const ngtcp2_cid *cid,
                kr_quic_table_t *table);
+
 struct pl_quic_conn_sess_data *kr_quic_table_lookup(const ngtcp2_cid *cid,
                kr_quic_table_t *table);
+
 int write_retry_packet(struct wire_buf *dest, kr_quic_table_t *table,
                ngtcp2_version_cid *dec_cids,
-               const struct sockaddr *src_addr,
-               uint8_t *secret, size_t secret_len);
+               const struct sockaddr *src_addr);
 
 #endif
index 3b69d373db90fea54572eb4d9111be7893a5375f..6e63930d65a61f86db99532922588c27c8e0b1dd 100644 (file)
@@ -6,6 +6,7 @@
 #include "lib/proto.h"
 #include "network.h"
 #include "quic_common.h"
+#include "quic_demux.h"
 #include "quic_stream.h"
 #include "lib/dnssec.h"
 #include "session2.h"
 #include <libknot/wire.h>
 #include <netinet/in.h>
 #include <ngtcp2/ngtcp2.h>
+#include <ngtcp2/ngtcp2_crypto.h>
 #include <sched.h>
+#include <stdlib.h>
 #include <uv.h>
 
-/* option to turn of ngtcp2 log which is usually enabled for log_level = debug */
-#define DEBUG_NGTCP2 true
-
 #define EPHEMERAL_CERT_EXPIRATION_SECONDS_RENEW_BEFORE ((time_t)60*60*24*7)
 
 /* QUIC only works with TLSv1.3, auth KnotDNS have experienced issues
@@ -51,11 +51,16 @@ static int handle_packet(struct pl_quic_conn_sess_data *conn,
        }
 
        if (ret == 0) {
-               /* given the 0 return value the pkt has been processed */
                wire_buf_reset(ctx->payload.wire_buf);
                return kr_ok();
        }
 
+       /* Avoid overwriting any more specific DoQ error codes. */
+       if (ret < 0 && conn->ccerr.error_code == DOQ_NO_ERROR) {
+               ngtcp2_ccerr_set_liberr(&conn->ccerr, ret, NULL, 0);
+       }
+
+       ngtcp2_duration pto_ns = 0;
        switch (ret) {
        case NGTCP2_ERR_RETRY:
                /* "Server must perform address validation by sending Retry packet
@@ -63,21 +68,46 @@ static int handle_packet(struct pl_quic_conn_sess_data *conn,
                 * and discard the connection state. Client application does
                 * not get this error code." */
                return QUIC_SEND_RETRY;
+
        case NGTCP2_ERR_DROP_CONN:
+               *doq_error = DOQ_UNSPECIFIED_ERROR;
+               QUIC_SET_DRAINING(conn);
+               kr_log_debug(DOQ, "Connection was dropped, waiting 1 PTO before termination\n");
+               pto_ns = ngtcp2_conn_get_pto(conn->conn);
+               quic_set_idle_timeout(conn->h.session,
+                               quic_ns_to_ms_ceil(pto_ns));
+               return QUIC_SEND_NONE;
+
        case NGTCP2_ERR_DRAINING:
-       case NGTCP2_ERR_CLOSING:
                *doq_error = DOQ_UNSPECIFIED_ERROR;
                QUIC_SET_DRAINING(conn);
-               return QUIC_SEND_CONN_CLOSE;
+               kr_log_debug(DOQ, "Connection entered draining state, waiting 3 PTO\n");
+               pto_ns = ngtcp2_conn_get_pto(conn->conn);
+               quic_set_idle_timeout(conn->h.session,
+                               quic_get_closing_timeout(pto_ns));
+               return QUIC_SEND_NONE;
+
+       case NGTCP2_ERR_CLOSING:
+               *doq_error = DOQ_UNSPECIFIED_ERROR;
+               QUIC_SET_CLOSING(conn);
+               kr_log_debug(DOQ, "Connection entered closing state, waiting 3 PTO\n");
+               pto_ns = ngtcp2_conn_get_pto(conn->conn);
+               quic_set_idle_timeout(conn->h.session,
+                               quic_get_closing_timeout(pto_ns));
+               return QUIC_SEND_NONE;
+
        case NGTCP2_ERR_CRYPTO:
-               *doq_error = DOQ_INTERNAL_ERROR;
-               kr_log_error(DOQ, "TLS stack error %d\n",
+               /* see RFC 9001 4.8. TLS Errors */
+               set_tls_error(conn, doq_error, NULL, 0);
+               kr_log_error(DOQ, "TLS stack alert: %d\n",
                                ngtcp2_conn_get_tls_alert(conn->conn));
-               QUIC_SET_DRAINING(conn);
+
                return QUIC_SEND_CONN_CLOSE;
+
        default:
                *doq_error = DOQ_UNSPECIFIED_ERROR;
-               QUIC_SET_CLOSING(conn);
+               kr_log_debug(DOQ, "Unspecified error (%d), sending connection close\n",
+                               ret);
                return QUIC_SEND_CONN_CLOSE;
        }
 
@@ -99,71 +129,73 @@ static int handshake_completed_cb(ngtcp2_conn *ngconn, void *user_data)
        struct pl_quic_conn_sess_data *conn = user_data;
        QUIC_SET_HS_COMPLETED(conn);
 
-       /* Notify worker that waiting tasks can be sent out */
        if (conn->h.session->outgoing) {
                // pass
        } else {
                quic_reset_expiry(conn);
        }
 
-       return kr_ok();
+       return NGTCP2_NO_ERROR;
 }
 
 static int kr_recv_stream_data_cb(ngtcp2_conn *ngconn, uint32_t flags,
        int64_t stream_id, uint64_t offset, const uint8_t *data,
        size_t datalen, void *user_data, void *stream_user_data)
 {
+       /* Stream session was already terminated because the connection closed */
+       if (stream_user_data == NULL) {
+               return NGTCP2_NO_ERROR;
+       }
+
+       (void)ngtcp2_conn_extend_max_stream_offset(ngconn, stream_id, datalen);
+       ngtcp2_conn_extend_max_offset(ngconn, datalen);
+
+       const uint8_t msg[] = "malformed data";
        struct pl_quic_conn_sess_data *conn = user_data;
        struct pl_quic_stream_sess_data *stream = stream_user_data;
 
-       if (kr_fails_assert(offset == wire_buf_data_length(&stream->pers_inbuf))) {
-               session2_force_close(stream->h.session);
+       /* just to be safe, ngtcp2 guarantees in order data with no gaps */
+       if (unlikely(kr_fails_assert(offset == wire_buf_data_length(
+                                               &stream->pers_inbuf)))) {
                return NGTCP2_ERR_CALLBACK_FAILURE;
        }
 
        stream->incflags = flags;
        stream->sdata_offset = offset;
 
-       /* The length prefix hasn't arrived yet */
-       if (wire_buf_data_length(&stream->pers_inbuf) < 2) {
+       if (wire_buf_data_length(&stream->pers_inbuf) < sizeof(uint16_t)) {
                if (datalen == 0) {
-                       session2_force_close(stream->h.session);
-                       return NGTCP2_ERR_CALLBACK_FAILURE;
+                       goto malformed;
                }
                if (datalen == 1 && wire_buf_data_length(&stream->pers_inbuf) == 0) {
                        /* received only the first length prefix octet */
                        stream->pers_inbuf.buf[0] = data[0];
-                       kr_require(wire_buf_consume(&stream->pers_inbuf, 1) == 0);
+                       wire_buf_consume(&stream->pers_inbuf, 1);
                        goto finished;
                }
                if (wire_buf_data_length(&stream->pers_inbuf) == 1) {
                        /* finish partial length prefix octet
                         * (previous first stream payload had datalen == 1) */
                        stream->pers_inbuf.buf[1] = data[0];
-                       kr_require(wire_buf_consume(&stream->pers_inbuf, 1) == 0);
+                       wire_buf_consume(&stream->pers_inbuf, 1);
                        data += 1;
                        datalen--;
                } else {
                        /* length prefix arrived in one piece */
                        knot_wire_write_u16(wire_buf_data(&stream->pers_inbuf),
                                                knot_wire_read_u16(data));
-                       kr_require(wire_buf_consume(&stream->pers_inbuf,
-                                               sizeof(uint16_t)) == 0);
+                       wire_buf_consume(&stream->pers_inbuf, sizeof(uint16_t));
                        data += sizeof(uint16_t);
                        datalen -= sizeof(uint16_t);
                }
 
-               uint16_t qsize = knot_wire_read_u16(wire_buf_data(&stream->pers_inbuf));
-               uint16_t qsize_prefixed = qsize + sizeof(uint16_t);
+               uint32_t qsize =
+                       knot_wire_read_u16(wire_buf_data(&stream->pers_inbuf));
+               uint32_t qsize_prefixed = qsize + sizeof(uint16_t);
                if (qsize > wire_buf_free_space_length(&stream->pers_inbuf)) {
-                       char *new_buf = realloc(stream->pers_inbuf.buf,
-                                       qsize_prefixed);
-                       if (!new_buf) {
-                               session2_force_close(stream->h.session);
-                               return NGTCP2_ERR_CALLBACK_FAILURE;
-                       }
-                       stream->pers_inbuf.buf = new_buf;
-                       stream->pers_inbuf.size = qsize_prefixed;
+                       uint32_t qsize_prefixed_safe = qsize_prefixed + 16;
+                       wire_buf_reserve(&stream->pers_inbuf,
+                                       qsize_prefixed_safe);
                }
                if (datalen == 0) {
                        goto finished;
@@ -179,35 +211,50 @@ static int kr_recv_stream_data_cb(ngtcp2_conn *ngconn, uint32_t flags,
                goto finished;
        }
 
-       kr_require(wire_buf_free_space_length(&stream->pers_inbuf) >= datalen);
+       /* the peer sent more data than declared but the length prefix.
+        * close the connection with EPROTO and report malformed data */
+       if (wire_buf_free_space_length(&stream->pers_inbuf) < datalen) {
+               uint32_t query_size = sizeof(uint16_t) +
+                       knot_wire_read_u16(wire_buf_data(&stream->pers_inbuf));
+               uint32_t payload_size = wire_buf_data_length(&stream->pers_inbuf);
+               kr_log_debug(DOQ, "invalid payload size, malformed data (%hu != %ju)\n",
+                               query_size, payload_size + datalen);
+               goto malformed;
+
+       }
        memcpy(wire_buf_free_space(&stream->pers_inbuf), data, datalen);
        wire_buf_consume(&stream->pers_inbuf, datalen);
-       (void)ngtcp2_conn_extend_max_stream_offset(ngconn, stream_id, datalen);
-       ngtcp2_conn_extend_max_offset(ngconn, datalen);
 
 finished:
        if (flags & NGTCP2_STREAM_DATA_FLAG_FIN) {
-               uint16_t query_size = sizeof(uint16_t) +
+               if (wire_buf_data_length(&stream->pers_inbuf) < sizeof(uint16_t)) {
+                       kr_log_debug(DOQ, "invalid payload size, malformed data (payload < 2)\n");
+                       goto malformed;
+               }
+               uint32_t query_size = sizeof(uint16_t) +
                        knot_wire_read_u16(wire_buf_data(&stream->pers_inbuf));
-               size_t payload_size = wire_buf_data_length(&stream->pers_inbuf);
+               uint32_t payload_size = wire_buf_data_length(&stream->pers_inbuf);
 
-               /* a remote endpoint might maliciously or accidentally send
-                * less that advertised by the length prefix => malformed data.
-                * respond by closing the connection forcefully.
-                * (see: RFC 9250 4.3.3. Protocol Errors) */
                if (query_size != payload_size) {
-                       kr_log_debug(DOQ, "length mismatch between length prefix and actual length of the query (%hu != %zu)\n",
+                       kr_log_debug(DOQ, "invalid payload size, malformed data (%hu != %hu)\n",
                                        query_size, payload_size);
-                       const uint8_t msg[] = "Incomplete query";
-                       set_application_error(conn, DOQ_PROTOCOL_ERROR,
-                                       msg, sizeof(msg) - 1);
-                       return NGTCP2_ERR_CALLBACK_FAILURE;
+                       goto malformed;
                }
                session2_inc_refs(stream->h.session);
                queue_push(conn->pending_unwrap, stream);
        }
 
-       return kr_ok();
+       return NGTCP2_NO_ERROR;
+/* a remote endpoint might maliciously or accidentally send
+ * less that advertised by the length prefix => malformed data.
+ * respond by closing the connection forcefully.
+ * (see: RFC 9250 4.3.3. Protocol Errors) */
+malformed:
+       QUIC_SET_CLOSING(conn);
+       set_application_error(conn, DOQ_PROTOCOL_ERROR,
+                       msg, sizeof(msg) - 1);
+       return NGTCP2_ERR_CALLBACK_FAILURE;
+
 }
 
 static int acked_stream_data_offset_cb(ngtcp2_conn *ngconn,
@@ -215,6 +262,10 @@ static int acked_stream_data_offset_cb(ngtcp2_conn *ngconn,
                void *user_data, void *stream_user_data)
 {
        (void)ngconn;
+       /* Stream session was already terminated because the connection closed */
+       if (stream_user_data == NULL) {
+               return NGTCP2_NO_ERROR;
+       }
        struct pl_quic_stream_sess_data *stream = stream_user_data;
        kr_quic_stream_ack_data(stream, stream_id, offset + datalen, false);
        return NGTCP2_NO_ERROR;
@@ -233,21 +284,21 @@ static int stream_open_cb(ngtcp2_conn *ngconn,
                .protocol = PROTOLAYER_TYPE_QUIC_STREAM,
                .param = &params
        };
-       struct session2 *new_subsession =
-               session2_new_child(conn->h.session,
-                               KR_PROTO_DOQ_STREAM,
-                               &data_param,
-                               1,
-                               false);
+       struct session2 *new_subsession = 
+               session2_new_child(conn->h.session, KR_PROTO_DOQ_STREAM,
+                               &data_param, 1, false);
 
        if (!new_subsession) {
                kr_log_error(DOQ, "Failed to create new quic stream session\n");
-               return kr_error(ENOMEM);
+               /* TODO could use EXCESSIVE_LOAD */
+               set_application_error(conn, DOQ_INTERNAL_ERROR, NULL, 0);
+               return NGTCP2_ERR_CALLBACK_FAILURE;
        }
 
        struct pl_quic_stream_sess_data *stream =
                protolayer_sess_data_get_proto(new_subsession,
                                PROTOLAYER_TYPE_QUIC_STREAM);
+       kr_require(stream);
        stream->conn_ref = conn;
        if (conn->streams_count <= 0) {
                add_head(&conn->streams, &stream->list_node);
@@ -267,10 +318,17 @@ static int stream_close_cb(ngtcp2_conn *ngconn, uint32_t flags,
                int64_t stream_id, uint64_t app_error_code,
                void *user_data, void *stream_user_data)
 {
+       /* Stream session was already terminated because the connection closed */
+       if (stream_user_data == NULL) {
+               return NGTCP2_NO_ERROR;
+       }
+
        struct pl_quic_conn_sess_data *conn = user_data;
        struct pl_quic_stream_sess_data *stream = stream_user_data;
+
+       stream->closed = true;
+
        session2_close(stream->h.session);
-       --conn->streams_count;
        ++conn->finished_streams;
 
        return NGTCP2_NO_ERROR;
@@ -280,7 +338,7 @@ static void kr_quic_rand_cb(uint8_t *dest, size_t destlen,
        const ngtcp2_rand_ctx *rand_ctx)
 {
        (void)rand_ctx;
-       dnssec_random_buffer(dest, destlen);
+       (void)dnssec_random_buffer(dest, destlen);
 }
 
 static int get_new_connection_id_cb(ngtcp2_conn *ngconn, ngtcp2_cid *cid,
@@ -288,16 +346,24 @@ static int get_new_connection_id_cb(ngtcp2_conn *ngconn, ngtcp2_cid *cid,
 {
        (void)ngconn;
        struct pl_quic_conn_sess_data *conn = user_data;
-       session2_event(conn->h.session->transport.parent,
-                       PROTOLAYER_EVENT_CONNECT_UPDATE, conn);
-       memcpy(cid, &conn->dcid, sizeof(ngtcp2_cid));
+       if (init_unique_cid(cid, cidlen, conn->table_ref) != 0) {
+               kr_log_error(DOQ, "Failed to create init new cid\n");
+               return NGTCP2_ERR_CALLBACK_FAILURE;
+       }
 
+       memcpy(&conn->dcid, cid, sizeof(ngtcp2_cid));
        if (ngtcp2_crypto_generate_stateless_reset_token(token, conn->secret,
                                sizeof(conn->secret), cid) != 0) {
+               kr_log_error(DOQ, "Failed to generate stateless reset token\n");
+               return NGTCP2_ERR_CALLBACK_FAILURE;
+       }
+
+       if (kr_quic_table_insert(conn, cid, conn->table_ref) == NULL) {
+               kr_log_error(DOQ, "Failed to add new cid to conn map\n");
                return NGTCP2_ERR_CALLBACK_FAILURE;
        }
 
-       return 0;
+       return NGTCP2_NO_ERROR;
 }
 
 int remove_connection_id_cb(ngtcp2_conn *ngconn,
@@ -305,15 +371,22 @@ int remove_connection_id_cb(ngtcp2_conn *ngconn,
 {
        (void)ngconn;
        struct pl_quic_conn_sess_data *conn = user_data;
-       ngtcp2_cid save_cid = conn->dcid;
-       memcpy(&conn->dcid, cid, sizeof(ngtcp2_cid));
-       session2_event(conn->h.session->transport.parent,
-                       PROTOLAYER_EVENT_CONNECT_RETIRE,
-                       conn);
-       conn->dcid = save_cid;
-       return 0;
+
+       kr_quic_cid_t **pcid = kr_quic_table_lookup2(cid, conn->table_ref);
+       if (!pcid || !*pcid) {
+               kr_log_error(DOQ, "Table doesn't contain cid that is to be removed\n");
+               return NGTCP2_ERR_CALLBACK_FAILURE;
+       }
+
+       if ((*pcid)->conn_sess->cid_pointers <= 1) {
+               kr_log_error(DOQ, "Cannot remove all connection ids, protocol error\n");
+               return NGTCP2_ERR_CALLBACK_FAILURE;
+       }
+
+       return kr_quic_table_rem2(pcid, conn->table_ref);
 }
 
+#if DEBUG_NGTCP2
 static void quic_debug_cb(void *user_data, const char *format, ...)
 {
        char buf[256];
@@ -323,6 +396,7 @@ static void quic_debug_cb(void *user_data, const char *format, ...)
        kr_log_debug(DOQ_LIBNGTCP2, "%s\n", buf);
        va_end(args);
 }
+#endif /* DEBUG_NGTCP2 */
 
 static int conn_new_handler(ngtcp2_conn **pconn, const ngtcp2_path *path,
                const ngtcp2_cid *scid, const ngtcp2_cid *dcid,
@@ -343,6 +417,8 @@ static int conn_new_handler(ngtcp2_conn **pconn, const ngtcp2_path *path,
                .acked_stream_data_offset = acked_stream_data_offset_cb,
                .stream_open = stream_open_cb,
                .stream_close = stream_close_cb,
+               // .stream_stop_sending - OPTIONAL
+               // .stream_reset - OPTIONAL
                // .recv_stateless_reset, - OPTIONAL
                // .ngtcp2_crypto_recv_retry_cb, - OPTIONAL
                // .extend_max_streams_bidi - OPTIONAL
@@ -353,7 +429,6 @@ static int conn_new_handler(ngtcp2_conn **pconn, const ngtcp2_path *path,
                .update_key = ngtcp2_crypto_update_key_cb,
                // .path_validation, - OPTIONAL
                // .select_preferred_addr - OPTIONAL
-               // .stream_rst, - OPTIONAL
                // .extend_max_remote_streams_bidi - OPTIONAL
                // .extend_max_remote_streams_uni - OPTIONAL
                // .extend_max_stream_data, - OPTIONAL
@@ -366,7 +441,6 @@ static int conn_new_handler(ngtcp2_conn **pconn, const ngtcp2_path *path,
                // .ack_datagram - OPTIONAL
                // .lost_datagram - OPTIONAL
                .get_path_challenge_data = ngtcp2_crypto_get_path_challenge_data_cb,
-               // .stream_stop_sending - OPTIONAL
                .version_negotiation = ngtcp2_crypto_version_negotiation_cb,
                // .recv_rx_key - OPTIONAL
                // .recv_tx_key - OPTIONAL
@@ -375,42 +449,32 @@ static int conn_new_handler(ngtcp2_conn **pconn, const ngtcp2_path *path,
        ngtcp2_settings settings;
        ngtcp2_settings_default(&settings);
        settings.initial_ts = now;
-
-       if (KR_LOG_LEVEL_IS(LOG_DEBUG)) {
-               settings.log_printf = quic_debug_cb;
-       }
-
-       size_t limit =
-               protolayer_globals[PROTOLAYER_TYPE_QUIC_CONN].wire_buf_max_overhead;
-
-       if (limit != 0) {
-               settings.max_tx_udp_payload_size = limit;
-       }
-
+       settings.max_tx_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
        settings.handshake_timeout = QUIC_HS_IDLE_TIMEOUT;
+#if DEBUG_NGTCP2
+       settings.log_printf = quic_debug_cb;
+#endif /* DEBUG_NGTCP2 */
        settings.no_pmtud = true;
 
        ngtcp2_transport_params params;
        ngtcp2_transport_params_default(&params);
-
-       /** This informs peer that active migration might not be available.
-        * Peer might still attempt to migrate. see RFC 9000/5.2.3 */
-       params.disable_active_migration = true;
-
-       /* We have no use for unidirectional streams */
-       params.initial_max_streams_uni = 0;
-
        if (unlikely(kr_fails_assert(the_network && the_network->quic_params))) {
                kr_log_debug(DOQ, "Missing network struct or network quic_parameters\n");
                return kr_error(EINVAL);
        }
        params.initial_max_streams_bidi = the_network->quic_params->max_streams;
+       /* DoQ has no use for unidirectional streams */
+       params.initial_max_streams_uni = 0;
        params.initial_max_stream_data_bidi_local = MAX_QUIC_FRAME_SIZE;
        params.initial_max_stream_data_bidi_remote = MAX_QUIC_FRAME_SIZE;
        params.initial_max_data = MAX_QUIC_PKT_SIZE;
+       /** This informs peer that active migration might not be available.
+        * Peer might still attempt to migrate.
+        * see RFC 9000 5.2.3 Considerations for Simple Load Balancers */
+       params.disable_active_migration = true;
        params.max_idle_timeout = QUIC_CONN_IDLE_TIMEOUT;
-       params.stateless_reset_token_present = 1;
-       // params.active_connection_id_limit = 8;
+       params.stateless_reset_token_present =
+               NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
 
        if (odcid != NULL) {
                params.original_dcid = *odcid;
@@ -418,7 +482,7 @@ static int conn_new_handler(ngtcp2_conn **pconn, const ngtcp2_path *path,
        }
 
        if (retry_sent) {
-               /* retry scid is retrieved from the
+               /* retry scid is retrieved from
                 * ngtcp2_crypto_verify_retry_roken2 as the odcid
                 * used by the client. */
                params.retry_scid = *dcid;
@@ -434,13 +498,14 @@ static int conn_new_handler(ngtcp2_conn **pconn, const ngtcp2_path *path,
        if (server) {
                const ngtcp2_cid *client_dcid = scid;
                const ngtcp2_cid *client_scid = dcid;
-               return ngtcp2_conn_server_new(pconn, client_dcid, client_scid, path,
-                               version, &callbacks, &settings, &params, NULL, conn);
+               return ngtcp2_conn_server_new(pconn, client_dcid, client_scid,
+                               path, version, &callbacks, &settings, &params,
+                               NULL, conn);
        } else {
                kr_log_warning(DOQ, "Client side is not implemented\n");
                return kr_error(EINVAL);
-               // return ngtcp2_conn_client_new(pconn, dcid, scid, path, version, &callbacks,
-               //                               &settings, &params, NULL, conn);
+               // return ngtcp2_conn_client_new(pconn, dcid, scid, path, version,
+               //              &callbacks, &settings, &params, NULL, conn);
        }
 }
 
@@ -474,10 +539,12 @@ int kr_tls_server_session(struct pl_quic_conn_sess_data *conn)
        }
 
        time_t now = time(NULL);
-       if (the_network->tls_credentials->valid_until != GNUTLS_X509_NO_WELL_DEFINED_EXPIRATION) {
+       if (the_network->tls_credentials->valid_until
+                       != GNUTLS_X509_NO_WELL_DEFINED_EXPIRATION) {
                if (the_network->tls_credentials->ephemeral_servicename) {
                        /* ephemeral cert: refresh if due to expire within a week */
-                       if (now >= the_network->tls_credentials->valid_until - EPHEMERAL_CERT_EXPIRATION_SECONDS_RENEW_BEFORE) {
+                       if (now >= the_network->tls_credentials->valid_until
+                                       - EPHEMERAL_CERT_EXPIRATION_SECONDS_RENEW_BEFORE) {
                                struct tls_credentials *newcreds = tls_get_ephemeral_credentials();
                                if (newcreds) {
                                        tls_credentials_release(the_network->tls_credentials);
@@ -495,28 +562,40 @@ int kr_tls_server_session(struct pl_quic_conn_sess_data *conn)
                }
        }
 
-       gnutls_priority_init2(&conn->priority, NULL, NULL, 0);
+       int ret = gnutls_priority_init2(&conn->priority, NULL, NULL, 0);
+       if (ret != GNUTLS_E_SUCCESS) {
+               kr_log_error(TLS, "gnutls_priority_init2(): %s (%d)\n",
+                               gnutls_strerror_name(ret), ret);
+               return ret;
+       }
 
        int flags = GNUTLS_SERVER | GNUTLS_NONBLOCK;
 #if GNUTLS_VERSION_NUMBER >= 0x030705
        if (gnutls_check_version("3.7.5"))
                flags |= GNUTLS_NO_TICKETS_TLS12;
 #endif
-       int ret = gnutls_init(&conn->tls_session, flags);
+       ret = gnutls_init(&conn->tls_session, flags);
        if (ret != GNUTLS_E_SUCCESS) {
-               kr_log_error(TLS, "gnutls_init(): %s (%d)\n", gnutls_strerror_name(ret), ret);
+               kr_log_error(TLS, "gnutls_init(): %s (%d)\n",
+                               gnutls_strerror_name(ret), ret);
                return ret;
        }
 
        gnutls_certificate_send_x509_rdn_sequence(conn->tls_session, 1);
        gnutls_certificate_server_set_request(conn->tls_session, GNUTLS_CERT_IGNORE);
        ret = gnutls_priority_set(conn->tls_session, conn->priority);
+       if (ret != GNUTLS_E_SUCCESS) {
+               kr_log_error(TLS, "gnutls_priority_set(): %s (%d)\n",
+                               gnutls_strerror_name(ret), ret);
+               return ret;
+       }
 
        conn->server_credentials = tls_credentials_reserve(the_network->tls_credentials);
        ret = gnutls_credentials_set(conn->tls_session, GNUTLS_CRD_CERTIFICATE,
                                     conn->server_credentials->credentials);
        if (ret != GNUTLS_E_SUCCESS) {
-               kr_log_error(TLS, "gnutls_credentials_set(): %s (%d)\n", gnutls_strerror_name(ret), ret);
+               kr_log_error(TLS, "gnutls_credentials_set(): %s (%d)\n",
+                               gnutls_strerror_name(ret), ret);
                return ret;
        }
 
@@ -527,7 +606,7 @@ int kr_tls_server_session(struct pl_quic_conn_sess_data *conn)
                kr_log_error(TLS, "setting priority '%s' failed at character %zd (...'%s') with %s (%d)\n",
                             tlsv13_priorities, errpos - tlsv13_priorities,
                             errpos, gnutls_strerror_name(err), err);
-               return kr_error(EINVAL);
+               return ret;
        }
 
        if (the_network->tls_session_ticket_ctx) {
@@ -539,7 +618,7 @@ int kr_tls_server_session(struct pl_quic_conn_sess_data *conn)
                .data = (void *)"doq",
                .size = 3
        };
-       gnutls_alpn_set_protocols(conn->tls_session, &alpn_datum, 1,
+       ret = gnutls_alpn_set_protocols(conn->tls_session, &alpn_datum, 1,
                        GNUTLS_ALPN_MANDATORY);
        if (ret != GNUTLS_E_SUCCESS) {
                kr_log_error(TLS, "gnutls_alpn_set_protocols(): %s (%d)\n", gnutls_strerror_name(ret), ret);
@@ -604,9 +683,8 @@ int quic_init_server_conn(struct pl_quic_conn_sess_data *conn,
                        conn->dec_cids.version,
                        now, true, conn->retry_sent,
                        conn);
-
-       if (ret >= 0) {
-               ret = tls_init_conn_session(conn, true);;
+       if (ret == 0) {
+               ret = tls_init_conn_session(conn, true);
        }
 
        return ret;
@@ -615,15 +693,18 @@ int quic_init_server_conn(struct pl_quic_conn_sess_data *conn,
 int quic_flush_streams(struct pl_quic_conn_sess_data *conn)
 {
        kr_require(conn);
-       node_t *s_node;
-       WALK_LIST(s_node, conn->streams) {
+       node_t *s_node = 0, *next = 0;
+       bool sent = false;
+       WALK_LIST_DELSAFE(s_node, next, conn->streams) {
                struct pl_quic_stream_sess_data *s =
                        container_of(s_node, struct pl_quic_stream_sess_data,
                                        list_node);
                /* Skip streams that have no data to send */
-               if (s->unsent_obuf == NULL)
+               if (s->write_closed || s->unsent_obuf == NULL) {
                        continue;
+               }
 
+               s->skip_update_time = true;
                if (session2_wrap_after(s->h.session,
                                PROTOLAYER_TYPE_DNS_SINGLE_STREAM,
                                protolayer_payload_wire_buf(
@@ -632,8 +713,14 @@ int quic_flush_streams(struct pl_quic_conn_sess_data *conn)
                                NULL,
                                NULL) < 0) {
                        /* Either closing or invalid */
-                       session2_close(s->h.session);
                }
+
+               s->skip_update_time = false;
+               sent = true;
+       }
+
+       if (sent) {
+               ngtcp2_conn_update_pkt_tx_time(conn->conn, quic_timestamp());
        }
 
        return kr_ok();
@@ -669,9 +756,13 @@ int send_special(ngtcp2_version_cid *dec_cids, kr_quic_table_t *table,
                struct pl_quic_conn_sess_data *conn,
                struct session2 *session, quic_doq_error_t *doq_error)
 {
+       if (kr_fails_assert(ctx)) {
+               return kr_error(EINVAL);
+       }
        char *err_buf = mm_alloc(&ctx->pool, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
        if (!err_buf)
                return kr_error(ENOMEM);
+
        struct wire_buf err_wb = {
                .buf = err_buf,
                .end = 0,
@@ -688,9 +779,12 @@ int send_special(ngtcp2_version_cid *dec_cids, kr_quic_table_t *table,
        ngtcp2_pkt_info pi = { 0 };
        uint8_t rnd = 0;
 
-       int ret = 0;
+       int ret = kr_error(EINVAL);
        switch (action) {
        case QUIC_SEND_VERSION_NEGOTIATION:
+               if (kr_fails_assert(!conn && dec_cids)) {
+                       break;
+               }
                kr_require(!conn);
                dnssec_random_buffer(&rnd, sizeof(rnd));
                uint32_t supported_quic[1] = { NGTCP2_PROTO_VER_V1 };
@@ -703,21 +797,27 @@ int send_special(ngtcp2_version_cid *dec_cids, kr_quic_table_t *table,
                );
                break;
        case QUIC_SEND_RETRY:
-               kr_require(dec_cids);
+               if (kr_fails_assert(dec_cids && table)) {
+                       break;
+               }
                ret = write_retry_packet(ctx->payload.wire_buf,
                                table, dec_cids,
-                               ctx->comm->src_addr,
-                               (uint8_t *)table->hash_secret,
-                               sizeof(table->hash_secret));
+                               ctx->comm->src_addr);
+
                if (conn) {
-                       QUIC_SET_CLOSING(conn);
+                       QUIC_SET_HS_ABORT(conn);
                }
                break;
        case QUIC_SEND_CONN_CLOSE:
-               if (!conn || !QUIC_CAN_SEND(conn)) {
+               if (kr_fails_assert(conn && session)) {
+                       break;
+               }
+               if (!QUIC_CAN_SEND(conn)) {
+                       ret = kr_ok();
                        break;
                }
 
+               /* Keep the more specific last error */
                if (conn->ccerr.error_code == DOQ_NO_ERROR) {
                        set_application_error(conn, *doq_error, NULL, 0);
                }
@@ -728,39 +828,58 @@ int send_special(ngtcp2_version_cid *dec_cids, kr_quic_table_t *table,
                        wire_buf_free_space_length(ctx->payload.wire_buf),
                        &conn->ccerr, now);
 
+               kr_log_debug(DOQ, "Connection entered closing state, waiting 3 PTO\n");
                QUIC_SET_CLOSING(conn);
+               quic_set_idle_timeout(conn->h.session, quic_get_closing_timeout(
+                                       ngtcp2_conn_get_pto(conn->conn)));
                break;
        /* Unused for now */
        // case QUIC_SEND_STATELESS_RESET:
        default:
-               ret = kr_error(EINVAL);
                break;
        }
 
        if (ret > 0) {
                wire_buf_consume(ctx->payload.wire_buf, ret);
-               session2_wrap_after(session,
-                               PROTOLAYER_TYPE_QUIC_CONN,
-                               ctx->payload,
-                               ctx->comm,
-                               ctx->finished_cb,
-                               ctx->finished_cb_baton);
+               if (session->proto == KR_PROTO_DOQ_CONN) {
+                       session2_wrap_after(session, PROTOLAYER_TYPE_QUIC_CONN,
+                                       ctx->payload, ctx->comm,
+                                       ctx->finished_cb,
+                                       ctx->finished_cb_baton);
+               } else {
+                       /* send special can be called from demux layer
+                        * where the conn session might not exist yet. */
+                       session2_wrap(session, ctx->payload, ctx->comm,
+                                       NULL, ctx->finished_cb,
+                                       ctx->finished_cb_baton);
+               }
                ret = kr_ok();
        }
 
-       mm_free(&ctx->pool, ctx->payload.wire_buf);
+       mm_free(&ctx->pool, ctx->payload.wire_buf->buf);
        ctx->payload.wire_buf = save_wb;
 
        return ret;
 }
 
+/* If the packet is INITIAL and the connection setup fails simply setting the
+ * conn->state to DRAINING will result in session teardown. */
 static enum protolayer_iter_cb_result pl_quic_conn_unwrap(void *sess_data,
                void *iter_data, struct protolayer_iter_ctx *ctx)
 {
        int ret = kr_ok();
        struct pl_quic_conn_sess_data *conn = sess_data;
+       if (conn->state & (QUIC_STATE_DRAINING | QUIC_STATE_HS_ABORT)) {
+               return protolayer_break(ctx, kr_ok());
+       }
+
+       /* Deferred payload (async processing) switches the buffer type
+        * (see protolayer_payload_ensure_long_lived) */
        if (ctx->payload.type == PROTOLAYER_PAYLOAD_BUFFER) {
                struct wire_buf *wb = mm_alloc(&ctx->pool, sizeof(struct wire_buf));
+               if (!wb) {
+                       return protolayer_break(ctx, kr_error(ENOMEM));
+               }
                wb->size = ctx->payload.buffer.len;
                wb->buf = ctx->payload.buffer.buf;
                wb->end = wb->size;
@@ -783,9 +902,12 @@ static enum protolayer_iter_cb_result pl_quic_conn_unwrap(void *sess_data,
        quic_doq_error_t doq_error;
        ret = handle_packet(conn, ctx, &doq_error);
        if (ret != kr_ok()) {
-               (void)send_special(&conn->dec_cids,
-                               conn->table_ref, ctx, ret, conn,
-                               conn->h.session, &doq_error);
+               if (ret != QUIC_SEND_NONE) {
+                       (void)send_special(&conn->dec_cids,
+                                       conn->table_ref, ctx, ret, conn,
+                                       conn->h.session, &doq_error);
+               }
+
                return protolayer_break(ctx, kr_ok());
        }
 
@@ -827,6 +949,24 @@ static enum protolayer_iter_cb_result pl_quic_conn_wrap(void *sess_data,
 
        kr_quic_set_addrs(ctx, &conn->path);
 
+       /* Flush bye message and promote state to DRAINING */
+       if (conn->state & QUIC_STATE_CLOSING) {
+               quic_doq_error_t doq_error = DOQ_NO_ERROR;
+               int ret = send_special(&conn->dec_cids,
+                               conn->table_ref,
+                               ctx,
+                               QUIC_SEND_CONN_CLOSE,
+                               conn,
+                               conn->h.session,
+                               &doq_error);
+               if (ret < 0) {
+                       kr_log_debug(DOQ, "sending CONNECTION_CLOSE failed: %d\n",
+                                       ret);
+               }
+               QUIC_SET_DRAINING(conn);
+               return protolayer_break(ctx, ret < 0 ? ret : 0);
+       }
+
        if (ctx->payload.type != PROTOLAYER_PAYLOAD_IOVEC) {
                ngtcp2_ssize sent = 0;
                ngtcp2_conn_info info = { 0 };
@@ -838,27 +978,21 @@ static enum protolayer_iter_cb_result pl_quic_conn_wrap(void *sess_data,
                }
 
                int nwrite = ngtcp2_conn_writev_stream(conn->conn,
-                               conn->path,
-                               &pi, wire_buf_free_space(ctx->payload.wire_buf),
+                               conn->path, &pi,
+                               wire_buf_free_space(ctx->payload.wire_buf),
                                wire_buf_free_space_length(ctx->payload.wire_buf),
                                &sent, NGTCP2_WRITE_STREAM_FLAG_NONE, -1, NULL,
                                0, quic_timestamp());
 
                if (nwrite > 0)
                        ngtcp2_conn_update_pkt_tx_time(conn->conn, quic_timestamp());
-               if (nwrite == 0)
+               if (nwrite == 0) {
                        quic_reset_expiry(conn);
+                       return protolayer_break(ctx, kr_ok());
+               }
 
-               if (nwrite <= 0) {
-                       if (nwrite == NGTCP2_ERR_NOMEM) {
-                               size_t new_size = ctx->payload.wire_buf->size + 1024;
-                               char *new_buf = realloc(ctx->payload.wire_buf->buf, new_size);
-                               kr_require(new_buf);
-                               ctx->payload.wire_buf->buf = new_buf;
-                               ctx->payload.wire_buf->size = new_size;
-                               ctx->payload.wire_buf->end = new_size;
-                       }
-
+               if (nwrite < 0) {
+                       ngtcp2_ccerr_set_liberr(&conn->ccerr, nwrite, NULL, 0);
                        return protolayer_break(ctx, kr_ok());
                }
 
@@ -893,7 +1027,8 @@ int quic_generate_secret(uint8_t *buf, size_t buflen)
        return kr_ok();
 }
 
-static int pl_quic_conn_sess_init(struct session2 *session, void *sess_data, void *param)
+static int pl_quic_conn_sess_init(struct session2 *session, void *sess_data,
+               void *param)
 {
        struct pl_quic_conn_sess_data *conn = sess_data;
        conn->state = 0;
@@ -914,21 +1049,6 @@ static int pl_quic_conn_sess_init(struct session2 *session, void *sess_data, voi
        memcpy(&conn->dec_cids, p->dec_cids, sizeof(ngtcp2_version_cid));
 
        struct comm_info *comm = p->comm_storage;
-       if (comm->src_addr) {
-               int len = kr_sockaddr_len(comm->src_addr);
-               kr_require(len > 0 && len <= sizeof(union kr_sockaddr));
-               memcpy(&conn->comm_storage.src_addr, comm->src_addr, len);
-       }
-       if (comm->comm_addr) {
-               int len = kr_sockaddr_len(comm->comm_addr);
-               kr_require(len > 0 && len <= sizeof(union kr_sockaddr));
-               memcpy(&conn->comm_storage.comm_addr, comm->comm_addr, len);
-       }
-       if (comm->dst_addr) {
-               int len = kr_sockaddr_len(comm->dst_addr);
-               kr_require(len > 0 && len <= sizeof(union kr_sockaddr));
-               memcpy(&conn->comm_storage.dst_addr, comm->dst_addr, len);
-       }
 
        conn->comm_storage = *comm;
        session->comm_storage = conn->comm_storage;
@@ -943,13 +1063,10 @@ static int pl_quic_conn_sess_init(struct session2 *session, void *sess_data, voi
        conn->tls_session = NULL;
        conn->server_credentials = NULL;
        if (unlikely(quic_generate_secret(conn->secret, sizeof(conn->secret)) != kr_ok())) {
-               pl_quic_conn_sess_deinit(conn->h.session, conn);
                kr_log_error(DOQ, "Failed to init connection session\n");
                return kr_error(EINVAL);
        }
 
-       quic_set_hs_timeout(session, QUIC_CONN_IDLE_TIMEOUT / NGTCP2_MILLISECONDS);
-
        return kr_ok();
 }
 
@@ -959,11 +1076,20 @@ static int pl_quic_conn_sess_deinit(struct session2 *session, void *sess_data)
        while (session2_tasklist_del_first(session, false) != NULL);
 
        session2_timer_stop(session);
-       struct pl_quic_stream_sess_data *s_node;
 
-       kr_log_debug(DOQ, "Closing connection, %s useful, served %zu streams\n",
-                       conn->finished_streams ? "was" : "wasn't",
-                       conn->finished_streams);
+       kr_require(session2_is_empty(session));
+       kr_require(EMPTY_LIST(conn->streams)); // NOLINT(bugprone-casting-through-void)
+       kr_require(conn->cid_pointers == 0);
+       kr_require(conn->streams_count == 0);
+
+       if (conn->state & QUIC_STATE_HANDSHAKE_DONE) {
+               kr_log_debug(DOQ, "Closing established connection, [%s] useful, served %zu streams\n",
+                               conn->finished_streams ? "was" : "was not",
+                               conn->finished_streams);
+       } else {
+               kr_log_debug(DOQ, "Closing connection with unfinished HS, wasn't useful, [%s] send retry\n",
+                               conn->retry_sent ? "did" : "did not");
+       }
 
        if (conn->priority) {
                gnutls_priority_deinit(conn->priority);
@@ -993,60 +1119,57 @@ static int pl_quic_conn_sess_deinit(struct session2 *session, void *sess_data)
        return kr_ok();
 }
 
+static int quic_bye(struct pl_quic_conn_sess_data *conn)
+{
+       if (!conn || !conn->conn) {
+               return kr_error(EINVAL);
+       }
+
+       struct protolayer_payload payload =
+               protolayer_payload_wire_buf(&conn->h.session->wire_buf, true);
+
+       return session2_wrap(conn->h.session,
+                       payload,
+                       &conn->comm_storage,
+                       NULL,
+                       /* finished_cb: Free to be used */NULL,
+                       /* finished_cb_baton: Free to be used */NULL);
+}
+
 static enum protolayer_event_cb_result pl_quic_conn_event_unwrap(
                enum protolayer_event_type event, void **baton,
                struct session2 *session, void *sess_data)
 {
        struct pl_quic_conn_sess_data *conn = sess_data;
-       if (event == PROTOLAYER_EVENT_DISCONNECT
-                       || event == PROTOLAYER_EVENT_CLOSE
-                       || event == PROTOLAYER_EVENT_FORCE_CLOSE
-                       || event == PROTOLAYER_EVENT_GENERAL_TIMEOUT
-                       || event == PROTOLAYER_EVENT_CONNECT_TIMEOUT) {
+       switch (event) {
+       case PROTOLAYER_EVENT_CLOSE:
+               QUIC_SET_CLOSING(conn);
+               (void)quic_bye(conn);
+               /* fallthrough */
+       case PROTOLAYER_EVENT_FORCE_CLOSE:
+       case PROTOLAYER_EVENT_CONNECT_TIMEOUT:
+       case PROTOLAYER_EVENT_GENERAL_TIMEOUT:
                while (queue_len(conn->pending_unwrap) > 0) {
                        struct session2 *s =
                                queue_head(conn->pending_unwrap)->h.session;
+                       queue_pop(conn->pending_unwrap);
                        session2_dec_refs(s);
                }
 
                node_t *s_node;
-               uint64_t last_stream_id = -1;
-               bool forced = false;
-               WALK_LIST_FIRST(s_node, conn->streams) {
+               node_t *temp_ptr;
+               WALK_LIST_DELSAFE(s_node, temp_ptr, conn->streams) {
                        struct pl_quic_stream_sess_data *s =
                                container_of(s_node,
                                                struct pl_quic_stream_sess_data,
                                                list_node);
 
-                       /* stream has a pending task to finish. If force
-                        * try force_close, otherwise defer conn termination */
-                       if (s->stream_id == last_stream_id) {
-                               if (event == PROTOLAYER_EVENT_FORCE_CLOSE
-                                               && !forced) {
-                                       session2_force_close(s->h.session);
-                                       forced = true;
-                                       continue;
-                               }
-
-                               session2_touch(session);
-                               session2_timer_restart(session);
-                               return PROTOLAYER_EVENT_CONSUME;
-                       }
-
-                       last_stream_id = s->stream_id;
-
-                       (void)ngtcp2_conn_shutdown_stream(s->conn, 0, s->stream_id, 0);
 
                        session2_close(s->h.session);
-                       /* These streams die with the connection, stream_close_cb
-                        * will not be called so adjust counters here. */
-                       --conn->streams_count;
-                       ++conn->finished_streams;
+                       s = NULL;
                }
 
-               if (!EMPTY_LIST(conn->streams)) {
-                       return PROTOLAYER_EVENT_CONSUME;
-               }
+               kr_require(EMPTY_LIST(conn->streams)); // NOLINT(bugprone-casting-through-void)
 
                if (!conn->disconnected) {
                        if (event == PROTOLAYER_EVENT_CLOSE) {
@@ -1056,10 +1179,14 @@ static enum protolayer_event_cb_result pl_quic_conn_event_unwrap(
                        }
                        conn->disconnected = true;
                }
+               /* fallthrough */
+       default:
+               /* Propagate into wrap direction of quic_conn session.
+                * If possible we will unref the session and free it.
+                * (in the unwrap direction the event propagation works only
+                * within the session, subsessions were handled manually) */
                return PROTOLAYER_EVENT_PROPAGATE;
        }
-
-       return PROTOLAYER_EVENT_PROPAGATE;
 }
 
 static enum protolayer_event_cb_result pl_quic_conn_event_wrap(
@@ -1074,21 +1201,25 @@ static enum protolayer_event_cb_result pl_quic_conn_event_wrap(
                if (baton && *baton) {
                        struct pl_quic_stream_sess_data *stream = *baton;
                        rem_node(&stream->list_node);
-                       session2_dec_refs(stream->h.session);
+                       stream->conn_ref->streams_count--;
+                       ngtcp2_conn_extend_max_streams_bidi(stream->conn, 1);
+                       ngtcp2_conn_shutdown_stream(stream->conn, 0,
+                                       stream->stream_id, 0);
+                       ngtcp2_conn_set_stream_user_data(conn->conn,
+                                       stream->stream_id, NULL);
+                       stream->conn = NULL;
+                       stream->conn_ref = NULL;
+                       uv_close((uv_handle_t *)&stream->h.session->timer,
+                                       on_session2_timer_close);
+                       return PROTOLAYER_EVENT_CONSUME;
                }
 
-               if (EMPTY_LIST(conn->streams) && conn->disconnected) {
+               // NOLINTNEXTLINE(bugprone-casting-through-void)
+               if (baton && EMPTY_LIST(conn->streams) && conn->disconnected) {
                        /* Connection can be terminated */
                        *baton = conn;
                        return PROTOLAYER_EVENT_PROPAGATE;
                }
-
-               /* Close terminates the timer. We failed to close
-                * due to a stream waiting for a task to finish
-                * so we restart the timer and try again */
-               session2_touch(session);
-               session2_timer_restart(session);
-               return PROTOLAYER_EVENT_CONSUME;
        }
 
        return PROTOLAYER_EVENT_PROPAGATE;
index 83de65aa5909dd317a448926f00157f5eeb9cf36..981b6198ded1856e52f5f24bd895a49b5037a247 100644 (file)
@@ -25,7 +25,10 @@ typedef enum {
        QUIC_STATE_AUTHORIZED     = (1 << 3),
        QUIC_STATE_EPROTO         = (1 << 4),
        QUIC_STATE_CLOSING        = (1 << 5),
+       /* Following states prevent connections from writing (retry packet
+        * is an exception, it doesn't require ngtcp2_conn struct). */
        QUIC_STATE_DRAINING       = (1 << 6),
+       QUIC_STATE_HS_ABORT       = (1 << 7),
 } quic_conn_state_t;
 
 /* Quic connection state set functions */
@@ -35,6 +38,8 @@ typedef enum {
        (conn)->state |= QUIC_STATE_CLOSING;
 #define QUIC_SET_HS_COMPLETED(conn) \
        (conn)->state |= QUIC_STATE_HANDSHAKE_DONE;
+#define QUIC_SET_HS_ABORT(conn) \
+       (conn)->state |= QUIC_STATE_HS_ABORT;
 #define QUIC_CAN_SEND(conn) \
        ((conn)->state < QUIC_STATE_DRAINING)
 
@@ -102,7 +107,38 @@ struct pl_quic_conn_sess_data {
        kr_quic_table_t *table_ref;
 };
 
+/* Iterates over the connections streams attempting to send out
+ * any data that failed to get sent previously. Also flushes
+ * any messages the connection itself might want to send. */
 int quic_flush_streams(struct pl_quic_conn_sess_data *conn);
+
+/* This function handles responses to special states from handle_packet(...),
+ * often followed by CONNECTION CLOSE. This function doesn't overwrite the
+ * buffer in the provided ctx and takes care of submitting the message
+ * to the wrap dirrection cascade.
+ * Currently sends special payload to the
+ * following QUIC_SEND requests:
+ *
+ * QUIC_SEND_VERSION_NEGOTIOATION
+ *   Response to a client using an unsupported QUIC version.
+ *
+ * QUIC_SEND_RETRY
+ *   sets the conn->state to QUIC_SET_HS_ABORT. Retry packets are sent
+ *   for address validation and are a response to the INITIAL packet.
+ *   Once the retry packet is sent the connection state is to be destroyed and
+ *   the client is expected to send a new INITIAL containing the address
+ *   validation token (as a response to the retry packet).
+ *
+ * QUIC_SEND_CONN_CLOSE
+ *   in case the ccerr for this connection is DOQ_NO_ERROR this function will
+ *   replace the ccerr with the DoQ error code passed to this function as
+ *   the doq_error parameter. If the current ccerr is not DOQ_NO_ERROR the ccerr
+ *   will not be changed to ensure more specific error code remains.
+ *
+ * Returns 0 on success,
+ * EINVAL if the requested state was missing some argument,
+ * ENOMEM if the function fails to allocate memory for the payload,
+ * or other negative error code from some ngtcp2 library function. */
 int send_special(ngtcp2_version_cid *dec_cids,
                kr_quic_table_t *table,
                struct protolayer_iter_ctx *ctx, int action,
index efba345b8b73077bc56a65a689122ff01cab7f81..92028a8eb88fd35ef24087e6ace4a955ab4440f4 100644 (file)
@@ -24,10 +24,11 @@ static int cmp_expiry_heap_nodes(void *c1, void *c2)
                        ((struct pl_quic_conn_sess_data *)c2)->h.heap_value)
                return 1;
 
-       /* If the heap_values equal stop the swapping subroutine as there is
-        * no clear hierarchy. This also protects new connections from being
-        * terminated by kr_quic_table_sweep right after initialisation. */
-       return -1;
+       if (((struct pl_quic_conn_sess_data *)c1)->h.heap_value <
+                       ((struct pl_quic_conn_sess_data *)c2)->h.heap_value)
+               return -1;
+
+       return 0;
 }
 
 static void conn_heap_reschedule(struct pl_quic_conn_sess_data *conn,
@@ -49,16 +50,6 @@ void quic_conn_mark_used(struct pl_quic_conn_sess_data *conn,
        conn_heap_reschedule(conn, table);
 }
 
-int kr_quic_table_rem2(kr_quic_cid_t **pcid, kr_quic_table_t *table)
-{
-       kr_quic_cid_t *cid = *pcid;
-       *pcid = cid->next;
-       free(cid);
-       table->pointers--;
-
-       return kr_ok();
-}
-
 void kr_quic_table_rem(struct pl_quic_conn_sess_data *conn,
                kr_quic_table_t *table)
 {
@@ -77,18 +68,28 @@ void kr_quic_table_rem(struct pl_quic_conn_sess_data *conn,
                                continue;
                        }
                        kr_quic_table_rem2(pcid, table);
-                       conn->cid_pointers--;
                }
 
                free(scids);
+       } else {
+               kr_quic_cid_t **pcid = kr_quic_table_lookup2(&conn->dcid, table);
+               if (kr_fails_assert(pcid != NULL && (*pcid) != NULL)) {
+                       /* Likely impossible without a significantly corrupt
+                        * state and/or presence of a programming error */
+                       kr_log_debug(DOQ, "Failed to remove connection cid from table, counters might not match\n");
+               } else {
+                       kr_quic_table_rem2(pcid, table);
+               }
+       }
+
+       kr_quic_cid_t **podcid = kr_quic_table_lookup2(&conn->odcid, table);
+       if (kr_fails_assert(podcid != NULL && *podcid != NULL)) {
+               kr_log_debug(DOQ, "Failed to remove connection cid from table, counters might not match\n");
+       } else {
+               kr_quic_table_rem2(podcid, table);
        }
        
        int pos = heap_find(table->expiry_heap, (heap_val_t *)conn);
-       /* Since deferred iteration context increases the session ref_count
-        * it is possible that the session will exist after being removed
-        * from the expiry heap. In such case no cid is found and the
-        * the heap_find function returns 0, which is not a valid value
-        * because the heap index starts at 1. */
        if (pos != 0) {
                heap_delete(table->expiry_heap, pos);
                table->usage--;
@@ -100,13 +101,7 @@ void kr_quic_table_free(kr_quic_table_t *table)
        if (!table)
                return;
 
-       while (!EMPTY_HEAP(table->expiry_heap)) {
-               struct pl_quic_conn_sess_data *c =
-                       *(struct pl_quic_conn_sess_data **)HHEAD(table->expiry_heap);
-
-               kr_quic_table_rem(c, table);
-       }
-
+       kr_require(EMPTY_HEAP(table->expiry_heap));
        kr_assert(table->usage == 0);
        kr_assert(table->pointers == 0);
 
@@ -116,88 +111,20 @@ void kr_quic_table_free(kr_quic_table_t *table)
        free(table);
 }
 
-void kr_quic_table_sweep(struct kr_quic_table *table,
-               struct protolayer_iter_ctx *ctx)
-{
-       uint64_t now = 0;
-       while (!EMPTY_HEAP(table->expiry_heap)) {
-               struct pl_quic_conn_sess_data *c =
-                       *(struct pl_quic_conn_sess_data **)
-                       HHEAD(table->expiry_heap);
-
-               if ((c->state & QUIC_STATE_BLOCKED)) {
-                       break;
-               /* when we reach the limit of open conns we lookup the most idle
-                * one but only close it if it received at least one query.
-                * This is to prevent closing brand new connections which has
-                * crippling effects on the number of answered queries when
-                * conn limits are reached. */
-               } else if (table->usage >= table->max_conns
-                               && c->finished_streams > 0
-                               && !c->disconnected) {
-                       quic_doq_error_t doq_error = DOQ_EXCESSIVE_LOAD;
-                       send_special(&c->dec_cids, c->table_ref, ctx,
-                                       QUIC_SEND_CONN_CLOSE, c, c->h.session,
-                                       &doq_error);
-                       session2_event(c->h.session->transport.parent,
-                                       PROTOLAYER_EVENT_DISCONNECT,
-                                       c);
-               } else if (c->state & QUIC_STATE_DRAINING) {
-                       session2_event(c->h.session->transport.parent,
-                                       PROTOLAYER_EVENT_DISCONNECT,
-                                       c);
-               // } else if (c->state & QUIC_STATE_CLOSING) {
-               //      quic_doq_error_t doq_error = DOQ_NO_ERROR;
-               //      send_special(&c->dec_cids, c->table_ref,
-               //                      ctx, QUIC_SEND_CONN_CLOSE,
-               //                      c, c->h.session, &doq_error);
-               //      session2_event(c->h.session->transport.parent,
-               //                      PROTOLAYER_EVENT_DISCONNECT,
-               //                      c);
-               } else if (kr_quic_conn_timeout(c, &now)) {
-                       int ret = ngtcp2_conn_handle_expiry(c->conn, now);
-                       if (ret != NGTCP2_NO_ERROR) {
-                               quic_doq_error_t doq_error = DOQ_NO_ERROR;
-                               /* see https://nghttp2.org/ngtcp2/ngtcp2_conn_handle_expiry.html */
-                               if (ret != NGTCP2_ERR_IDLE_CLOSE) {
-                                       send_special(&c->dec_cids, c->table_ref,
-                                                       ctx, QUIC_SEND_CONN_CLOSE,
-                                                       c, c->h.session, &doq_error);
-                               }
-                               session2_event(c->h.session->transport.parent,
-                                               PROTOLAYER_EVENT_DISCONNECT, c);
-                       } else {
-                               // quic_conn_mark_used(c, table);
-                       }
-               }
-               // HHEAD already handled, NOOP, avoid infinite loop
-               if (*(struct pl_quic_conn_sess_data **)
-                               HHEAD(table->expiry_heap) == c) {
-                       break;
-               }
-       }
-}
-
 static enum protolayer_iter_cb_result pl_quic_demux_unwrap(void *sess_data,
                void *iter_data, struct protolayer_iter_ctx *ctx)
 {
-       int ret = kr_ok();
        struct pl_quic_conn_sess_data *qconn = NULL;
        struct pl_quic_demux_sess_data *demux = sess_data;
 
-       /* Currently we only receive WIRE_BUF payload */
-       // if (ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF) {
-       //      kr_log_warning(DOQ, "Unexpected payload type in quic-demux\n");
-       //      return protolayer_break(ctx, kr_error(ENOTSUP));
-       // }
-
        bool retry_sent = false;
        ngtcp2_version_cid dec_cids;
        ngtcp2_cid odcid;
        ngtcp2_cid dcid;
        ngtcp2_cid scid;
 
-       ret = ngtcp2_pkt_decode_version_cid(&dec_cids,
+       kr_require(ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF);
+       int ret = ngtcp2_pkt_decode_version_cid(&dec_cids,
                        wire_buf_data(ctx->payload.wire_buf),
                        wire_buf_data_length(ctx->payload.wire_buf),
                        SERVER_DEFAULT_SCIDLEN);
@@ -220,11 +147,7 @@ static enum protolayer_iter_cb_result pl_quic_demux_unwrap(void *sess_data,
        qconn = kr_quic_table_lookup(&dcid, demux->conn_table);
        if (!qconn) {
                if (demux->conn_table->usage >= demux->conn_table->max_conns) {
-                       kr_quic_table_sweep(demux->conn_table, ctx);
-                       if (demux->conn_table->usage >= demux->conn_table->max_conns) {
-                               /* no luck */
-                               return protolayer_break(ctx, kr_ok());
-                       }
+                       return protolayer_break(ctx, kr_ok());
                }
 
                ngtcp2_pkt_hd header = { 0 };
@@ -274,12 +197,9 @@ static enum protolayer_iter_cb_result pl_quic_demux_unwrap(void *sess_data,
                                        QUIC_REGULAR_TOKEN_TIMEOUT, now);
                        }
                        if (ret != 0) {
-                               /* FIXME the generate string might not be correct */
                                kr_log_debug(DOQ, "Failed to verify retry or regular token: %s (%d)\n",
                                                ngtcp2_strerror(ret), ret);
                                return protolayer_break(ctx, kr_ok());
-                       } else {
-                               kr_log_debug(DOQ, "Retry or regular token successfully verified\n");
                        }
 
                } else {
@@ -287,7 +207,7 @@ static enum protolayer_iter_cb_result pl_quic_demux_unwrap(void *sess_data,
 
                        /* TODO remove 'likely' once outgoing DoQ is supported */
                        if (likely(!demux->h.session->outgoing)) {
-                               if (!init_unique_cid(&dcid, 0, demux->conn_table)) {
+                               if (init_unique_cid(&dcid, 0, demux->conn_table) != 0) {
                                        kr_log_debug(DOQ, "Failed to initialize unique cid (servers choice)\n");
                                        return protolayer_break(ctx, kr_ok());
                                }
@@ -316,23 +236,50 @@ static enum protolayer_iter_cb_result pl_quic_demux_unwrap(void *sess_data,
                                        1,
                                        false);
 
+               if (!new_conn_sess) {
+                       return protolayer_break(ctx, kr_error(ENOMEM));
+               }
+
                struct pl_quic_conn_sess_data *conn_sess_data =
                        protolayer_sess_data_get_proto(new_conn_sess,
                                        PROTOLAYER_TYPE_QUIC_CONN);
-               kr_quic_table_add(conn_sess_data, &dcid,
-                               demux->conn_table);
+               kr_quic_table_add(conn_sess_data, &dcid, demux->conn_table);
+               kr_quic_table_insert(conn_sess_data, &odcid, demux->conn_table);
                qconn = conn_sess_data;
-       }
 
-       ret = session2_unwrap(qconn->h.session,
-                       ctx->payload,
-                       ctx->comm,
-                       // NULL,
-                       ctx->finished_cb,
-                       ctx->finished_cb_baton);
+               /* We need the handling of the first packet of the connection
+                * to execute right away. Skip defer to make sure connection
+                * state contains correct flags when we check below. */
+               ret = session2_unwrap_after(qconn->h.session,
+                               PROTOLAYER_TYPE_DEFER,
+                               ctx->payload,
+                               ctx->comm,
+                               ctx->finished_cb,
+                               ctx->finished_cb_baton);
+               /* The connection failed to initialize. Either a general
+                * failure or a retry pkt was sent, both cases mean we
+                * can discard the current conn state (close the session) */
+               if (qconn->state >= QUIC_STATE_DRAINING) {
+                       session2_force_close(new_conn_sess);
+                       return protolayer_break(ctx, kr_ok());
+               }
+       } else {
+               ret = session2_unwrap(qconn->h.session,
+                               ctx->payload,
+                               ctx->comm,
+                               ctx->finished_cb,
+                               ctx->finished_cb_baton);
+       }
 
-       quic_conn_mark_used(qconn, demux->conn_table);
-       kr_quic_table_sweep(demux->conn_table, ctx);
+       /* The received message caused the conn to enter closing state,
+        * terminate session here since the protolayer_iter_ctx needs
+        * the session, otherwise mark used and sweep. */
+       if ((qconn->state & QUIC_STATE_DRAINING)
+               || (qconn->state & QUIC_STATE_CLOSING)) {
+               // pass
+       } else {
+               quic_conn_mark_used(qconn, demux->conn_table);
+       }
        return protolayer_break(ctx, kr_ok());
 }
 
@@ -348,17 +295,13 @@ kr_quic_table_t *kr_quic_table_new(size_t max_conns, size_t udp_payload,
                return NULL;
        }
 
+
        new_table->size = table_size;
        new_table->usage = 0;
+       new_table->pointers = 0;
        new_table->max_conns = max_conns;
        new_table->udp_payload_limit = udp_payload;
 
-       // kr_require(new_table->creds);
-       // ret = gnutls_certificate_allocate_credentials(&new_table->creds->credentials);
-       // if (ret != GNUTLS_E_SUCCESS)
-       //      goto fail;
-       //
-
        // NOTE: Taken from tls-proxy.c/96, we might need to use this
        // to enforce the use of tls1.3 (tls1.3 compat mode might be problematic)
        //
@@ -450,40 +393,6 @@ static int pl_quic_demux_sess_deinit(struct session2 *session, void *data)
        return kr_ok();
 }
 
-static int remove_connection_id(struct pl_quic_demux_sess_data *demux,
-               ngtcp2_cid *cid, void *user_data)
-{
-       kr_quic_cid_t **pcid = kr_quic_table_lookup2(cid, demux->conn_table);
-       if (!pcid || !*pcid) {
-               kr_log_error(DOQ, "Table doesn't contain cid that is to be removed\n");
-               return kr_error(EINVAL);
-       }
-
-       if ((*pcid)->conn_sess->cid_pointers <= 1) {
-               kr_log_error(DOQ, "Cannot remove all connection ids, protocol error\n");
-               return kr_error(EPROTO);
-       }
-
-       return kr_quic_table_rem2(pcid, demux->conn_table);
-}
-
-static int update_connection_id_map(struct pl_quic_demux_sess_data *demux,
-               struct pl_quic_conn_sess_data *conn)
-{
-       ngtcp2_cid *cid = &conn->dcid;
-       if (!init_unique_cid(cid, cid->datalen, demux->conn_table)) {
-               kr_log_error(DOQ, "Failed to create init new cid\n");
-               return kr_error(EINVAL);
-       }
-
-       if (kr_quic_table_insert(conn, cid, demux->conn_table) == NULL) {
-               kr_log_error(DOQ, "Failed to add new cid to conn map\n");
-               return kr_error(EINVAL);
-       }
-
-       return kr_ok();
-}
-
 static enum protolayer_event_cb_result pl_quic_demux_event_unwrap(
                enum protolayer_event_type event, void **baton,
                struct session2 *session, void *sess_data)
@@ -494,40 +403,12 @@ static enum protolayer_event_cb_result pl_quic_demux_event_unwrap(
                        struct pl_quic_conn_sess_data *c =
                                *(struct pl_quic_conn_sess_data **)HHEAD(
                                                demux->conn_table->expiry_heap);
-                       session2_force_close(c->h.session);
-               }
-
-               session2_dec_refs(session);
-               return PROTOLAYER_EVENT_CONSUME;
-       }
-
-       if (*baton == NULL) {
-               return PROTOLAYER_EVENT_PROPAGATE;
-       }
-
-       struct pl_quic_conn_sess_data *conn = *baton;
-
-       /* received NEW_CONNECTION_ID, update mapping to conn_sess_data */
-       if (event == PROTOLAYER_EVENT_CONNECT_UPDATE) {
-               if (update_connection_id_map(demux, conn) != kr_ok()) {
-                       event = PROTOLAYER_EVENT_DISCONNECT;
-                       /* fallthrough */
+                       ngtcp2_ccerr_set_application_error(&c->ccerr,
+                                       DOQ_NO_ERROR, NULL, 0);
+                       session2_event(c->h.session, event, NULL);
                }
        }
 
-       if (event == PROTOLAYER_EVENT_CONNECT_RETIRE) {
-               if (remove_connection_id(demux, &conn->dcid, conn) != kr_ok()) {
-                       event = PROTOLAYER_EVENT_DISCONNECT;
-                       /* fallthrough */
-               }
-       }
-
-       if (event == PROTOLAYER_EVENT_DISCONNECT ||
-                       event == PROTOLAYER_EVENT_CONNECT_TIMEOUT) {
-               session2_close(conn->h.session);
-               return PROTOLAYER_EVENT_CONSUME;
-       }
-
        return PROTOLAYER_EVENT_PROPAGATE;
 }
 
@@ -539,15 +420,14 @@ static enum protolayer_event_cb_result pl_quic_demux_event_wrap(
                        || event == PROTOLAYER_EVENT_CLOSE
                        || event == PROTOLAYER_EVENT_GENERAL_TIMEOUT
                        || event == PROTOLAYER_EVENT_CONNECT_TIMEOUT) {
-               kr_require(baton && *baton);
-               struct pl_quic_demux_sess_data *demux = sess_data;
-               struct pl_quic_conn_sess_data *conn = *baton;
-               kr_quic_table_rem(conn, demux->conn_table);
-
-               kr_require(EMPTY_LIST(conn->streams));
-               session2_dec_refs(conn->h.session);
-
-               return PROTOLAYER_EVENT_CONSUME;
+               /* Terminate subsession */
+               if (baton && *baton) {
+                       struct pl_quic_conn_sess_data *conn = *baton;
+                       kr_quic_table_rem(conn, conn->table_ref);
+                       uv_close((uv_handle_t *)&conn->h.session->timer,
+                                       on_session2_timer_close);
+                       return PROTOLAYER_EVENT_CONSUME;
+               }
        }
 
        return PROTOLAYER_EVENT_PROPAGATE;
index ac5208b3062c1a62e4713a8dceb2652bd3cb400c..3422a8c71cb8256bdadc89bc7ecb4a40923b3017 100644 (file)
@@ -19,7 +19,7 @@ static enum protolayer_iter_cb_result pl_quic_stream_unwrap(void *sess_data,
 {
        struct pl_quic_stream_sess_data *stream = sess_data;
 
-       if (!(stream->incflags & NGTCP2_STREAM_DATA_FLAG_FIN)) {
+       if (unlikely(!(stream->incflags & NGTCP2_STREAM_DATA_FLAG_FIN))) {
                return protolayer_break(ctx, kr_error(EINVAL));
        }
 
@@ -60,7 +60,7 @@ uint8_t *kr_quic_stream_add_data(struct pl_quic_stream_sess_data *s,
        }
 
        list_t *list = (list_t *)&s->outbufs;
-       if (EMPTY_LIST(*list)) {
+       if (EMPTY_LIST(*list)) { // NOLINT(bugprone-casting-through-void)
                s->unsent_obuf = obuf;
        }
        add_tail((list_t *)&s->outbufs, (node_t *)obuf);
@@ -79,7 +79,7 @@ static enum protolayer_iter_cb_result pl_quic_stream_wrap(void *sess_data,
        if (likely(ctx->payload.type == PROTOLAYER_PAYLOAD_IOVEC)) {
                if (unlikely(kr_quic_stream_add_data(stream, ctx->payload.iovec.iov[1].iov_base,
                                ctx->payload.iovec.iov[1].iov_len) == NULL)) {
-                       return kr_error(ENOMEM);
+                       return protolayer_break(ctx, kr_error(ENOMEM));
                }
 
                ctx->payload = protolayer_payload_wire_buf(&stream->outbuf,
@@ -97,11 +97,7 @@ static enum protolayer_iter_cb_result pl_quic_stream_wrap(void *sess_data,
                size_t uf = stream->unsent_offset;
                struct kr_quic_obuf *uo = stream->unsent_obuf;
                if (uo == NULL) {
-                       if (sent_msgs > 0) {
-                               break;
-                       }
-
-                       protolayer_break(ctx, kr_ok());
+                       break;
                }
 
                if (wire_buf_data_length(ctx->payload.wire_buf) != 0) {
@@ -114,6 +110,8 @@ static enum protolayer_iter_cb_result pl_quic_stream_wrap(void *sess_data,
                if (nwrite < 0) {
                        if (nwrite == NGTCP2_ERR_NOMEM) {
                                kr_log_error(DOQ, "Insufficient memory available\n");
+                       } else if (nwrite == NGTCP2_ERR_STREAM_SHUT_WR) {
+                               stream->write_closed = true;
                        }
 
                        return protolayer_break(ctx, kr_ok());
@@ -126,8 +124,8 @@ static enum protolayer_iter_cb_result pl_quic_stream_wrap(void *sess_data,
 
                protolayer_finished_cb finished_cb = NULL;
                void *finished_baton = NULL;
-               if (sent > 0 && stream->unsent_obuf == NULL && stream->h.session->outgoing
-                               && false) {
+               if (sent > 0 && stream->unsent_obuf == NULL
+                               && stream->h.session->outgoing) {
                        finished_cb = ctx->finished_cb;
                        finished_baton = ctx->finished_cb_baton;
                }
@@ -140,6 +138,11 @@ static enum protolayer_iter_cb_result pl_quic_stream_wrap(void *sess_data,
                                finished_baton);
        } while (sent > 0 && sent_msgs < QUIC_MAX_SEND_PER_RECV);
 
+       if (!stream->skip_update_time) {
+               ngtcp2_conn_update_pkt_tx_time(stream->conn_ref->conn,
+                               quic_timestamp());
+       }
+
        return protolayer_break(ctx, kr_ok());
 }
 
@@ -148,7 +151,7 @@ static int send_stream(struct pl_quic_stream_sess_data *stream,
                size_t len, bool fin, ngtcp2_ssize *sent)
 {
        if (!stream->conn_ref || !QUIC_CAN_SEND(stream->conn_ref)) {
-               return 0;
+               return kr_error(ENODATA);
        }
 
        int64_t stream_id = stream->stream_id;
@@ -198,9 +201,11 @@ static int pl_quic_stream_sess_init(struct session2 *session,
 
        struct kr_quic_stream_param *p = param;
        stream->conn = p->conn;
+       stream->closed = false;
        stream->stream_id = p->stream_id;
+       stream->write_closed = false;
+       stream->skip_update_time = false;
        session->comm_storage = p->comm_storage;
-
        if (stream->obufs_size == 0) {
                init_list(&stream->outbufs);
        } else {
@@ -216,14 +221,16 @@ void kr_quic_stream_ack_data(struct pl_quic_stream_sess_data *stream,
        struct list *obs = &stream->outbufs;
        struct kr_quic_obuf *first;
 
+       // NOLINTNEXTLINE(bugprone-casting-through-void)
        while (!EMPTY_LIST(*obs) && end_acked >=
                        (first = HEAD(*obs))->len + stream->first_offset) {
                rem_node(&first->node);
                stream->obufs_size -= first->len;
                stream->first_offset += first->len;
                if (stream->unsent_obuf == first) {
-                       stream->unsent_obuf =
-                               EMPTY_LIST(*obs) == 0 ? NULL : HEAD(*obs);
+                       // NOLINTNEXTLINE(bugprone-casting-through-void)
+                       stream->unsent_obuf = EMPTY_LIST(*obs)
+                               ? NULL : HEAD(*obs);
                        stream->unsent_offset = 0;
                }
                free(first);
@@ -233,19 +240,6 @@ void kr_quic_stream_ack_data(struct pl_quic_stream_sess_data *stream,
 static int pl_quic_stream_sess_deinit(struct session2 *session, void *sess_data)
 {
        struct pl_quic_stream_sess_data *stream = sess_data;
-
-       if (stream->ngtcp2_stream_active) {
-               if (!stream->conn) {
-                       kr_log_debug(DOQ, "Stream missing connection pointer, cannot update ngtcp2 stream limits\n");
-               } else {
-                       stream->ngtcp2_stream_active = false;
-                       ngtcp2_conn_extend_max_streams_bidi(stream->conn, 1);
-               }
-       }
-
-       if (!session2_tasklist_is_empty(session)) {
-               session2_tasklist_finalize_expired(session);
-       }
        kr_quic_stream_ack_data(stream, stream->stream_id, SIZE_MAX, false);
        wire_buf_deinit(&stream->pers_inbuf);
        wire_buf_deinit(&stream->outbuf);
@@ -265,11 +259,7 @@ static enum protolayer_event_cb_result pl_quic_stream_event_unwrap(
                                stream->stream_id,
                                SIZE_MAX,
                                false);
-
-               stream->conn = NULL;
-               stream->conn_ref = NULL;
        }
-
        return PROTOLAYER_EVENT_PROPAGATE;
 }
 
@@ -279,13 +269,9 @@ static enum protolayer_event_cb_result pl_quic_stream_event_wrap(
 {
        if (event == PROTOLAYER_EVENT_CLOSE
                        || event == PROTOLAYER_EVENT_FORCE_CLOSE) {
-               if (session2_is_empty(session)) {
-                       *baton = sess_data;
-                       return PROTOLAYER_EVENT_PROPAGATE;
-               }
-               return PROTOLAYER_EVENT_CONSUME;
+               kr_require(session2_is_empty(session));
+               *baton = sess_data;
        }
-
        return PROTOLAYER_EVENT_PROPAGATE;
 }
 
index 33808d9b0e9e18b2fdf004dbf0a6a0b63dd7f4c9..0c5bf5fd5e6844fd0d83cad88cb9bbfa5c7cbff8 100644 (file)
@@ -32,8 +32,10 @@ struct pl_quic_stream_sess_data {
        struct kr_quic_obuf *unsent_obuf;
        size_t first_offset;
        size_t unsent_offset;
-
+       bool closed;
+       bool write_closed;
        uint32_t incflags;
+       bool skip_update_time;
        uint64_t sdata_offset;
 
        struct pl_quic_conn_sess_data *conn_ref;
@@ -41,3 +43,5 @@ struct pl_quic_stream_sess_data {
 
 void kr_quic_stream_ack_data(struct pl_quic_stream_sess_data *stream,
                int64_t stream_id, size_t end_acked, bool keep_stream);
+uint8_t *kr_quic_stream_add_data(struct pl_quic_stream_sess_data *s,
+                  uint8_t *data, size_t len);
index d07119bfc97d25489641ec4b172318178672f42e..e99b7cc5ee5f3b338d4df67d6529d7781b73f45b 100644 (file)
@@ -80,6 +80,7 @@ static const enum protolayer_type protolayer_grp_doq_conn[] = {
 
 static const enum protolayer_type protolayer_grp_doq_demux[] = {
        PROTOLAYER_TYPE_UDP,
+       PROTOLAYER_TYPE_PROXYV2_STREAM,
        PROTOLAYER_TYPE_QUIC_DEMUX,
        PROTOLAYER_TYPE_NULL,
 };
@@ -1740,7 +1741,7 @@ static void on_session2_handle_close(uv_handle_t *handle)
        io_free(handle);
 }
 
-static void on_session2_timer_close(uv_handle_t *handle)
+void on_session2_timer_close(uv_handle_t *handle)
 {
        session2_dec_refs(handle->data);
 }
index 1ba05ae3154a15b7bf6a863d47c222d695de4a13..2a5fa967091c25109c1f4fe5e30f0529da3d2a9c 100644 (file)
@@ -1197,3 +1197,6 @@ static inline void session2_touch(struct session2 *session)
 {
        session->last_activity = kr_now();
 }
+
+/* Decrements the session ref_count, used as a function parameter of uv_close. */
+void on_session2_timer_close(uv_handle_t *handle);
index 61bf67b02a193458526289a13eb47c1ac5c76196..c49dc0df4c92c7b5cd0b5c327d246194216623fe 100644 (file)
                             "type": "integer",
                             "minimum": 1,
                             "maximum": 4096,
-                            "description": "Maximum number of concurrent streams a connection is allowed to open.",
+                            "description": "Maximum number of concurrent streams each connection is allowed to open. Each stream allocates > 64 KB of memory, setting this value too high might quicly consume a lot of memory. We recommend between 4 and 64.",
                             "default": 1024
                         },
                         "require-retry": {
index 6f84544ee9c878f139409424eb38af2da6de6f29..e017fc70c2d4a107f33520e9e7ac035d6cba80c7 100644 (file)
@@ -55,10 +55,10 @@ class QUICSchema(ConfigSchema):
 
     ---
     max_conns: Maximum number of active connections a single worker is allowed to accept.
-    max_streams: Maximum number of concurrent streams a connection is allowed to open.
+    max_streams: Maximum number of concurrent streams each connection is allowed to open. Each stream allocates > 64 KB of memory, setting this value too high might quicly consume a lot of memory. We recommend between 4 and 64.
     require_retry: Require address validation for unknown source addresses.
                     This adds a 1-RTT delay to connection establishment.
-    """
+    """  # noqa: E501
 
     max_conns: Int1_4096 = Int1_4096(1024)
     max_streams: Int1_4096 = Int1_4096(1024)