Commit f88e58f9 authored by Marek Vavruša's avatar Marek Vavruša

daemon: tcp support, code cleanup

parent 7b345ea9
......@@ -30,7 +30,7 @@ LT_INIT
PKG_PROG_PKG_CONFIG
# Check for dependencies
AC_CHECK_FUNCS([nftw])
AC_CHECK_FUNCS([nftw, mkdtemp])
PKG_CHECK_MODULES([libuv], [libuv])
PKG_CHECK_MODULES([libknot], [libknot])
PKG_CHECK_MODULES([cmocka], [cmocka], AC_CONFIG_FILES([tests/Makefile]))
......
......@@ -9,6 +9,10 @@ sbin_PROGRAMS = kresolved
kresolved_SOURCES = \
layer/query.h \
layer/query.c \
udp.h \
udp.c \
tcp.h \
tcp.c \
worker.h \
worker.c \
main.c
......
......@@ -24,7 +24,8 @@
#include <libknot/errcode.h>
#include "lib/resolve.h"
#include "worker.h"
#include "daemon/udp.h"
#include "daemon/tcp.h"
void signal_handler(uv_signal_t *handle, int signum)
{
......@@ -105,31 +106,37 @@ int main(int argc, char **argv)
uv_signal_init(loop, &sigint);
uv_signal_start(&sigint, signal_handler, SIGINT);
/* Create a worker. */
struct worker_ctx worker;
worker_init(&worker, &mm);
/* Bind to sockets. */
char addr_str[SOCKADDR_STRLEN] = {'\0'};
sockaddr_tostr(addr_str, sizeof(addr_str), &addr);
uv_udp_t udp_sock;
memset(&udp_sock, 0, sizeof(uv_udp_t));
uv_udp_init(loop, &udp_sock);
ret = uv_udp_bind(&udp_sock, (struct sockaddr *)&addr, 0);
if (ret == 0) {
printf("[system] listening on '%s'\n", addr_str);
} else {
fprintf(stderr, "[system] failed to bind to '%s'\n", addr_str);
return EXIT_FAILURE;
uv_tcp_t tcp_sock;
uv_tcp_init(loop, &tcp_sock);
printf("[system] listening on '%s/UDP'\n", addr_str);
ret = udp_bind((uv_handle_t *)&udp_sock, &worker, (struct sockaddr *)&addr);
if (ret == KNOT_EOK) {
printf("[system] listening on '%s/TCP'\n", addr_str);
ret = tcp_bind((uv_handle_t *)&tcp_sock, &worker, (struct sockaddr *)&addr);
}
/* Start a worker. */
struct worker_ctx worker;
worker_init(&worker, &mm);
worker_start(&udp_sock, &worker);
/* Run the event loop. */
fflush(stdout);
ret = uv_run(loop, UV_RUN_DEFAULT);
/* Check results */
if (ret != KNOT_EOK) {
fprintf(stderr, "[system] bind to '%s' %s\n", addr_str, knot_strerror(ret));
ret = EXIT_FAILURE;
} else {
/* Run the event loop. */
fflush(stdout);
ret = uv_run(loop, UV_RUN_DEFAULT);
}
/* Cleanup. */
worker_stop(&udp_sock);
udp_unbind((uv_handle_t *)&udp_sock);
tcp_unbind((uv_handle_t *)&tcp_sock);
worker_deinit(&worker);
return ret;
......
/* Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
*/
#include <libknot/errcode.h>
#include <libknot/internal/utils.h>
#include "daemon/tcp.h"
#include "daemon/worker.h"
static void buf_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
struct worker_ctx *worker = handle->data;
buf->base = mm_alloc(worker->pool, suggested_size);
buf->len = suggested_size;
}
static void buf_free(uv_handle_t* handle, const uv_buf_t* buf)
{
struct worker_ctx *worker = handle->data;
mm_free(worker->pool, buf->base);
}
static void tcp_send(uv_handle_t *handle, const knot_pkt_t *answer)
{
uint16_t pkt_size = 0;
uv_buf_t buf[2];
buf[0].base = (char *)&pkt_size;
buf[0].len = sizeof(pkt_size);
buf[1].base = (char *)answer->wire;
buf[1].len = answer->size;
wire_write_u16((uint8_t *)buf[0].base, answer->size);
uv_try_write((uv_stream_t *)handle, buf, 2);
}
static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
{
struct worker_ctx *worker = handle->data;
assert(worker->pool);
/* Check the incoming wire length (malformed, EOF or error). */
if (nread < sizeof(uint16_t)) {
buf_free((uv_handle_t *)handle, buf);
tcp_unbind((uv_handle_t *)handle);
free(handle);
return;
}
/* Set packet size */
nread = wire_read_u16((const uint8_t *)buf->base);
/* Create packets */
knot_pkt_t *query = knot_pkt_new(buf->base + sizeof(uint16_t), nread, worker->pool);
knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, worker->pool);
/* Resolve */
int ret = worker_exec(worker, answer, query);
if (ret == KNOT_EOK && answer->size > 0) {
tcp_send((uv_handle_t *)handle, answer);
}
/* Cleanup */
knot_pkt_free(&query);
knot_pkt_free(&answer);
buf_free((uv_handle_t *)handle, buf);
}
static void tcp_accept(uv_stream_t *server, int status)
{
if (status != 0) {
return;
}
uv_tcp_t *client = malloc(sizeof(uv_tcp_t));
uv_tcp_init(server->loop, client);
client->data = server->data;
if (uv_accept(server, (uv_stream_t*)client) != 0) {
uv_close((uv_handle_t*)client, NULL);
free(client);
}
uv_read_start((uv_stream_t*)client, buf_alloc, tcp_recv);
}
int tcp_bind(uv_handle_t *handle, struct worker_ctx *worker, struct sockaddr *addr)
{
uv_tcp_t *sock = (uv_tcp_t *)handle;
int ret = uv_tcp_bind(sock, addr, 0);
if (ret != 0) {
return KNOT_ERROR;
}
ret = uv_listen((uv_stream_t *)sock, 128, tcp_accept);
if (ret != 0) {
return KNOT_ERROR;
}
sock->data = worker;
return KNOT_EOK;
}
void tcp_unbind(uv_handle_t *handle)
{
uv_udp_recv_stop((uv_udp_t *)handle);
uv_close(handle, NULL);
}
/* Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <uv.h>
#include "worker.h"
int tcp_bind(uv_handle_t *handle, struct worker_ctx *worker, struct sockaddr *addr);
void tcp_unbind(uv_handle_t *handle);
/* Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
*/
#include <libknot/errcode.h>
#include "daemon/udp.h"
#include "daemon/worker.h"
static void buf_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
struct worker_ctx *worker = handle->data;
buf->base = mm_alloc(worker->pool, suggested_size);
buf->len = suggested_size;
}
static void buf_free(uv_handle_t* handle, const uv_buf_t* buf)
{
struct worker_ctx *worker = handle->data;
mm_free(worker->pool, buf->base);
}
static void udp_send(uv_udp_t *handle, knot_pkt_t *answer, const struct sockaddr *addr)
{
uv_buf_t sendbuf = uv_buf_init((char *)answer->wire, answer->size);
uv_udp_try_send(handle, &sendbuf, 1, addr);
}
static void udp_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf,
const struct sockaddr *addr, unsigned flags)
{
struct worker_ctx *worker = handle->data;
assert(worker->pool);
/* Check the incoming wire length. */
if (nread < KNOT_WIRE_HEADER_SIZE) {
buf_free((uv_handle_t *)handle, buf);
return;
}
/* Create packets */
knot_pkt_t *query = knot_pkt_new(buf->base, nread, worker->pool);
knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, worker->pool);
/* Resolve */
int ret = worker_exec(worker, answer, query);
if (ret == KNOT_EOK && answer->size > 0) {
udp_send(handle, answer, addr);
}
/* Cleanup */
buf_free((uv_handle_t *)handle, buf);
knot_pkt_free(&query);
knot_pkt_free(&answer);
}
int udp_bind(uv_handle_t *handle, struct worker_ctx *worker, struct sockaddr *addr)
{
uv_udp_t *sock = (uv_udp_t *)handle;
int ret = uv_udp_bind(sock, addr, 0);
if (ret != 0) {
return KNOT_ERROR;
}
sock->data = worker;
uv_udp_recv_start(sock, &buf_alloc, &udp_recv);
return KNOT_EOK;
}
void udp_unbind(uv_handle_t *handle)
{
uv_udp_recv_stop((uv_udp_t *)handle);
uv_close(handle, NULL);
}
/* Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <uv.h>
#include "worker.h"
int udp_bind(uv_handle_t *handle, struct worker_ctx *worker, struct sockaddr *addr);
void udp_unbind(uv_handle_t *handle);
......@@ -26,43 +26,51 @@
/* Defines */
#define CACHE_DEFAULT_SIZE 10*1024*1024
static void buf_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
int worker_init(struct worker_ctx *worker, mm_ctx_t *mm)
{
struct worker_ctx *worker = handle->data;
buf->base = mm_alloc(worker->pool, suggested_size);
buf->len = suggested_size;
}
if (worker == NULL) {
return KNOT_EINVAL;
}
static void buf_free(uv_handle_t* handle, const uv_buf_t* buf)
{
struct worker_ctx *worker = handle->data;
mm_free(worker->pool, buf->base);
memset(worker, 0, sizeof(struct worker_ctx));
worker->pool = mm;
/* Open resolution context */
int ret = kr_context_init(&worker->resolve, mm);
if (ret != KNOT_EOK) {
return ret;
}
/* Open resolution context cache */
worker->resolve.cache = kr_cache_open("/tmp/kresolved", mm, CACHE_DEFAULT_SIZE);
if (worker->resolve.cache == NULL) {
fprintf(stderr, "Cache directory '/tmp/kresolved' not exists, exitting.\n");
kr_context_deinit(&worker->resolve);
return KNOT_ERROR;
}
return KNOT_EOK;
}
static void worker_send(uv_udp_t *handle, knot_pkt_t *answer, const struct sockaddr *addr)
void worker_deinit(struct worker_ctx *worker)
{
uv_buf_t sendbuf = uv_buf_init((char *)answer->wire, answer->size);
uv_udp_try_send(handle, &sendbuf, 1, addr);
if (worker == NULL) {
return;
}
kr_context_deinit(&worker->resolve);
}
static void worker_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf,
const struct sockaddr *addr, unsigned flags)
int worker_exec(struct worker_ctx *worker, knot_pkt_t *answer, knot_pkt_t *query)
{
struct worker_ctx *worker = handle->data;
assert(worker->pool);
if (nread < KNOT_WIRE_HEADER_SIZE) {
buf_free((uv_handle_t *)handle, buf);
return;
if (worker == NULL) {
return KNOT_EINVAL;
}
/* Parse query packet. */
knot_pkt_t *query = knot_pkt_new((uint8_t *)buf->base, nread, worker->pool);
int ret = knot_pkt_parse(query, 0);
if (ret != KNOT_EOK) {
knot_pkt_free(&query);
buf_free((uv_handle_t *)handle, buf);
return; /* Ignore malformed query. */
return ret; /* Ignore malformed query. */
}
/* Process query packet. */
......@@ -73,57 +81,17 @@ static void worker_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf,
int state = knot_layer_in(&proc, query);
/* Build an answer. */
knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, worker->pool);
while (state == KNOT_NS_PROC_FULL) {
if (state == KNOT_NS_PROC_FULL) {
knot_pkt_init_response(answer, query);
state = knot_layer_out(&proc, answer);
if (answer->size > 0) {
worker_send(handle, answer, addr);
}
}
/* Cleanup. */
knot_layer_finish(&proc);
buf_free((uv_handle_t *)handle, buf);
knot_pkt_free(&query);
knot_pkt_free(&answer);
}
int worker_init(struct worker_ctx *worker, mm_ctx_t *mm)
{
memset(worker, 0, sizeof(struct worker_ctx));
worker->pool = mm;
/* Open resolution context */
int ret = kr_context_init(&worker->resolve, mm);
if (ret != KNOT_EOK) {
return ret;
}
/* Open resolution context cache */
worker->resolve.cache = kr_cache_open("/tmp/kresolved", mm, CACHE_DEFAULT_SIZE);
if (worker->resolve.cache == NULL) {
fprintf(stderr, "Cache directory '/tmp/kresolved' not exists, exitting.\n");
kr_context_deinit(&worker->resolve);
if (state == KNOT_NS_PROC_FAIL) {
return KNOT_ERROR;
}
return KNOT_EOK;
}
void worker_deinit(struct worker_ctx *worker)
{
kr_context_deinit(&worker->resolve);
}
void worker_start(uv_udp_t *handle, struct worker_ctx *worker)
{
handle->data = worker;
uv_udp_recv_start(handle, &buf_alloc, &worker_recv);
}
void worker_stop(uv_udp_t *handle)
{
uv_udp_recv_stop(handle);
}
......@@ -16,17 +16,38 @@
#pragma once
#include <uv.h>
#include <libknot/packet/pkt.h>
#include <libknot/internal/mempattern.h>
#include "lib/resolve.h"
/*!
* \brief Query resolution worker.
*/
struct worker_ctx {
struct kr_context resolve;
mm_ctx_t *pool;
};
/*!
* \brief Initialize worker context.
* \param worker
* \param mm
* \return KNOT_E*
*/
int worker_init(struct worker_ctx *worker, mm_ctx_t *mm);
/*!
* \brief Clear worker context.
* \param worker
*/
void worker_deinit(struct worker_ctx *worker);
void worker_start(uv_udp_t *req, struct worker_ctx *worker);
void worker_stop(uv_udp_t *req);
/*!
* \brief Resolve query.
* \param worker
* \param answer
* \param query
* \return KNOT_E*
*/
int worker_exec(struct worker_ctx *worker, knot_pkt_t *answer, knot_pkt_t *query);
......@@ -5,6 +5,10 @@ daemon/Makefile.am
daemon/layer/query.c
daemon/layer/query.h
daemon/main.c
daemon/tcp.c
daemon/tcp.h
daemon/udp.c
daemon/udp.h
daemon/worker.c
daemon/worker.h
lib/Makefile.am
......
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