--------
- 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
{
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);
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;
}
/* 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);
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);
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) {
if (ret != 0) {
return ret;
}
- struct session *s = session_new(handle);
+ struct session *s = session_new(handle, has_tls);
if (s == NULL) {
ret = -1;
}
/** 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);
#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.
*/
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;
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,
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);
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;
}
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)
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. */
/*! @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);
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;
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;
}
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);
if (ret != 0) {
worker_del_tcp_waiting(ctx->worker, addr);
free(conn);
+ session_close(session);
return kr_error(EINVAL);
}
session_timer_stop(session);
worker_del_tcp_waiting(ctx->worker, addr);
free(conn);
+ session_close(session);
return kr_error(EAGAIN);
}
session_timer_stop(session);
worker_del_tcp_waiting(ctx->worker, addr);
free(conn);
+ session_close(session);
return kr_error(EINVAL);
}
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()) {