*/
#include "quic_conn.h"
-#include "engine.h"
-#include "lib/generic/trie.h"
-#include "lib/log.h"
-#include "lib/resolve.h"
-#include "network.h"
#include "quic_stream.h"
#include "quic_common.h"
#include "libdnssec/random.h"
#include "libdnssec/error.h"
-#include "session2.h"
#include "worker.h"
-#include <libknot/wire.h>
-#include <ngtcp2/ngtcp2.h>
-#include <uv.h>
#define EPHEMERAL_CERT_EXPIRATION_SECONDS_RENEW_BEFORE ((time_t)60*60*24*7)
struct protolayer_iter_ctx *ctx)
{
uint64_t now = quic_timestamp();
- const ngtcp2_path *path = ngtcp2_conn_get_path(conn->conn);
ngtcp2_pkt_info pi = { .ecn = NGTCP2_ECN_NOT_ECT, };
int ret = -1;
if (ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF) {
- ret = ngtcp2_conn_read_pkt(conn->conn, path, &pi,
+ ret = ngtcp2_conn_read_pkt(conn->conn, conn->path, &pi,
wire_buf_data(ctx->payload.wire_buf),
wire_buf_data_length(ctx->payload.wire_buf), now);
} else {
- ret = ngtcp2_conn_read_pkt(conn->conn, path, &pi,
+ ret = ngtcp2_conn_read_pkt(conn->conn, conn->path, &pi,
ctx->payload.buffer.buf,
ctx->payload.buffer.len, now);
}
switch (ret) {
case NGTCP2_ERR_RETRY:
+ /* "Server must perform address validation by sending Retry packet
+ * (see ngtcp2_crypto_write_retry() and ngtcp2_pkt_write_retry()),
+ * and discard the connection state. Client application does
+ * not get this error code." */
+ QUIC_SET_CLOSING(conn);
return QUIC_SEND_RETRY;
case NGTCP2_ERR_DROP_CONN:
QUIC_SET_DRAINING(conn);
break;
case NGTCP2_ERR_CLOSING:
/* Since we received the CLOSING, we are not allowed to
- * send last packet, therefore DRAINING */
+ * send last packet -> elevate to DRAINING */
QUIC_SET_DRAINING(conn);
break;
case NGTCP2_ERR_CRYPTO:
if (datalen == 0) {
/* This is invalid see ngtcp2_recv_stream_data doc */
if (!(flags & NGTCP2_STREAM_DATA_FLAG_FIN)) {
- // return NGTCP2_PROTOCOL_VIOLATION;
return NGTCP2_ERR_CALLBACK_FAILURE;
}
if (offset == 0) {
memcpy(wire_buf_free_space(&stream->pers_inbuf), data, datalen);
- kr_require(wire_buf_consume(&stream->pers_inbuf, datalen) == kr_ok());
+ wire_buf_consume(&stream->pers_inbuf, datalen);
} else {
/* remove size header from new data and add it to the start of wb. */
memcpy(wire_buf_free_space(&stream->pers_inbuf), data + sizeof(uint16_t), datalen - sizeof(uint16_t));
knot_wire_write_u16(wire_buf_data(&stream->pers_inbuf),
knot_wire_read_u16(wire_buf_data(&stream->pers_inbuf)) + datalen - sizeof(uint16_t));
- kr_require(wire_buf_consume(&stream->pers_inbuf, datalen - sizeof(uint16_t)) == kr_ok());
+ wire_buf_consume(&stream->pers_inbuf, datalen - sizeof(uint16_t));
}
- /* we can ignore ret return value, it can only be ENOMEM, at which point
- * there is nothing we can do anyway and the connection will timeout cleanly */
(void)ngtcp2_conn_extend_max_stream_offset(ngconn, stream_id, datalen);
ngtcp2_conn_extend_max_offset(ngconn, datalen);
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);
}
++conn->streams_count;
- kr_require(ngtcp2_conn_set_stream_user_data(ngconn, stream_id,
- stream) == NGTCP2_NO_ERROR);
+ ngtcp2_conn_set_stream_user_data(ngconn, stream_id, stream);
return NGTCP2_NO_ERROR;
}
.acked_stream_data_offset = acked_stream_data_offset_cb,
.stream_open = stream_open_cb,
.stream_close = stream_close_cb,
- // .recv_stateless_rst, TODO - OPTIONAL
+ // .recv_stateless_reset, - OPTIONAL
// .ngtcp2_crypto_recv_retry_cb, - OPTIONAL
// .extend_max_streams_bidi - OPTIONAL
// .extend_max_streams_uni - OPTIONAL
.update_key = ngtcp2_crypto_update_key_cb,
// .path_validation, - OPTIONAL
// .select_preferred_addr - OPTIONAL
- // .recv_stream_rst, TODO - OPTIONAL
+ // .stream_rst, - OPTIONAL
// .extend_max_remote_streams_bidi - OPTIONAL
// .extend_max_remote_streams_uni - OPTIONAL
// .extend_max_stream_data, - OPTIONAL
}
}
-static void kr_quic_set_addrs(struct protolayer_iter_ctx *ctx, ngtcp2_path *path)
+static void kr_quic_set_addrs(struct protolayer_iter_ctx *ctx, ngtcp2_path **path)
{
const struct sockaddr *remote = NULL;
const struct sockaddr *local = NULL;
local = session2_get_sockname(ctx->session);
}
- path->remote.addr = (struct sockaddr *)remote;
- path->remote.addrlen = kr_sockaddr_len(remote);
- path->local.addr = (struct sockaddr *)local;
- path->local.addrlen = kr_sockaddr_len(local);
+ (*path)->remote.addr = (struct sockaddr *)remote;
+ (*path)->remote.addrlen = kr_sockaddr_len(remote);
+ (*path)->local.addr = (struct sockaddr *)local;
+ (*path)->local.addrlen = kr_sockaddr_len(local);
}
int kr_tls_server_session(struct pl_quic_conn_sess_data *conn)
}
gnutls_certificate_send_x509_rdn_sequence(conn->tls_session, 1);
- gnutls_certificate_server_set_request(conn->tls_session, GNUTLS_CERT_REQUEST);
+ gnutls_certificate_server_set_request(conn->tls_session, GNUTLS_CERT_IGNORE);
ret = gnutls_priority_set(conn->tls_session, conn->priority);
conn->server_credentials = tls_credentials_reserve(the_network->tls_credentials);
}
uint64_t now = quic_timestamp();
- ngtcp2_path path;
- kr_quic_set_addrs(ctx, &path);
- int ret = conn_new_handler(&conn->conn, &path,
+ int ret = conn_new_handler(&conn->conn, conn->path,
&conn->scid, &conn->dcid, &conn->odcid,
conn->dec_cids.version,
now, true, false,
int send_special(struct pl_quic_conn_sess_data *conn,
struct protolayer_iter_ctx *ctx, int action)
{
- kr_require(ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF);
- char *err_buf = mm_alloc(&ctx->pool, /* FIXME */2048);
+ 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,
- .size = 2048 /*FIXME*/,
+ .size = NGTCP2_MAX_UDP_PAYLOAD_SIZE,
.start = 0,
};
break;
case QUIC_SEND_RETRY:
- if (!conn || !ctx->comm || ! ctx->comm->target) {
- kr_log_error(DOQ, "unable to send Retry packet due to missing data\n");
+ if (!conn || !ctx->comm) {
+ kr_log_error(DOQ, "unable to send Retry packet\n");
break;
}
- kr_require(conn && ctx->comm->target);
init_random_cid(&new_dcid, 0);
if (wire_buf_free_space_length(ctx->payload.wire_buf) <
ctx->payload.wire_buf->size = NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN;
ctx->payload.wire_buf->end = ctx->payload.wire_buf->size;
}
-
+
ret = ngtcp2_crypto_generate_retry_token(
retry_token, conn->secret,
sizeof(conn->secret), conn->dec_cids.version,
ccerr.error_code = KR_QUIC_ERR_EXCESSIVE_LOAD;
ret = ngtcp2_conn_write_connection_close(
conn->conn,
- (ngtcp2_path *)ngtcp2_conn_get_path(conn->conn),
+ conn->path,
&pi,
wire_buf_free_space(ctx->payload.wire_buf),
wire_buf_free_space_length(ctx->payload.wire_buf),
}
if (ret > 0) {
- kr_require(wire_buf_consume(ctx->payload.wire_buf, ret) == kr_ok());
- session2_wrap(conn->h.session,
+ wire_buf_consume(ctx->payload.wire_buf, ret);
+ session2_wrap_after(conn->h.session,
+ PROTOLAYER_TYPE_QUIC_CONN,
ctx->payload,
ctx->comm,
- NULL,
ctx->finished_cb,
ctx->finished_cb_baton);
ret = kr_ok();
ctx->payload = protolayer_payload_wire_buf(wb, false);
}
+ kr_quic_set_addrs(ctx, &conn->path);
+
if (!conn->conn) {
copy_comm_storage(conn, &ctx->comm_storage);
while (queue_len(conn->pending_unwrap) > 0) {
session2_unwrap(queue_head(conn->pending_unwrap)->h.session,
ctx->payload,
- &conn->comm_storage,
+ NULL /* &conn->comm_storage */,
ctx->finished_cb,
ctx->finished_cb_baton);
queue_pop(conn->pending_unwrap);
ngtcp2_ssize sent = 0;
ngtcp2_pkt_info pi = { 0 };
- kr_require(ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF);
if (wire_buf_data_length(ctx->payload.wire_buf) > 0) {
return protolayer_continue(ctx);
}
- int nwrite = ngtcp2_conn_writev_stream(conn->conn,
- (ngtcp2_path *)ngtcp2_conn_get_path(conn->conn),
+ int nwrite = ngtcp2_conn_writev_stream(conn->conn,
+ 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,
return protolayer_break(ctx, kr_error(EINVAL));
}
- kr_require(wire_buf_consume(ctx->payload.wire_buf, nwrite) == kr_ok());
+ wire_buf_consume(ctx->payload.wire_buf, nwrite);
}
ngtcp2_conn_update_pkt_tx_time(conn->conn, quic_timestamp());
int quic_generate_secret(uint8_t *buf, size_t buflen)
{
- kr_require(buf != NULL && buflen > 0 && buflen <= 32);
+ if (unlikely(buf == NULL || buflen > 32)) {
+ return kr_error(EINVAL);
+ }
uint8_t rand[16], hash[32];
int ret = dnssec_random_buffer(rand, sizeof(rand));
if (ret != DNSSEC_EOK) {
{
struct pl_quic_conn_sess_data *conn = sess_data;
conn->state = 0;
+ conn->path = calloc(1, sizeof(ngtcp2_path));
+ if (!conn->path) {
+ return kr_error(ENOMEM);
+ }
- kr_require(param);
struct kr_quic_conn_param *p = param;
conn->dcid = p->dcid;
conn->scid = p->scid;
conn->comm_storage = *comm;
session->comm_storage = conn->comm_storage;
-
queue_init(conn->pending_unwrap);
conn->is_server = !session->outgoing;
conn->streams_count = 0;
conn->tls_session = NULL;
conn->server_credentials = NULL;
- if (quic_generate_secret(conn->secret, sizeof(conn->secret)) != kr_ok()) {
+ 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);
struct pl_quic_conn_sess_data *conn = sess_data;
while (session2_tasklist_del_first(session, false) != NULL);
- kr_log_debug(DOQ, "Closing connection, %s useful, served %zu streams\n",
+ kr_log_info(DOQ, "Closing connection, %s useful, served %zu streams\n",
conn->finished_streams ? "was" : "wasn't",
conn->finished_streams);
--conn->streams_count;
}
- kr_require(conn->streams_count == 0);
if (conn->priority) {
gnutls_priority_deinit(conn->priority);
}
kr_log_error(DOQ, "Client side of QUIC is not implemented\n");
}
+ if (conn->path) {
+ free(conn->path);
+ }
+
conn->priority = NULL;
conn->tls_session = NULL;
conn->server_credentials = NULL;
#include "libdnssec/random.h"
#include "contrib/openbsd/siphash.h"
-#include "quic_conn.h"
-#include "session2.h"
-#include "worker.h"
-#include <ngtcp2/ngtcp2.h>
-#include <sys/cdefs.h>
#include "quic_demux.h"
#define BUCKETS_PER_CONNS 8 // Each connecion has several dCIDs, and each CID takes one hash table bucket.
static uint64_t cid2hash(const ngtcp2_cid *cid, kr_quic_table_t *table)
{
SIPHASH_CTX ctx;
- kr_require(table->hash_secret != NULL);
SipHash24_Init(&ctx, (const SIPHASH_KEY *)(table->hash_secret));
SipHash24_Update(&ctx, cid->data, MIN(cid->datalen, 8));
uint64_t ret = SipHash24_End(&ctx);
session2_event(c->h.session->transport.parent,
PROTOLAYER_EVENT_DISCONNECT,
NULL);
-
-
} else if (kr_quic_conn_timeout(c, &now)) {
int ret = ngtcp2_conn_handle_expiry(c->conn, now);
if (ret != NGTCP2_NO_ERROR) {
struct pl_quic_conn_sess_data *qconn = NULL;
struct pl_quic_demux_sess_data *demux = sess_data;
- if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF)) {
- kr_log_warning(DOQ, "Unexpected payload type in quic-conn\n");
- return protolayer_break(ctx, kr_error(ENOTSUP));
- }
+ /* 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));
+ // }
ngtcp2_version_cid dec_cids;
ngtcp2_cid odcid;
memcpy(&odcid, &dcid, sizeof(odcid));
}
- if (!demux->h.session->outgoing) {
+ /* TODO remove likely once outgoing DoQ is supported */
+ if (likely(!demux->h.session->outgoing)) {
if (!init_unique_cid(&dcid, 0, demux->conn_table)) {
kr_log_error(DOQ, "Failed to initialize unique cid (servers choice)\n");
return protolayer_break(ctx, kr_ok());
1,
false);
- new_conn_sess->comm_storage = demux->h.session->comm_storage;
-
struct pl_quic_conn_sess_data *conn_sess_data =
protolayer_sess_data_get_proto(new_conn_sess,
PROTOLAYER_TYPE_QUIC_CONN);
ret = session2_unwrap(qconn->h.session,
ctx->payload,
ctx->comm,
+ // NULL,
ctx->finished_cb,
ctx->finished_cb_baton);
- quic_conn_mark_used(qconn, demux->conn_table);
- // kr_quic_table_sweep(demux->conn_table, ctx);
+
+ if (!QUIC_CAN_SEND(qconn)) {
+ /* Explicitly remove the connection session. */
+ session2_event(qconn->h.session->transport.parent,
+ PROTOLAYER_EVENT_DISCONNECT,
+ NULL);
+ return protolayer_break(ctx, kr_ok());
+ }
+ quic_conn_mark_used(qconn, demux->conn_table);
+ kr_quic_table_sweep(demux->conn_table, ctx);
return protolayer_break(ctx, kr_ok());
}
}
struct tls_credentials *creds = the_network->tls_credentials;
- kr_require(creds->credentials != NULL);
if (!quic->conn_table) {
quic->conn_table = kr_quic_table_new(QUIC_MAX_OPEN_CONNS,
kr_log_error(DOQ, "Failed to create QUIC connection table\n");
return kr_error(ENOMEM);
}
-
- kr_require(quic->conn_table);
}
return kr_ok();
struct session2 *session, void *sess_data)
{
struct pl_quic_demux_sess_data *demux = sess_data;
+ if (event == PROTOLAYER_EVENT_CLOSE || event == PROTOLAYER_EVENT_FORCE_CLOSE) {
+ while (!EMPTY_HEAP(demux->conn_table->expiry_heap)) {
+ struct pl_quic_conn_sess_data *c =
+ *(struct pl_quic_conn_sess_data **)HHEAD(
+ demux->conn_table->expiry_heap);
+ kr_quic_table_rem(c, demux->conn_table);
+ session2_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) {
- kr_require(*baton);
- struct pl_quic_conn_sess_data *conn = *baton;
if (update_connection_id_map(demux, conn) != kr_ok()) {
event = PROTOLAYER_EVENT_DISCONNECT;
/* fallthrough */
}
if (event == PROTOLAYER_EVENT_CONNECT_RETIRE) {
- kr_require(*baton);
- struct pl_quic_conn_sess_data *conn = *baton;
if (remove_connection_id(demux, &conn->dcid, conn) != kr_ok()) {
event = PROTOLAYER_EVENT_DISCONNECT;
/* fallthrough */
}
}
- if (event == PROTOLAYER_EVENT_CLOSE || event == PROTOLAYER_EVENT_FORCE_CLOSE) {
- while (!EMPTY_HEAP(demux->conn_table->expiry_heap)) {
- struct pl_quic_conn_sess_data *c =
- *(struct pl_quic_conn_sess_data **)HHEAD(
- demux->conn_table->expiry_heap);
- kr_quic_table_rem(c, demux->conn_table);
- session2_event(c->h.session,
- PROTOLAYER_EVENT_DISCONNECT, NULL);
- }
-
- session2_dec_refs(session);
- return PROTOLAYER_EVENT_CONSUME;
- }
-
if (event == PROTOLAYER_EVENT_DISCONNECT ||
event == PROTOLAYER_EVENT_CONNECT_TIMEOUT) {
- if (*baton == NULL)
- return PROTOLAYER_EVENT_CONSUME;
-
- struct pl_quic_conn_sess_data *conn = *baton;
kr_quic_table_rem(conn, demux->conn_table);
- session2_event(conn->h.session, PROTOLAYER_EVENT_DISCONNECT, NULL);
+ session2_close(conn->h.session);
return PROTOLAYER_EVENT_CONSUME;
}
#include "lib/resolve.h"
#include "quic_common.h"
-#include "quic_conn.h"
-#include "session2.h"
-#include <ngtcp2/ngtcp2.h>
#include "quic_stream.h"
-#define OUTBUF_SIZE 4096
-
/* forward declaration */
static int send_stream(struct pl_quic_stream_sess_data *stream,
struct protolayer_iter_ctx *ctx, uint8_t *data,
return protolayer_break(ctx, kr_error(EINVAL));
}
- kr_assert(stream->incflags & NGTCP2_STREAM_DATA_FLAG_FIN);
ctx->payload = protolayer_payload_wire_buf(&stream->pers_inbuf, false);
return protolayer_continue(ctx);
}
uint8_t *kr_quic_stream_add_data(struct pl_quic_stream_sess_data *s,
uint8_t *data, size_t len)
{
- if (s == NULL) {
- return NULL;
- }
-
size_t prefix = sizeof(uint16_t);
struct kr_quic_obuf *obuf = malloc(sizeof(*obuf) + prefix + len);
static enum protolayer_iter_cb_result pl_quic_stream_wrap(void *sess_data,
void *iter_data, struct protolayer_iter_ctx *ctx)
{
- kr_require(ctx->payload.type = PROTOLAYER_PAYLOAD_IOVEC);
struct pl_quic_stream_sess_data *stream = sess_data;
- kr_require(stream->stream_id >= 0);
ngtcp2_ssize sent = 0;
- kr_quic_stream_add_data(stream, ctx->payload.iovec.iov[1].iov_base,
- ctx->payload.iovec.iov[1].iov_len);
+ 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);
+ }
ctx->payload = protolayer_payload_wire_buf(&stream->outbuf, false);
*sent = 0;
}
- kr_require(wire_buf_consume(ctx->payload.wire_buf, nwrite) == kr_ok());
+ wire_buf_consume(ctx->payload.wire_buf, nwrite);
return nwrite;
}
void kr_quic_stream_mark_sent(struct pl_quic_stream_sess_data *stream,
size_t amount_sent)
{
- kr_require(stream);
-
stream->unsent_offset += amount_sent;
kr_assert(stream->unsent_offset <= stream->unsent_obuf->len);
if (stream->unsent_offset == stream->unsent_obuf->len) {
struct pl_quic_stream_sess_data *stream = sess_data;
stream->h.session = session;
- wire_buf_init(&stream->pers_inbuf, OUTBUF_SIZE);
- wire_buf_init(&stream->outbuf, OUTBUF_SIZE);
+ wire_buf_init(&stream->pers_inbuf, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+ wire_buf_init(&stream->outbuf, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
session->secure = true;
- kr_require(param);
struct kr_quic_stream_param *p = param;
stream->conn = p->conn;
stream->stream_id = p->stream_id;
- stream->comm_storage = p->comm_storage;
+ session->comm_storage = p->comm_storage;
if (stream->obufs_size == 0) {
init_list(&stream->outbufs);
void kr_quic_stream_ack_data(struct pl_quic_stream_sess_data *stream,
int64_t stream_id, size_t end_acked, bool keep_stream)
{
- kr_require(stream);
struct list *obs = &stream->outbufs;
struct kr_quic_obuf *first;
}
}
-int update_stream_pers_buffer(struct pl_quic_stream_sess_data *stream,
- const uint8_t *data, size_t len, int64_t stream_id)
-{
- kr_require(len > 0 && data && stream);
-
- if (wire_buf_free_space_length(&stream->pers_inbuf) < len) {
- size_t inc = MIN(stream->outbuf.size, 1024);
- char *new_buf = realloc(stream->pers_inbuf.buf,
- wire_buf_data_length(&stream->pers_inbuf) + inc);
- kr_require(new_buf);
- stream->pers_inbuf.buf = new_buf;
- stream->pers_inbuf.end += inc;
- stream->pers_inbuf.size += inc;
- }
-
- memcpy(wire_buf_free_space(&stream->pers_inbuf), data, len);
- kr_require(wire_buf_consume(&stream->pers_inbuf, len) == kr_ok());
-
- return kr_ok();
-}
-
static int pl_quic_stream_sess_deinit(struct session2 *session, void *sess_data)
{
struct pl_quic_stream_sess_data *stream = sess_data;
ngtcp2_conn_shutdown_stream(stream->conn, 0, stream->stream_id, 0);
- kr_require(queue_len(session->waiting) <= 0);
kr_quic_stream_ack_data(stream, stream->stream_id, SIZE_MAX, false);
wire_buf_deinit(&stream->pers_inbuf);
wire_buf_deinit(&stream->outbuf);