struct local_state *local_state;
};
typedef int kr_log_level_t;
-enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_REQDBG};
+enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_PROTOLAYER, LOG_GRP_REQDBG};
kr_layer_t kr_layer_t_static;
_Bool kr_dbg_assertion_abort;
struct local_state *local_state;
};
typedef int kr_log_level_t;
-enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_REQDBG};
+enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_PROTOLAYER, LOG_GRP_REQDBG};
kr_layer_t kr_layer_t_static;
_Bool kr_dbg_assertion_abort;
struct local_state *local_state;
};
typedef int kr_log_level_t;
-enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_REQDBG};
+enum kr_log_group {LOG_GRP_UNKNOWN = -1, LOG_GRP_SYSTEM = 1, LOG_GRP_CACHE, LOG_GRP_IO, LOG_GRP_NETWORK, LOG_GRP_TA, LOG_GRP_TLS, LOG_GRP_GNUTLS, LOG_GRP_TLSCLIENT, LOG_GRP_XDP, LOG_GRP_DOH, LOG_GRP_DNSSEC, LOG_GRP_HINT, LOG_GRP_PLAN, LOG_GRP_ITERATOR, LOG_GRP_VALIDATOR, LOG_GRP_RESOLVER, LOG_GRP_SELECTION, LOG_GRP_ZCUT, LOG_GRP_COOKIES, LOG_GRP_STATISTICS, LOG_GRP_REBIND, LOG_GRP_WORKER, LOG_GRP_POLICY, LOG_GRP_TASENTINEL, LOG_GRP_TASIGNALING, LOG_GRP_TAUPDATE, LOG_GRP_DAF, LOG_GRP_DETECTTIMEJUMP, LOG_GRP_DETECTTIMESKEW, LOG_GRP_GRAPHITE, LOG_GRP_PREFILL, LOG_GRP_PRIMING, LOG_GRP_SRVSTALE, LOG_GRP_WATCHDOG, LOG_GRP_NSID, LOG_GRP_DNSTAP, LOG_GRP_TESTS, LOG_GRP_DOTAUTH, LOG_GRP_HTTP, LOG_GRP_CONTROL, LOG_GRP_MODULE, LOG_GRP_DEVEL, LOG_GRP_RENUMBER, LOG_GRP_EDE, LOG_GRP_PROTOLAYER, LOG_GRP_REQDBG};
kr_layer_t kr_layer_t_static;
_Bool kr_dbg_assertion_abort;
'network.c',
'proxyv2.c',
'session.c',
+ 'session2.c',
'tls.c',
'tls_ephemeral_credentials.c',
'tls_session_ticket-srv.c',
--- /dev/null
+/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <ucw/lib.h>
+#include <sys/socket.h>
+
+#include "lib/log.h"
+#include "lib/utils.h"
+
+#include "daemon/session2.h"
+
+
+typedef void (*session2_push_cb)(struct session2 *s, int status,
+ void *target, void *baton);
+
+static int session2_transport_pushv(struct session2 *s,
+ const struct iovec *iov, int iovcnt,
+ void *target,
+ session2_push_cb cb, void *baton);
+static inline int session2_transport_push(struct session2 *s,
+ char *buf, size_t buf_len,
+ void *target,
+ session2_push_cb cb, void *baton);
+
+struct protolayer_globals protolayer_globals[PROTOLAYER_PROTOCOL_COUNT] = {0};
+
+
+enum protolayer_protocol protolayer_grp_doudp[] = {
+ PROTOLAYER_UDP,
+ PROTOLAYER_DNS_DGRAM,
+ PROTOLAYER_NULL
+};
+
+enum protolayer_protocol protolayer_grp_dotcp[] = {
+ PROTOLAYER_TCP,
+ PROTOLAYER_DNS_MSTREAM,
+ PROTOLAYER_NULL
+};
+
+enum protolayer_protocol protolayer_grp_dot[] = {
+ PROTOLAYER_TCP,
+ PROTOLAYER_TLS,
+ PROTOLAYER_DNS_MSTREAM,
+ PROTOLAYER_NULL
+};
+
+enum protolayer_protocol protolayer_grp_doh[] = {
+ PROTOLAYER_TCP,
+ PROTOLAYER_TLS,
+ PROTOLAYER_HTTP,
+ PROTOLAYER_DNS_DGRAM,
+ PROTOLAYER_NULL
+};
+
+
+enum protolayer_protocol *protolayer_grps[PROTOLAYER_GRP_COUNT] = {
+#define XX(id, name, desc) [PROTOLAYER_GRP_##id] = protolayer_grp_##name,
+ PROTOLAYER_GRP_MAP(XX)
+#undef XX
+};
+
+char *protolayer_grp_descs[PROTOLAYER_GRP_COUNT] = {
+#define XX(id, name, desc) [PROTOLAYER_GRP_##id] = desc,
+ PROTOLAYER_GRP_MAP(XX)
+#undef XX
+};
+
+
+/** Gets context for the layer with the specified index from the manager. */
+static inline struct protolayer_data *protolayer_manager_get(
+ struct protolayer_manager *m, size_t layer_ix)
+{
+ if (kr_fails_assert(layer_ix < m->num_layers))
+ return NULL;
+
+ const size_t *offsets = (size_t *)m->data;
+ char *pl_data_beg = m->data + (m->num_layers * sizeof(*offsets));
+ return (struct protolayer_data *)(pl_data_beg + offsets[layer_ix]);
+}
+
+static inline void protolayer_cb_ctx_next(struct protolayer_cb_ctx *ctx)
+{
+ if (ctx->direction == PROTOLAYER_UNWRAP)
+ ctx->layer_ix++;
+ else
+ ctx->layer_ix--;
+}
+
+static int protolayer_cb_ctx_finish(struct protolayer_cb_ctx *ctx, int ret,
+ bool reset_layers)
+{
+ if (reset_layers) {
+ struct protolayer_manager *m = ctx->manager;
+ struct protolayer_globals *globals = &protolayer_globals[m->grp];
+ for (size_t i = 0; i < m->num_layers; i++) {
+ struct protolayer_data *d = protolayer_manager_get(m, i);
+ if (globals->reset)
+ globals->reset(m, d);
+ }
+ }
+
+ if (ctx->status)
+ kr_log_debug(PROTOLAYER, "layer iteration of group '%s' ended with status %d",
+ protolayer_grp_descs[ctx->manager->grp], ret);
+
+ if (ctx->finished_cb)
+ ctx->finished_cb(ret, ctx->finished_cb_target,
+ ctx->finished_cb_baton);
+ free(ctx);
+ return ret;
+}
+
+/** Processes as many layers as possible synchronously, returning when either
+ * a layer has gone asynchronous, or when the whole sequence has finished.
+ *
+ * May be called multiple times on the same `ctx` to continue processing
+ * after an asynchronous operation. */
+static int protolayer_step(struct protolayer_cb_ctx *ctx)
+{
+ while (true) {
+ struct protolayer_data *ldata = protolayer_manager_get(
+ ctx->manager, ctx->layer_ix);
+ if (kr_fails_assert(ldata)) {
+ /* Probably layer index or data corruption */
+ return kr_error(EINVAL);
+ }
+
+ enum protolayer_protocol protocol = ldata->protocol;
+ struct protolayer_globals *globals = &protolayer_globals[protocol];
+
+ if (!ldata->processed) { /* Avoid repetition */
+ ctx->async_mode = false;
+ ctx->status = 0;
+ ctx->result = PROTOLAYER_CB_NULL;
+
+ protolayer_cb cb = (ctx->direction == PROTOLAYER_UNWRAP)
+ ? globals->unwrap : globals->wrap;
+
+ cb(ldata, ctx);
+ ldata->processed = true;
+ }
+
+ if (!ctx->result) {
+ ctx->async_mode = true;
+ return PROTOLAYER_RET_ASYNC; /* Next step is callback */
+ }
+
+ if (ctx->result == PROTOLAYER_CB_WAIT) {
+ kr_assert(ctx->status == 0);
+ return protolayer_cb_ctx_finish(
+ ctx, PROTOLAYER_RET_WAITING, false);
+ }
+
+ if (ctx->result == PROTOLAYER_CB_BREAK) {
+ kr_assert(ctx->status <= 0);
+ return protolayer_cb_ctx_finish(
+ ctx, PROTOLAYER_RET_NORMAL, true);
+ }
+
+ if (kr_fails_assert(ctx->status == 0)) {
+ /* Status should be zero without a BREAK. */
+ return protolayer_cb_ctx_finish(
+ ctx, kr_error(ECANCELED), true);
+ }
+
+ if (ctx->result == PROTOLAYER_CB_CONTINUE) {
+ protolayer_cb_ctx_next(ctx);
+ continue;
+ }
+
+ /* Should never get here */
+ kr_assert(false);
+ return protolayer_cb_ctx_finish(ctx, kr_error(EINVAL), true);
+ }
+}
+
+/** Submits the specified buffer to the sequence of layers represented by the
+ * specified protolayer manager. The sequence will be processed in the
+ * specified direction.
+ *
+ * Returns 0 when all layers have finished, 1 when some layers are asynchronous
+ * and waiting for continuation, 2 when a layer is waiting for more data,
+ * or a negative number for errors (kr_error). */
+static int protolayer_manager_submit(
+ struct protolayer_manager *manager,
+ enum protolayer_direction direction,
+ char *buf, size_t buf_len, void *target,
+ protolayer_finished_cb cb, void *baton)
+{
+ size_t layer_ix = (direction == PROTOLAYER_UNWRAP)
+ ? 0 : manager->num_layers - 1;
+
+ struct protolayer_cb_ctx *ctx = malloc(sizeof(*ctx)); // TODO - mempool?
+ kr_require(ctx);
+
+ *ctx = (struct protolayer_cb_ctx) {
+ .data = { .target = target },
+ .direction = direction,
+ .layer_ix = layer_ix,
+ .manager = manager,
+ .finished_cb = cb,
+ .finished_cb_target = target,
+ .finished_cb_baton = baton
+ };
+ protolayer_set_buffer(ctx, buf, buf_len);
+
+ return protolayer_step(ctx);
+}
+
+
+struct protolayer_manager *protolayer_manager_new(struct session2 *s,
+ enum protolayer_grp grp)
+{
+ if (kr_fails_assert(grp))
+ return NULL;
+
+ size_t num_layers = 0;
+ size_t size = sizeof(struct protolayer_manager);
+ enum protolayer_protocol *protocols = protolayer_grps[grp];
+ if (kr_fails_assert(protocols))
+ return NULL;
+ enum protolayer_protocol *p = protocols;
+
+ /* Space for offset index */
+ for (; *p; p++)
+ num_layers++;
+ if (kr_fails_assert(num_layers))
+ return NULL;
+ size_t offsets[num_layers];
+ size += sizeof(offsets);
+
+ /* Space for layer-specific data, guaranteeing alignment */
+ size_t total_data_size = 0;
+ for (size_t i = 0; i < num_layers; i++) {
+ offsets[i] = total_data_size;
+ size_t d = protolayer_globals[protocols[i]].data_size;
+ size += ALIGN_TO(d, CPU_STRUCT_ALIGN);
+ }
+ size += total_data_size;
+
+ /* Allocate and initialize manager */
+ struct protolayer_manager *m = malloc(size);
+ kr_require(m);
+ m->grp = grp;
+ m->session = s;
+ m->num_layers = num_layers;
+ memcpy(m->data, offsets, sizeof(offsets));
+
+ /* Initialize layer data */
+ for (size_t i = 0; i < num_layers; i++) {
+ struct protolayer_globals *globals = &protolayer_globals[protocols[i]];
+ struct protolayer_data *data = protolayer_manager_get(m, i);
+ data->protocol = protocols[i];
+ data->size = globals->data_size;
+ globals->init(m, data);
+ }
+
+ return m;
+}
+
+void protolayer_manager_free(struct protolayer_manager *m)
+{
+ if (!m) return;
+
+ for (size_t i = 0; i < m->num_layers; i++) {
+ struct protolayer_data *data = protolayer_manager_get(m, i);
+ protolayer_globals[data->protocol].deinit(m, data);
+ }
+
+ free(m);
+}
+
+void protolayer_continue(struct protolayer_cb_ctx *ctx)
+{
+ if (ctx->async_mode) {
+ protolayer_cb_ctx_next(ctx);
+ protolayer_step(ctx);
+ } else {
+ ctx->result = PROTOLAYER_CB_CONTINUE;
+ }
+}
+
+void protolayer_wait(struct protolayer_cb_ctx *ctx)
+{
+ if (ctx->async_mode) {
+ protolayer_cb_ctx_finish(ctx, PROTOLAYER_RET_WAITING, false);
+ } else {
+ ctx->result = PROTOLAYER_CB_WAIT;
+ }
+}
+
+void protolayer_break(struct protolayer_cb_ctx *ctx, int status)
+{
+ ctx->status = status;
+ if (ctx->async_mode) {
+ protolayer_cb_ctx_finish(ctx, PROTOLAYER_RET_NORMAL, true);
+ } else {
+ ctx->result = PROTOLAYER_CB_BREAK;
+ }
+}
+
+static void protolayer_push_finished(struct session2 *s, int status, void *target, void *baton)
+{
+ protolayer_break(baton, status);
+}
+
+void protolayer_pushv(struct protolayer_cb_ctx *ctx,
+ struct iovec *iov, int iovcnt,
+ void *target)
+{
+ int ret = session2_transport_pushv(ctx->manager->session, iov, iovcnt,
+ target, protolayer_push_finished, ctx);
+ if (ret && ctx->finished_cb)
+ ctx->finished_cb(ret, ctx->finished_cb_target,
+ ctx->finished_cb_baton);
+}
+
+void protolayer_push(struct protolayer_cb_ctx *ctx, char *buf, size_t buf_len,
+ void *target)
+{
+ int ret = session2_transport_push(ctx->manager->session, buf, buf_len,
+ target, protolayer_push_finished, ctx);
+ if (ret && ctx->finished_cb)
+ ctx->finished_cb(ret, ctx->finished_cb_target,
+ ctx->finished_cb_baton);
+}
+
+
+struct session2 *session2_new(enum session2_transport_type transport_type,
+ void *transport_ctx,
+ enum protolayer_grp layer_grp,
+ bool outgoing)
+{
+ kr_require(transport_type && transport_ctx && layer_grp);
+
+ struct session2 *s = malloc(sizeof(*s));
+ kr_require(s);
+
+ s->transport.type = transport_type;
+ s->transport.ctx = transport_ctx;
+
+ s->layers = protolayer_manager_new(s, layer_grp);
+ if (!s->layers) {
+ free(s);
+ return NULL;
+ }
+
+ s->outgoing = outgoing;
+
+ return s;
+}
+
+void session2_free(struct session2 *s)
+{
+ protolayer_manager_free(s->layers);
+ free(s);
+}
+
+int session2_unwrap(struct session2 *s, char *buf, size_t buf_len, void *target,
+ protolayer_finished_cb cb, void *baton)
+{
+ return protolayer_manager_submit(s->layers, PROTOLAYER_UNWRAP,
+ buf, buf_len, target, cb, baton);
+}
+
+int session2_wrap(struct session2 *s, char *buf, size_t buf_len, void *target,
+ protolayer_finished_cb cb, void *baton)
+{
+ return protolayer_manager_submit(s->layers, PROTOLAYER_WRAP,
+ buf, buf_len, target, cb, baton);
+}
+
+
+struct parent_pushv_ctx {
+ struct session2 *session;
+ session2_push_cb cb;
+ void *target;
+ void *baton;
+
+ char *buf;
+ size_t buf_len;
+};
+
+static void session2_transport_parent_pushv_finished(int status, void *target, void *baton)
+{
+ struct parent_pushv_ctx *ctx = baton;
+ if (ctx->cb)
+ ctx->cb(ctx->session, status, target, ctx->baton);
+ free(ctx->buf);
+ free(ctx);
+}
+
+static void session2_transport_udp_pushv_finished(uv_udp_send_t *req, int status)
+{
+ struct parent_pushv_ctx *ctx = req->data;
+ if (ctx->cb)
+ ctx->cb(ctx->session, status, ctx->target, ctx->baton);
+ free(ctx->buf);
+ free(ctx);
+ free(req);
+}
+
+static void session2_transport_stream_pushv_finished(uv_write_t *req, int status)
+{
+ struct parent_pushv_ctx *ctx = req->data;
+ if (ctx->cb)
+ ctx->cb(ctx->session, status, ctx->target, ctx->baton);
+ free(ctx->buf);
+ free(ctx);
+ free(req);
+}
+
+static int concat_iovs(const struct iovec *iov, int iovcnt, char **buf, size_t *buf_len)
+{
+ if (!iov || iovcnt <= 0)
+ return kr_error(ENODATA);
+
+ size_t len = 0;
+ for (int i = 0; i < iovcnt; i++) {
+ size_t old_len = len;
+ len += iov[i].iov_len;
+ if (kr_fails_assert(len >= old_len)) {
+ *buf = NULL;
+ return kr_error(EFBIG);
+ }
+ }
+
+ *buf_len = len;
+ if (len == 0) {
+ *buf = NULL;
+ return kr_ok();
+ }
+
+ *buf = malloc(len);
+ kr_require(*buf);
+
+ char *c = *buf;
+ for (int i = 0; i < iovcnt; i++) {
+ if (iov[i].iov_len == 0)
+ continue;
+ memcpy(c, iov[i].iov_base, iov[i].iov_len);
+ c += iov[i].iov_len;
+ }
+
+ return kr_ok();
+}
+
+static int session2_transport_pushv(struct session2 *s,
+ const struct iovec *iov, int iovcnt,
+ void *target,
+ session2_push_cb cb, void *baton)
+{
+ if (kr_fails_assert(s))
+ return kr_error(EINVAL);
+
+ struct parent_pushv_ctx *ctx = malloc(sizeof(*ctx));
+ kr_require(ctx);
+ *ctx = (struct parent_pushv_ctx) {
+ .session = s,
+ .cb = cb,
+ .baton = baton,
+ .target = target
+ };
+
+ switch (s->transport.type) {
+ case SESSION2_TRANSPORT_HANDLE:;
+ uv_handle_t *handle = s->transport.handle;
+ if (kr_fails_assert(handle)) {
+ free(ctx);
+ return kr_error(EINVAL);
+ }
+
+ if (handle->type == UV_UDP) {
+ uv_udp_send_t *req = malloc(sizeof(*req));
+ req->data = ctx;
+ uv_udp_send(req, (uv_udp_t *)handle,
+ (uv_buf_t *)iov, iovcnt, target,
+ session2_transport_udp_pushv_finished);
+ return kr_ok();
+ } else if (handle->type == UV_TCP) {
+ uv_write_t *req = malloc(sizeof(*req));
+ req->data = ctx;
+ uv_write(req, (uv_stream_t *)handle, (uv_buf_t *)iov, iovcnt,
+ session2_transport_stream_pushv_finished);
+ return kr_ok();
+ }
+
+ kr_assert(false && "Unsupported handle");
+ free(ctx);
+ return kr_error(EINVAL);
+
+ case SESSION2_TRANSPORT_PARENT:;
+ struct session2 *parent = s->transport.parent;
+ if (kr_fails_assert(parent)) {
+ free(ctx);
+ return kr_error(EINVAL);
+ }
+ int ret = concat_iovs(iov, iovcnt, &ctx->buf, &ctx->buf_len);
+ if (ret) {
+ free(ctx);
+ return ret;
+ }
+ session2_wrap(parent, ctx->buf, ctx->buf_len, target,
+ session2_transport_parent_pushv_finished, ctx);
+ return kr_ok();
+
+ default:
+ kr_assert(false && "Invalid transport");
+ free(ctx);
+ return kr_error(EINVAL);
+ }
+}
+
+struct push_ctx {
+ struct iovec iov;
+ session2_push_cb cb;
+ void *baton;
+};
+
+static void session2_transport_single_push_finished(struct session2 *s,
+ int status,
+ void *target, void *baton)
+{
+ struct push_ctx *ctx = baton;
+ if (ctx->cb)
+ ctx->cb(s, status, target, ctx->baton);
+ free(ctx);
+}
+
+static inline int session2_transport_push(struct session2 *s,
+ char *buf, size_t buf_len,
+ void *target,
+ session2_push_cb cb, void *baton)
+{
+ struct push_ctx *ctx = malloc(sizeof(*ctx));
+ kr_require(ctx);
+ *ctx = (struct push_ctx) {
+ .iov = {
+ .iov_base = buf,
+ .iov_len = buf_len
+ },
+ .cb = cb,
+ .baton = baton
+ };
+
+ return session2_transport_pushv(s, &ctx->iov, 1, target,
+ session2_transport_single_push_finished, ctx);
+}
--- /dev/null
+/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <uv.h>
+
+#include "contrib/mempattern.h"
+
+/* Forward declarations */
+struct session2;
+struct protolayer_cb_ctx;
+
+/** Protocol types - individual implementations of protocol layers. */
+enum protolayer_protocol {
+ PROTOLAYER_NULL = 0,
+ PROTOLAYER_TCP,
+ PROTOLAYER_UDP,
+ PROTOLAYER_TLS,
+ PROTOLAYER_HTTP,
+
+ PROTOLAYER_UDP_TO_QCONN,
+ PROTOLAYER_QCONN_TO_QSTREAM,
+
+ PROTOLAYER_DNS_DGRAM,
+ PROTOLAYER_DNS_MSTREAM, /* DoTCP allows multiple packets per stream */
+ PROTOLAYER_DNS_SSTREAM, /* DoQ only allows a single packet per stream */
+
+ PROTOLAYER_PROTOCOL_COUNT
+};
+
+#define PROTOLAYER_GRP_MAP(XX) \
+ XX(DOUDP, doudp, "DNS UDP") \
+ XX(DOTCP, dotcp, "DNS TCP") \
+ XX(DOT, dot, "DNS-over-TLS") \
+ XX(DOH, doh, "DNS-over-HTTPS")
+
+/** Pre-defined sequences of protocol layers. */
+enum protolayer_grp {
+ PROTOLAYER_GRP_NULL = 0,
+#define XX(id, name, desc) PROTOLAYER_GRP_##id,
+ PROTOLAYER_GRP_MAP(XX)
+#undef XX
+ PROTOLAYER_GRP_COUNT
+};
+
+/** Maps protocol layer group IDs to human-readable descriptions.
+ * E.g. PROTOLAYER_GRP_DOH has description 'DNS-over-HTTPS'. */
+extern char *protolayer_grp_descs[];
+
+/** Flow control indicators for protocol layer `wrap` and `unwrap` callbacks.
+ * Use with `protolayer_continue`, `protolayer_wait` and `protolayer_break`
+ * functions. */
+enum protolayer_cb_result {
+ PROTOLAYER_CB_NULL = 0,
+
+ PROTOLAYER_CB_CONTINUE,
+ PROTOLAYER_CB_WAIT,
+ PROTOLAYER_CB_BREAK,
+ PROTOLAYER_CB_PUSH,
+};
+
+enum protolayer_direction {
+ PROTOLAYER_WRAP,
+ PROTOLAYER_UNWRAP,
+};
+
+enum protolayer_ret {
+ /** Returned when a protolayer context iteration has finished
+ * processing, i.e. with _BREAK. */
+ PROTOLAYER_RET_NORMAL = 0,
+
+ /** Returned when a protolayer context iteration is waiting for an
+ * asynchronous callback to a continuation function. This will never be
+ * passed to `protolayer_finished_cb`, only returned by
+ * `session2_unwrap` or `session2_wrap`. */
+ PROTOLAYER_RET_ASYNC,
+
+ /** Returned when a protolayer context iteration has ended on a layer
+ * that needs more data from another buffer. */
+ PROTOLAYER_RET_WAITING,
+};
+
+/** Called when a context iteration (started by `session2_unwrap` or
+ * `session2_wrap`) has ended - i.e. the input buffer will not be processed
+ * any further.
+ *
+ * `status` may be one of `enum protolayer_ret` or a negative
+ * number indicating an error.
+ * `target` is the `target` parameter passed to the `session2_(un)wrap`
+ * function.
+ * `baton` is the `baton` parameter passed to the
+ * `session2_(un)wrap` function. */
+typedef void (*protolayer_finished_cb)(int status, void *target, void *baton);
+
+enum protolayer_cb_data_type {
+ PROTOLAYER_CB_DATA_NULL = 0,
+ PROTOLAYER_CB_DATA_BUFFER,
+ PROTOLAYER_CB_DATA_IOVEC,
+};
+
+/** Context for protocol layer callbacks, containing buffer data and internal
+ * information for protocol layer manager. */
+struct protolayer_cb_ctx {
+ /* read-write */
+
+ /** Data processed by the sequence of layers. All the data is always
+ * owned by its creator. It is also the layer (group) implementor's
+ * responsibility to keep data compatible in between layers. No data is
+ * ever (de-)allocated by the protolayer manager! */
+ struct {
+ enum protolayer_cb_data_type type;
+ union {
+ /** Only valid if `type` is `_BUFFER`. */
+ struct {
+ char *buf;
+ size_t len;
+ } buffer;
+
+ /** Only valid if `type` is `_IOVEC`. */
+ struct {
+ struct iovec *iov;
+ int cnt;
+ } iovec;
+ };
+ /** Always valid; may be `NULL`. */
+ void *target;
+ } data;
+
+ /* internal manager information - private */
+ enum protolayer_direction direction;
+ bool async_mode;
+ unsigned int layer_ix;
+ struct protolayer_manager *manager;
+ int status;
+ enum protolayer_cb_result result;
+
+ /* callback for when the layer iteration has ended - read-only */
+ protolayer_finished_cb finished_cb;
+ void *finished_cb_target;
+ void *finished_cb_baton;
+};
+
+/** Convenience function to put a buffer pointer to the specified context. */
+static inline void protolayer_set_buffer(struct protolayer_cb_ctx *ctx,
+ char *buf, size_t len)
+{
+ ctx->data.type = PROTOLAYER_CB_DATA_BUFFER;
+ ctx->data.buffer.buf = buf;
+ ctx->data.buffer.len = len;
+}
+
+/** Convenience function to put an iovec pointer to the specified context. */
+static inline void protolayer_set_iovec(struct protolayer_cb_ctx *ctx,
+ struct iovec *iov, int iovcnt)
+{
+ ctx->data.type = PROTOLAYER_CB_DATA_IOVEC;
+ ctx->data.iovec.iov = iov;
+ ctx->data.iovec.cnt = iovcnt;
+}
+
+
+/** Common header for per-session layer-specific data. When implementing
+ * a new layer, this is to be put at the beginning of the struct. */
+#define PROTOLAYER_DATA_HEADER struct {\
+ enum protolayer_protocol protocol;\
+ size_t size; /**< Size of the entire struct (incl. header) */\
+ bool processed; /**< Safeguard so that the layer does not get executed
+ * multiple times. */\
+}
+
+/** Per-session layer-specific data - generic struct. */
+struct protolayer_data {
+ PROTOLAYER_DATA_HEADER;
+ uint8_t data[];
+};
+
+typedef void (*protolayer_cb)(struct protolayer_data *layer,
+ struct protolayer_cb_ctx *ctx);
+typedef int (*protolayer_data_cb)(struct protolayer_manager *manager,
+ struct protolayer_data *layer);
+
+/** The default implementation for the `struct protolayer_globals::reset`
+ * callback. Simply calls the `deinit` and `init` callbacks. */
+int protolayer_data_reset_default(struct protolayer_manager *manager,
+ struct protolayer_data *layer);
+
+
+/** A collection of protocol layers and their layer-specific data. */
+struct protolayer_manager {
+ enum protolayer_grp grp;
+ struct session2 *session;
+ size_t num_layers;
+ char data[];
+};
+
+/** Allocates and initializes a new manager. */
+struct protolayer_manager *protolayer_manager_new(struct session2 *s,
+ enum protolayer_grp grp);
+
+/** Deinitializes all layer data in the manager and deallocates it. */
+void protolayer_manager_free(struct protolayer_manager *m);
+
+
+/** Global data for a specific layered protocol. */
+struct protolayer_globals {
+ size_t data_size; /**< Size of the layer-specific data struct. */
+ protolayer_data_cb init; /**< Initializes the layer-specific data struct. */
+ protolayer_data_cb deinit; /**< De-initializes the layer-specific data struct. */
+ protolayer_data_cb reset; /**< Resets the layer-specific data struct
+ * after finishing a sequence. Default
+ * implementation is available as
+ * `protolayer_data_reset_default`. */
+ protolayer_cb unwrap; /**< Strips the buffer of protocol-specific
+ * data. E.g. a HTTP layer removes HTTP
+ * status and headers. */
+ protolayer_cb wrap; /**< Wraps the buffer into protocol-specific
+ * data. E.g. a HTTP layer adds HTTP status
+ * and headers. */
+};
+
+/** Global data about layered protocols. Indexed by `enum protolayer_protocol`. */
+extern struct protolayer_globals protolayer_globals[PROTOLAYER_PROTOCOL_COUNT];
+
+/** *Continuation function* - signals the protolayer manager to continue
+ * processing the next layer. */
+void protolayer_continue(struct protolayer_cb_ctx *ctx);
+
+/** *Continuation function* - signals that the layer needs more data to produce
+ * a new buffer for the next layer. */
+void protolayer_wait(struct protolayer_cb_ctx *ctx);
+
+/** *Continuation function* - signals that the layer wants to stop processing
+ * of the buffer and clean up, possibly due to an error (indicated by
+ * `status`).
+ *
+ * `status` must be 0 or a negative integer. */
+void protolayer_break(struct protolayer_cb_ctx *ctx, int status);
+
+/** *Continuation function* - pushes data to the session's transport and
+ * signals that the layer wants to stop processing of the buffer and clean up.
+ *
+ * `target` is the target data for the transport - in most cases, it will be
+ * unused and may be `NULL`; except for UDP, where it must point to a `struct
+ * sockaddr_*` to indicate the target address.
+ *
+ * This function is meant to be called by the `wrap` callback of first layer in
+ * the sequence. */
+void protolayer_pushv(struct protolayer_cb_ctx *ctx,
+ struct iovec *iov, int iovcnt, void *target);
+
+/** *Continuation function* - pushes data to the session's transport and
+ * signals that the layer wants to stop processing of the buffer and clean up.
+ *
+ * `target` is the target data for the transport - in most cases, it will be
+ * unused and may be `NULL`; except for UDP, where it must point to a `struct
+ * sockaddr_*` to indicate the target address.
+ *
+ * This function is meant to be called by the `wrap` callback of first layer in
+ * the sequence. */
+void protolayer_push(struct protolayer_cb_ctx *ctx, char *buf, size_t buf_len,
+ void *target);
+
+
+/** Indicates how a session sends data in the `wrap` direction and receives
+ * data in the `unwrap` direction. */
+enum session2_transport_type {
+ SESSION2_TRANSPORT_NULL = 0,
+ SESSION2_TRANSPORT_HANDLE,
+ SESSION2_TRANSPORT_PARENT,
+};
+
+struct session2 {
+ struct {
+ enum session2_transport_type type;
+ union {
+ void *ctx;
+ uv_handle_t *handle;
+ struct session2 *parent;
+ };
+ } transport;
+
+ struct protolayer_manager *layers;
+ bool outgoing : 1;
+};
+
+/** Allocates and initializes a new session with the specified protocol layer
+ * group, and the provided transport context. */
+struct session2 *session2_new(enum session2_transport_type transport_type,
+ void *transport_ctx,
+ enum protolayer_grp layer_grp,
+ bool outgoing);
+
+/** Allocates and initializes a new session with the specified protocol layer
+ * group, using a *libuv handle* as its transport. */
+static inline struct session2 *session2_new_handle(uv_handle_t *handle,
+ enum protolayer_grp layer_grp,
+ bool outgoing)
+{
+ return session2_new(SESSION2_TRANSPORT_HANDLE, handle, layer_grp,
+ outgoing);
+}
+
+/** Allocates and initializes a new session with the specified protocol layer
+ * group, using a *parent session* as its transport. */
+static inline struct session2 *session2_new_child(struct session2 *parent,
+ enum protolayer_grp layer_grp,
+ bool outgoing)
+{
+ return session2_new(SESSION2_TRANSPORT_PARENT, parent, layer_grp,
+ outgoing);
+}
+
+/** De-allocates the session. */
+void session2_free(struct session2 *s);
+
+/** Sends the specified buffer to be processed in the `unwrap` direction by the
+ * session's protocol layers. The `target` parameter may contain a pointer to
+ * transport-specific data, e.g. for UDP, it shall contain a pointer to the
+ * sender's `struct sockaddr_*`.
+ *
+ * Once all layers are processed, `cb` is called with `baton` passed as one
+ * of its parameters. `cb` may also be `NULL`. See `protolayer_finished_cb` for
+ * more info.
+ *
+ * Returns one of `enum protolayer_ret` or a negative number
+ * indicating an error. */
+int session2_unwrap(struct session2 *s, char *buf, size_t buf_len, void *target,
+ protolayer_finished_cb cb, void *baton);
+
+/** Sends the specified buffer to be processed in the `wrap` direction by the
+ * session's protocol layers. The `target` parameter may contain a pointer to
+ * some data specific to the producer-consumer layer of this session.
+ *
+ * Once all layers are processed, `cb` is called with `baton` passed as one
+ * of its parameters. `cb` may also be `NULL`. See `protolayer_finished_cb` for
+ * more info.
+ *
+ * Returns one of `enum protolayer_ret` or a negative number
+ * indicating an error. */
+int session2_wrap(struct session2 *s, char *buf, size_t buf_len, void *target,
+ protolayer_finished_cb cb, void *baton);
GRP_NAME_ITEM(LOG_GRP_DEVEL),
GRP_NAME_ITEM(LOG_GRP_RENUMBER),
GRP_NAME_ITEM(LOG_GRP_EDE),
+ GRP_NAME_ITEM(LOG_GRP_PROTOLAYER),
GRP_NAME_ITEM(LOG_GRP_REQDBG),
{ NULL, LOG_GRP_UNKNOWN },
};
LOG_GRP_DEVEL,
LOG_GRP_RENUMBER,
LOG_GRP_EDE,
+ LOG_GRP_PROTOLAYER,
/* ^^ Add new log groups above ^^. */
LOG_GRP_REQDBG, /* Must be first non-displayed entry in enum! */
};
#define LOG_GRP_DEVEL_TAG "devel" /**< ``devel``: for development purposes */
#define LOG_GRP_RENUMBER_TAG "renum" /**< ``renum``: operation related to renumber */
#define LOG_GRP_EDE_TAG "exterr" /**< ``exterr``: extended error module */
+#define LOG_GRP_PROTOLAYER_TAG "prlayr" /**< ``prlayr``: protocol layer system (session2) */
#define LOG_GRP_REQDBG_TAG "reqdbg" /**< ``reqdbg``: debug logs enabled by policy actions */
///@}