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

daemon TLS: increase wire-buffer size

When decoding large packets, gnutls gives the application chunks
of size 16kb. So that tls session wirebuffer must be at least
KNOT_WIRE_MAX_PKTSIZE + 16kb.  (message re-formatted by vcunat)
parent f7846901
......@@ -18,7 +18,7 @@ Bugfixes
--------
- http module: only run prometheus in parent process if using --forks=N,
as the submodule collects metrics from all sub-processes as well.
- policy.TLS_FORWARD fixes (!714)
- TLS fixes for corner cases (!714, !700)
- fix build with -DNOVERBOSELOG (#424)
Improvements
......
......@@ -108,7 +108,7 @@ static int udp_bind_finalize(uv_handle_t *handle)
{
check_bufsize(handle);
/* Handle is already created, just create context. */
struct session *s = session_new(handle);
struct session *s = session_new(handle, false);
assert(s);
session_flags(s)->outgoing = false;
return io_start_read(handle);
......@@ -254,7 +254,8 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
if (!client) {
return;
}
int res = io_create(master->loop, (uv_handle_t *)client, SOCK_STREAM, AF_UNSPEC);
int res = io_create(master->loop, (uv_handle_t *)client,
SOCK_STREAM, AF_UNSPEC, tls);
if (res) {
if (res == UV_EMFILE) {
worker->too_many_open = true;
......@@ -268,22 +269,20 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
}
/* struct session was allocated \ borrowed from memory pool. */
struct session *session = client->data;
assert(session_flags(session)->outgoing == false);
struct session *s = client->data;
assert(session_flags(s)->outgoing == false);
assert(session_flags(s)->has_tls == tls);
if (uv_accept(master, (uv_stream_t *)client) != 0) {
/* close session, close underlying uv handles and
* deallocate (or return to memory pool) memory. */
session_close(session);
session_close(s);
return;
}
/* Set deadlines for TCP connection and start reading.
* It will re-check every half of a request time limit if the connection
* is idle and should be terminated, this is an educated guess. */
struct session *s = client->data;
assert(session_flags(s)->outgoing == false);
struct sockaddr *peer = session_get_peer(s);
int peer_len = sizeof(union inaddr);
int ret = uv_tcp_getpeername(client, peer, &peer_len);
......@@ -297,7 +296,6 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
uint64_t idle_in_timeout = net->tcp.in_idle_timeout;
uint64_t timeout = KR_CONN_RTT_MAX / 2;
session_flags(s)->has_tls = tls;
if (tls) {
timeout += TLS_MAX_HANDSHAKE_TIME;
struct tls_ctx_t *ctx = session_tls_get_server_ctx(s);
......@@ -415,7 +413,7 @@ int tcp_bindfd_tls(uv_tcp_t *handle, int fd, int tcp_backlog)
return _tcp_bindfd(handle, fd, tls_accept, tcp_backlog);
}
int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family)
int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family, bool has_tls)
{
int ret = -1;
if (type == SOCK_DGRAM) {
......@@ -427,7 +425,7 @@ int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family)
if (ret != 0) {
return ret;
}
struct session *s = session_new(handle);
struct session *s = session_new(handle, has_tls);
if (s == NULL) {
ret = -1;
}
......
......@@ -35,8 +35,10 @@ void tcp_timeout_trigger(uv_timer_t *timer);
/** Initialize the handle, incl. ->data = struct session * instance.
* \param type = SOCK_*
* \param family = AF_* */
int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family);
* \param family = AF_*
* \param has_tls has meanings only when type is SOCK_STREAM */
int io_create(uv_loop_t *loop, uv_handle_t *handle, int type,
unsigned family, bool has_tls);
void io_deinit(uv_handle_t *handle);
void io_free(uv_handle_t *handle);
......
......@@ -26,6 +26,8 @@
#include "daemon/io.h"
#include "lib/generic/queue.h"
#define TLS_CHUNK_SIZE (16 * 1024)
/* Per-session (TCP or UDP) persistent structure,
* that exists between remote counterpart and a local socket.
*/
......@@ -301,7 +303,7 @@ struct session *session_get(uv_handle_t *h)
return h->data;
}
struct session *session_new(uv_handle_t *handle)
struct session *session_new(uv_handle_t *handle, bool has_tls)
{
if (!handle) {
return NULL;
......@@ -314,13 +316,20 @@ struct session *session_new(uv_handle_t *handle)
queue_init(session->waiting);
session->tasks = trie_create(NULL);
if (handle->type == UV_TCP) {
uint8_t *wire_buf = malloc(KNOT_WIRE_MAX_PKTSIZE);
size_t wire_buffer_size = KNOT_WIRE_MAX_PKTSIZE;
if (has_tls) {
/* When decoding large packets,
* gnutls gives the application chunks of size 16 kb each. */
wire_buffer_size += TLS_CHUNK_SIZE;
session->sflags.has_tls = true;
}
uint8_t *wire_buf = malloc(wire_buffer_size);
if (!wire_buf) {
free(session);
return NULL;
}
session->wire_buf = wire_buf;
session->wire_buf_size = KNOT_WIRE_MAX_PKTSIZE;
session->wire_buf_size = wire_buffer_size;
} else if (handle->type == UV_UDP) {
/* We use the singleton buffer from worker for all UDP (!)
* libuv documentation doesn't really guarantee this is OK,
......@@ -533,13 +542,13 @@ knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm)
const uv_handle_t *handle = session->handle;
uint8_t *msg_start = &session->wire_buf[session->wire_buf_start_idx];
ssize_t wirebuf_msg_data_size = session->wire_buf_end_idx - session->wire_buf_start_idx;
uint16_t msg_size = wirebuf_msg_data_size;
uint16_t msg_size = 0;
if (!handle) {
session->sflags.wirebuf_error = true;
return NULL;
} else if (handle->type == UV_TCP) {
if (msg_size < 2) {
if (wirebuf_msg_data_size < 2) {
return NULL;
}
msg_size = knot_wire_read_u16(msg_start);
......@@ -550,13 +559,21 @@ knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm)
if (msg_size + 2 > wirebuf_msg_data_size) {
return NULL;
}
if (msg_size == 0) {
session->sflags.wirebuf_error = true;
return NULL;
}
msg_start += 2;
} else if (wirebuf_msg_data_size < UINT16_MAX) {
msg_size = wirebuf_msg_data_size;
} else {
session->sflags.wirebuf_error = true;
return NULL;
}
knot_pkt_t *pkt = knot_pkt_new(msg_start, msg_size, mm);
if (pkt) {
session->sflags.wirebuf_error = false;
}
session->sflags.wirebuf_error = (pkt == NULL);
return pkt;
}
......@@ -679,7 +696,7 @@ size_t session_wirebuf_get_len(struct session *session)
size_t session_wirebuf_get_size(struct session *session)
{
return sizeof(session->wire_buf);
return session->wire_buf_size;
}
uint8_t *session_wirebuf_get_free_start(struct session *session)
......
......@@ -32,8 +32,9 @@ struct session_flags {
bool wirebuf_error : 1; /**< True: last operation with wirebuf ended up with an error. */
};
/* Allocate new session for a libuv handle. */
struct session *session_new(uv_handle_t *handle);
/* Allocate new session for a libuv handle.
* If handle->tyoe is UV_UDP, tls parameter will be ignored. */
struct session *session_new(uv_handle_t *handle, bool has_tls);
/* Clear and free given session. */
void session_free(struct session *session);
/* Clear session. */
......
......@@ -131,7 +131,8 @@ static inline struct worker_ctx *get_worker(void)
/*! @internal Create a UDP/TCP handle for an outgoing AF_INET* connection.
* socktype is SOCK_* */
static uv_handle_t *ioreq_spawn(struct worker_ctx *worker, int socktype, sa_family_t family)
static uv_handle_t *ioreq_spawn(struct worker_ctx *worker,
int socktype, sa_family_t family, bool has_tls)
{
bool precond = (socktype == SOCK_DGRAM || socktype == SOCK_STREAM)
&& (family == AF_INET || family == AF_INET6);
......@@ -147,7 +148,7 @@ static uv_handle_t *ioreq_spawn(struct worker_ctx *worker, int socktype, sa_fami
if (!handle) {
return NULL;
}
int ret = io_create(worker->loop, handle, socktype, family);
int ret = io_create(worker->loop, handle, socktype, family, has_tls);
if (ret) {
if (ret == UV_EMFILE) {
worker->too_many_open = true;
......@@ -941,7 +942,7 @@ static uv_handle_t *retransmit(struct qr_task *task)
if (kr_resolve_checkout(&ctx->req, NULL, (struct sockaddr *)choice, SOCK_DGRAM, task->pktbuf) != 0) {
return ret;
}
ret = ioreq_spawn(ctx->worker, SOCK_DGRAM, choice->sin6_family);
ret = ioreq_spawn(ctx->worker, SOCK_DGRAM, choice->sin6_family, false);
if (!ret) {
return ret;
}
......@@ -1202,52 +1203,54 @@ static int tcp_task_existing_connection(struct session *session, struct qr_task
return kr_ok();
}
static int tcp_task_make_connection(struct session *session, struct qr_task *task,
const struct sockaddr *addr /* , knot_pkt_t *packet */)
static int tcp_task_make_connection(struct qr_task *task, const struct sockaddr *addr)
{
struct request_ctx *ctx = task->ctx;
struct worker_ctx *worker = ctx->worker;
/* Check if there must be TLS */
struct engine *engine = worker->engine;
struct network *net = &engine->net;
const char *key = tcpsess_key(addr);
struct tls_client_ctx_t *tls_ctx = NULL;
struct tls_client_paramlist_entry *entry = map_get(&net->tls_client_params, key);
if (entry) {
/* Address is configured to be used with TLS.
* We need to allocate auxiliary data structure. */
tls_ctx = tls_client_ctx_new(entry, worker);
if (!tls_ctx) {
return kr_error(EINVAL);
}
}
uv_connect_t *conn = malloc(sizeof(uv_connect_t));
if (!conn) {
tls_client_ctx_free(tls_ctx);
return kr_error(EINVAL);
}
uv_handle_t *client = ioreq_spawn(worker, SOCK_STREAM,
addr->sa_family);
bool has_tls = (tls_ctx != NULL);
uv_handle_t *client = ioreq_spawn(worker, SOCK_STREAM, addr->sa_family, has_tls);
if (!client) {
tls_client_ctx_free(tls_ctx);
free(conn);
return kr_error(EINVAL);
}
session = client->data;
struct session *session = client->data;
assert(session_flags(session)->has_tls == has_tls);
if (has_tls) {
tls_client_ctx_set_session(tls_ctx, session);
session_tls_set_client_ctx(session, tls_ctx);
}
/* Add address to the waiting list.
* Now it "is waiting to be connected to." */
int ret = worker_add_tcp_waiting(ctx->worker, addr, session);
if (ret < 0) {
free(conn);
session_close(session);
return kr_error(EINVAL);
}
/* Check if there must be TLS */
struct engine *engine = ctx->worker->engine;
struct network *net = &engine->net;
const char *key = tcpsess_key(addr);
struct tls_client_paramlist_entry *entry = map_get(&net->tls_client_params, key);
if (entry) {
/* Address is configured to be used with TLS.
* We need to allocate auxiliary data structure. */
assert(session_tls_get_client_ctx(session) == NULL);
struct tls_client_ctx_t *tls_ctx = tls_client_ctx_new(entry, worker);
if (!tls_ctx) {
worker_del_tcp_waiting(ctx->worker, addr);
free(conn);
return kr_error(EINVAL);
}
tls_client_ctx_set_session(tls_ctx, session);
session_tls_set_client_ctx(session, tls_ctx);
session_flags(session)->has_tls = true;
}
conn->data = session;
/* Store peer address for the session. */
struct sockaddr *peer = session_get_peer(session);
......@@ -1259,6 +1262,7 @@ static int tcp_task_make_connection(struct session *session, struct qr_task *tas
if (ret != 0) {
worker_del_tcp_waiting(ctx->worker, addr);
free(conn);
session_close(session);
return kr_error(EINVAL);
}
......@@ -1273,6 +1277,7 @@ static int tcp_task_make_connection(struct session *session, struct qr_task *tas
session_timer_stop(session);
worker_del_tcp_waiting(ctx->worker, addr);
free(conn);
session_close(session);
return kr_error(EAGAIN);
}
......@@ -1283,6 +1288,7 @@ static int tcp_task_make_connection(struct session *session, struct qr_task *tas
session_timer_stop(session);
worker_del_tcp_waiting(ctx->worker, addr);
free(conn);
session_close(session);
return kr_error(EINVAL);
}
......@@ -1321,7 +1327,7 @@ static int tcp_task_step(struct qr_task *task,
ret = tcp_task_existing_connection(session, task);
} else {
/* Make connection. */
ret = tcp_task_make_connection(session, task, addr);
ret = tcp_task_make_connection(task, addr);
}
if (ret != kr_ok()) {
......
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