Verified Commit ab4816f2 authored by Grigorii Demidov's avatar Grigorii Demidov Committed by Vladimír Čunát

daemon: logic around struct session was relocated to separate module; input...

daemon: logic around struct session was relocated to separate module; input data buffering scheme was changed (libuv); attempt was made to simplify processing of the stream
parent 776b6ad1
......@@ -9,6 +9,7 @@ kresd_SOURCES := \
daemon/tls_ephemeral_credentials.c \
daemon/tls_session_ticket-srv.c \
daemon/zimport.c \
daemon/session.c \
daemon/main.c
kresd_DIST := daemon/lua/kres.lua daemon/lua/kres-gen.lua \
......
This diff is collapsed.
......@@ -25,33 +25,6 @@
struct tls_ctx_t;
struct tls_client_ctx_t;
/* Per-session (TCP or UDP) persistent structure,
* that exists between remote counterpart and a local socket.
*/
struct session {
bool outgoing; /**< True: to upstream; false: from a client. */
bool throttled;
bool has_tls;
bool connected;
bool closing;
union inaddr peer;
uv_handle_t *handle;
uv_timer_t timeout;
struct qr_task *buffering; /**< Worker buffers the incomplete TCP query here. */
struct tls_ctx_t *tls_ctx;
struct tls_client_ctx_t *tls_client_ctx;
uint8_t msg_hdr[4]; /**< Buffer for DNS message header. */
ssize_t msg_hdr_idx; /**< The number of bytes in msg_hdr filled so far. */
qr_tasklist_t tasks;
qr_tasklist_t waiting;
ssize_t bytes_to_skip;
};
void session_free(struct session *s);
struct session *session_new(void);
int udp_bind(uv_udp_t *handle, struct sockaddr *addr);
int udp_bindfd(uv_udp_t *handle, int fd);
int tcp_bind(uv_tcp_t *handle, struct sockaddr *addr);
......
This diff is collapsed.
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdbool.h>
#include <uv.h>
#include "lib/generic/array.h"
struct qr_task;
struct worker_ctx;
struct session;
/* Allocate new session. */
struct session *session_new(void);
/* Clear and free given session. */
void session_free(struct session *s);
/* Clear session. */
void session_clear(struct session *s);
/** Close session. */
void session_close(struct session *session);
/** Start reading from underlying libuv IO handle. */
int session_start_read(struct session *session);
/** List of tasks been waiting for IO. */
/** Check if list is empty. */
bool session_waitinglist_is_empty(const struct session *session);
/** Get the first element. */
struct qr_task *session_waitinglist_get_first(const struct session *session);
/** Get the list length. */
size_t session_waitinglist_get_len(const struct session *session);
/** Add task to the list. */
int session_waitinglist_add(struct session *session, struct qr_task *task);
/** Remove task from the list. */
int session_waitinglist_del(struct session *session, struct qr_task *task);
/** Remove task from the list by index. */
int session_waitinglist_del_index(struct session *session, int index);
/** Retry resolution for each task in the list. */
void session_waitinglist_retry(struct session *session, bool increase_timeout_cnt);
/** Finalize all tasks in the list. */
void session_waitinglist_finalize(struct session *session, int status);
/** List of tasks associated with session. */
/** Check if list is empty. */
bool session_tasklist_is_empty(const struct session *session);
/** Get the first element. */
struct qr_task *session_tasklist_get_first(const struct session *session);
/** Get the list length. */
size_t session_tasklist_get_len(const struct session *session);
/** Add task to the list. */
int session_tasklist_add(struct session *session, struct qr_task *task);
/** Remove task from the list. */
int session_tasklist_del(struct session *session, struct qr_task *task);
/** Remove task from the list by index. */
int session_tasklist_del_index(struct session *session, int index);
/** Find task with given msg_id */
struct qr_task* session_tasklist_find(const struct session *session, uint16_t msg_id);
/** Finalize all tasks in the list. */
void session_tasklist_finalize(struct session *session, int status);
/** Both of task lists (associated & waiting). */
/** Check if empty. */
bool session_is_empty(const struct session *session);
/** Finalize all tasks. */
void session_tasks_finalize(struct session *session, int status);
/** Operations with flags */
bool session_is_outgoing(const struct session *session);
void session_set_outgoing(struct session *session, bool outgoing);
bool session_is_closing(const struct session *session);
void session_set_closing(struct session *session, bool closing);
bool session_is_connected(const struct session *session);
void session_set_connected(struct session *session, bool connected);
bool session_is_throttled(const struct session *session);
void session_set_throttled(struct session *session, bool throttled);
bool session_has_tls(const struct session *session);
void session_set_has_tls(struct session *session, bool has_tls);
bool session_wirebuf_error(struct session *session);
/** Get peer address. */
struct sockaddr *session_get_peer(struct session *session);
/** Get pointer to server-side tls-related data. */
struct tls_ctx_t *session_tls_get_server_ctx(const struct session *session);
/** Set pointer to server-side tls-related data. */
void session_tls_set_server_ctx(struct session *session, struct tls_ctx_t *ctx);
/** Get pointer to client-side tls-related data. */
struct tls_client_ctx_t *session_tls_get_client_ctx(const struct session *session);
/** Set pointer to client-side tls-related data. */
void session_tls_set_client_ctx(struct session *session, struct tls_client_ctx_t *ctx);
/** Get pointer to that part of tls-related data which has common structure for
* server and client. */
struct tls_common_ctx *session_tls_get_common_ctx(const struct session *session);
/** Get pointer to underlying libuv handle for IO operations. */
uv_handle_t *session_get_handle(struct session *session);
/** Set pointer to libuv handle for IO operations. */
int session_set_handle(struct session *session, uv_handle_t *handle);
/** Get pointer to session timer handle. */
uv_timer_t *session_get_timer(struct session *session);
/** Start session timer. */
int session_timer_start(struct session *session, uv_timer_cb cb,
uint64_t timeout, uint64_t repeat);
/** Restart session timer without changing it parameters. */
int session_timer_restart(struct session *session);
/** Stop session timer. */
int session_timer_stop(struct session *session);
/** Get start of session buffer for wire data. */
uint8_t *session_wirebuf_get_start(struct session *session);
/** Get size of session wirebuffer. */
size_t session_wirebuf_get_size(struct session *session);
/** Get length of data in the session wirebuffer. */
size_t session_wirebuf_get_len(struct session *session);
/** Get start of free space in session wirebuffer. */
uint8_t *session_wirebuf_get_free_start(struct session *session);
/** Get amount of free space in session wirebuffer. */
size_t session_wirebuf_get_free_size(struct session *session);
int session_wirebuf_process(struct session *session);
ssize_t session_wirebuf_consume(struct session *session,
const uint8_t *data, ssize_t len);
/** poison session structure with ASAN. */
void session_poison(struct session *session);
/** unpoison session structure with ASAN. */
void session_unpoison(struct session *session);
knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm);
int session_discard_packet(struct session *session, const knot_pkt_t *pkt);
......@@ -34,6 +34,7 @@
#include "daemon/io.h"
#include "daemon/tls.h"
#include "daemon/worker.h"
#include "daemon/session.h"
#define EPHEMERAL_CERT_EXPIRATION_SECONDS_RENEW_BEFORE 60*60*24*7
#define GNUTLS_PIN_MIN_VERSION 0x030400
......@@ -354,9 +355,10 @@ void tls_close(struct tls_common_ctx *ctx)
assert(ctx->session);
if (ctx->handshake_state == TLS_HS_DONE) {
const struct sockaddr *peer = session_get_peer(ctx->session);
kr_log_verbose("[%s] closing tls connection to `%s`\n",
ctx->client_side ? "tls_client" : "tls",
kr_straddr(&ctx->session->peer.ip));
kr_straddr(peer));
ctx->handshake_state = TLS_HS_CLOSING;
gnutls_bye(ctx->tls_session, GNUTLS_SHUT_RDWR);
}
......@@ -384,12 +386,11 @@ int tls_write(uv_write_t *req, uv_handle_t *handle, knot_pkt_t *pkt, uv_write_cb
return kr_error(EINVAL);
}
struct session *session = handle->data;
struct tls_common_ctx *tls_ctx = session->outgoing ? &session->tls_client_ctx->c :
&session->tls_ctx->c;
struct session *s = handle->data;
struct tls_common_ctx *tls_ctx = session_tls_get_common_ctx(s);
assert (tls_ctx);
assert (session->outgoing == tls_ctx->client_side);
assert (session_is_outgoing(s) == tls_ctx->client_side);
const uint16_t pkt_size = htons(pkt->size);
const char *logstring = tls_ctx->client_side ? client_logstring : server_logstring;
......@@ -426,17 +427,16 @@ int tls_write(uv_write_t *req, uv_handle_t *handle, knot_pkt_t *pkt, uv_write_cb
return kr_ok();
}
int tls_process(struct worker_ctx *worker, uv_stream_t *handle, const uint8_t *buf, ssize_t nread)
ssize_t tls_process_input_data(struct session *s, const uint8_t *buf, ssize_t nread)
{
struct session *session = handle->data;
struct tls_common_ctx *tls_p = session->outgoing ? &session->tls_client_ctx->c :
&session->tls_ctx->c;
struct tls_common_ctx *tls_p = session_tls_get_common_ctx(s);
if (!tls_p) {
return kr_error(ENOSYS);
}
assert(tls_p->session == session);
assert(tls_p->session == s);
assert(tls_p->recv_buf == buf && nread <= sizeof(tls_p->recv_buf));
const char *logstring = tls_p->client_side ? client_logstring : server_logstring;
tls_p->buf = buf;
......@@ -455,9 +455,13 @@ int tls_process(struct worker_ctx *worker, uv_stream_t *handle, const uint8_t *b
}
/* See https://gnutls.org/manual/html_node/Data-transfer-and-termination.html#Data-transfer-and-termination */
int submitted = 0;
ssize_t submitted = 0;
bool is_retrying = false;
uint64_t retrying_start = 0;
uint8_t *wire_buf = session_wirebuf_get_free_start(s);
size_t wire_buf_size = session_wirebuf_get_free_size(s);
while (true) {
ssize_t count = gnutls_record_recv(tls_p->tls_session, tls_p->recv_buf, sizeof(tls_p->recv_buf));
ssize_t count = gnutls_record_recv(tls_p->tls_session, wire_buf, wire_buf_size);
if (count == GNUTLS_E_AGAIN) {
break; /* No data available */
} else if (count == GNUTLS_E_INTERRUPTED) {
......@@ -479,17 +483,15 @@ int tls_process(struct worker_ctx *worker, uv_stream_t *handle, const uint8_t *b
kr_log_verbose("[%s] gnutls_record_recv failed: %s (%zd)\n",
logstring, gnutls_strerror_name(count), count);
return kr_error(EIO);
}
DEBUG_MSG("[%s] submitting %zd data to worker\n", logstring, count);
int ret = worker_process_tcp(worker, handle, tls_p->recv_buf, count);
if (ret < 0) {
return ret;
}
if (count <= 0) {
} else if (count == 0) {
break;
}
submitted += ret;
DEBUG_MSG("[%s] received %zd data\n", logstring, count);
wire_buf += count;
wire_buf_size -= count;
submitted += count;
}
assert(tls_p->consumed == tls_p->nread);
return submitted;
}
......@@ -1127,13 +1129,13 @@ int tls_client_connect_start(struct tls_client_ctx_t *client_ctx,
return kr_error(EINVAL);
}
assert(session->outgoing && session->handle->type == UV_TCP);
assert(session_is_outgoing(session) && session_get_handle(session)->type == UV_TCP);
struct tls_common_ctx *ctx = &client_ctx->c;
gnutls_session_set_ptr(ctx->tls_session, client_ctx);
gnutls_handshake_set_timeout(ctx->tls_session, ctx->worker->engine->net.tcp.tls_handshake_timeout);
session->tls_client_ctx = client_ctx;
session_tls_set_client_ctx(session, client_ctx);
ctx->handshake_cb = handshake_cb;
ctx->handshake_state = TLS_HS_IN_PROGRESS;
ctx->session = session;
......
......@@ -134,7 +134,7 @@ int tls_write(uv_write_t *req, uv_handle_t* handle, knot_pkt_t * pkt, uv_write_c
/*! Unwrap incoming data from a TLS stream and pass them to TCP session.
* @return the number of newly-completed requests (>=0) or an error code
*/
int tls_process(struct worker_ctx *worker, uv_stream_t *handle, const uint8_t *buf, ssize_t nread);
ssize_t tls_process_input_data(struct session *s, const uint8_t *buf, ssize_t nread);
/*! Set TLS certificate and key from files. */
int tls_certificate_set(struct network *net, const char *tls_cert, const char *tls_key);
......
This diff is collapsed.
......@@ -37,30 +37,17 @@ struct worker_ctx *worker_create(struct engine *engine, knot_mm_t *pool,
/**
* Process an incoming packet (query from a client or answer from upstream).
*
* @param worker the singleton worker
* @param handle socket through which the request came
* @param query the packet, or NULL on an error from the transport layer
* @param addr the address from which the packet came (or NULL, possibly, on error)
* @param session session the where packet came from
* @param query the packet, or NULL on an error from the transport layer
* @return 0 or an error code
*/
int worker_submit(struct worker_ctx *worker, uv_handle_t *handle, knot_pkt_t *query,
const struct sockaddr* addr);
/**
* Process incoming DNS message fragment(s) that arrived over a stream (TCP, TLS).
*
* If the fragment contains only a partial message, it is buffered.
* If the fragment contains a complete query or completes current fragment, execute it.
* @return the number of newly-completed requests (>=0) or an error code
*/
int worker_process_tcp(struct worker_ctx *worker, uv_stream_t *handle,
const uint8_t *msg, ssize_t len);
int worker_submit(struct session *session, knot_pkt_t *query);
/**
* End current DNS/TCP session, this disassociates pending tasks from this session
* which may be freely closed afterwards.
*/
int worker_end_tcp(struct worker_ctx *worker, uv_handle_t *handle);
int worker_end_tcp(struct session *s);
/**
* Start query resolution with given query.
......@@ -83,16 +70,48 @@ struct kr_request *worker_task_request(struct qr_task *task);
/** Collect worker mempools */
void worker_reclaim(struct worker_ctx *worker);
/** Closes given session */
void worker_session_close(struct session *session);
struct session *worker_session_borrow(struct worker_ctx *worker);
void worker_session_release(struct worker_ctx *worker, uv_handle_t *handle);
void *worker_iohandle_borrow(struct worker_ctx *worker);
void worker_iohandle_release(struct worker_ctx *worker, void *h);
ssize_t worker_gnutls_push(gnutls_transport_ptr_t h, const void *buf, size_t len);
ssize_t worker_gnutls_client_push(gnutls_transport_ptr_t h, const void *buf, size_t len);
int worker_task_step(struct qr_task *task, const struct sockaddr *packet_source,
knot_pkt_t *packet);
int worker_task_numrefs(const struct qr_task *task);
/** Finalize given task */
int worker_task_finalize(struct qr_task *task, int state);
void worker_task_complete(struct qr_task *task);
void worker_task_ref(struct qr_task *task);
void worker_task_unref(struct qr_task *task);
void worker_task_timeout_inc(struct qr_task *task);
int worker_add_tcp_connected(struct worker_ctx *worker,
const struct sockaddr *addr,
struct session *session);
int worker_del_tcp_connected(struct worker_ctx *worker,
const struct sockaddr *addr);
knot_pkt_t *worker_task_get_pktbuf(const struct qr_task *task);
struct request_ctx *worker_task_get_request(struct qr_task *task);
struct session *worker_request_get_source_session(struct request_ctx *);
void worker_request_set_source_session(struct request_ctx *, struct session *session);
/** @cond internal */
/** Number of request within timeout window. */
......@@ -107,9 +126,6 @@ typedef array_t(void *) mp_freelist_t;
/** List of query resolution tasks. */
typedef array_t(struct qr_task *) qr_tasklist_t;
/** Session list. */
typedef array_t(struct session *) qr_sessionlist_t;
/** \details Worker state is meant to persist during the whole life of daemon. */
struct worker_ctx {
struct engine *engine;
......
......@@ -91,8 +91,12 @@ void __asan_poison_memory_region(void const volatile *addr, size_t size);
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
#define kr_asan_poison(addr, size) __asan_poison_memory_region((addr), (size))
#define kr_asan_unpoison(addr, size) __asan_unpoison_memory_region((addr), (size))
#define kr_asan_custom_poison(fn, addr) fn ##_poison((addr))
#define kr_asan_custom_unpoison(fn, addr) fn ##_unpoison((addr))
#else
#define kr_asan_poison(addr, size)
#define kr_asan_unpoison(addr, size)
#define kr_asan_custom_poison(fn, addr)
#define kr_asan_custom_unpoison(fn, addr)
#endif
/* @endcond */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment