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

daemon/net: independent network manager

parent e00befcd
kresolved_SOURCES := \
daemon/layer/query.c \
daemon/udp.c \
daemon/tcp.c \
daemon/io.c \
daemon/network.c \
daemon/engine.c \
daemon/worker.c \
daemon/bindings.c \
......
......@@ -17,43 +17,118 @@
#include <libknot/errcode.h>
#include <libknot/internal/utils.h>
#include "daemon/tcp.h"
#include "daemon/io.h"
#include "daemon/network.h"
#include "daemon/worker.h"
static void buf_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
#define ENDPOINT_BUFSIZE 512 /**< This is an artificial limit for DNS query. */
static void *buf_alloc(void)
{
struct endpoint_data *data = malloc(sizeof(*data) + ENDPOINT_BUFSIZE);
if (data == NULL) {
return NULL;
}
data->buf = uv_buf_init((char *)data + sizeof(*data), ENDPOINT_BUFSIZE);
return data;
}
static void buf_get(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
struct endpoint_data *data = handle->data;
*buf = data->buf;
}
static void buf_free(uv_handle_t* handle)
{
free(handle->data);
}
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)
{
uv_loop_t *loop = handle->loop;
struct worker_ctx *worker = loop->data;
/* Check the incoming wire length. */
if (nread < KNOT_WIRE_HEADER_SIZE) {
return;
}
/* Create packets */
knot_pkt_t *query = knot_pkt_new(buf->base, nread, worker->mm);
knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, worker->mm);
/* Resolve */
int ret = worker_exec(worker, answer, query);
if (ret == KNOT_EOK && answer->size > 0) {
udp_send(handle, answer, addr);
}
/* Cleanup */
knot_pkt_free(&query);
knot_pkt_free(&answer);
}
int udp_bind(struct endpoint *ep, struct sockaddr *addr)
{
uv_udp_t *handle = &ep->udp;
int ret = uv_udp_bind(handle, addr, 0);
if (ret != 0) {
return ret;
}
handle->data = buf_alloc();
if (handle->data == NULL) {
udp_unbind(ep);
return kr_error(ENOMEM);
}
uv_udp_recv_start(handle, &buf_get, &udp_recv);
return 0;
}
void udp_unbind(struct endpoint *ep)
{
struct worker_ctx *worker = handle->data;
buf->base = mm_alloc(worker->mm, suggested_size);
buf->len = suggested_size;
uv_udp_t *handle = &ep->udp;
uv_udp_recv_stop(handle);
buf_free((uv_handle_t *)handle);
uv_close((uv_handle_t *)handle, NULL);
}
static void buf_free(uv_handle_t* handle, const uv_buf_t* buf)
static void tcp_unbind_handle(uv_handle_t *handle)
{
struct worker_ctx *worker = handle->data;
mm_free(worker->mm, buf->base);
uv_read_stop((uv_stream_t *)handle);
buf_free(handle);
uv_close(handle, NULL);
}
static void tcp_send(uv_handle_t *handle, const knot_pkt_t *answer)
{
uint16_t pkt_size = 0;
uint16_t pkt_size = htons(answer->size);
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;
uv_loop_t *loop = handle->loop;
struct worker_ctx *worker = loop->data;
/* Check the incoming wire length (malformed, EOF or error). */
if (nread < (ssize_t) sizeof(uint16_t)) {
buf_free((uv_handle_t *)handle, buf);
tcp_unbind((uv_handle_t *)handle);
tcp_unbind_handle((uv_handle_t *)handle);
free(handle);
return;
}
......@@ -74,47 +149,60 @@ static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
/* 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)
static void tcp_accept(uv_stream_t *master, 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 (client == NULL) {
return;
}
if (uv_accept(server, (uv_stream_t*)client) != 0) {
uv_close((uv_handle_t*)client, NULL);
uv_tcp_init(master->loop, client);
client->data = buf_alloc();
if (client->data == NULL) {
tcp_unbind_handle((uv_handle_t *)client);
free(client);
return;
}
uv_read_start((uv_stream_t*)client, buf_alloc, tcp_recv);
if (uv_accept(master, (uv_stream_t*)client) != 0) {
tcp_unbind_handle((uv_handle_t *)client);
free(client);
return;
}
uv_read_start((uv_stream_t*)client, buf_get, tcp_recv);
}
int tcp_bind(uv_handle_t *handle, struct worker_ctx *worker, struct sockaddr *addr)
int tcp_bind(struct endpoint *ep, struct sockaddr *addr)
{
uv_tcp_t *sock = (uv_tcp_t *)handle;
int ret = uv_tcp_bind(sock, addr, 0);
uv_tcp_t *handle = &ep->tcp;
int ret = uv_tcp_bind(handle, addr, 0);
if (ret != 0) {
return KNOT_ERROR;
return ret;
}
ret = uv_listen((uv_stream_t *)sock, 128, tcp_accept);
ret = uv_listen((uv_stream_t *)handle, 16, tcp_accept);
if (ret != 0) {
return KNOT_ERROR;
tcp_unbind(ep);
return ret;
}
sock->data = worker;
return KNOT_EOK;
handle->data = buf_alloc();
if (handle->data == NULL) {
tcp_unbind(ep);
return kr_error(ENOMEM);
}
return 0;
}
void tcp_unbind(uv_handle_t *handle)
void tcp_unbind(struct endpoint *ep)
{
uv_udp_recv_stop((uv_udp_t *)handle);
uv_close(handle, NULL);
tcp_unbind_handle((uv_handle_t *)&ep->tcp);
}
......@@ -17,7 +17,9 @@
#pragma once
#include <uv.h>
#include "worker.h"
struct endpoint;
int tcp_bind(uv_handle_t *handle, struct worker_ctx *worker, struct sockaddr *addr);
void tcp_unbind(uv_handle_t *handle);
int udp_bind(struct endpoint *ep, struct sockaddr *addr);
void udp_unbind(struct endpoint *ep);
int tcp_bind(struct endpoint *ep, struct sockaddr *addr);
void tcp_unbind(struct endpoint *ep);
\ No newline at end of file
......@@ -22,8 +22,8 @@
#include "lib/defines.h"
#include "lib/resolve.h"
#include "daemon/udp.h"
#include "daemon/tcp.h"
#include "daemon/network.h"
#include "daemon/worker.h"
#include "daemon/engine.h"
#include "daemon/bindings.h"
......@@ -63,27 +63,21 @@ static void help(int argc, char *argv[])
" [rundir] Path to the working directory (default: .)\n");
}
static int set_addr(struct sockaddr_storage *ss, char *addr)
static const char *set_addr(char *addr, int *port)
{
char *port = strchr(addr, '#');
if (port) {
sockaddr_port_set(ss, atoi(port + 1));
*port = '\0';
char *p = strchr(addr, '#');
if (p) {
*port = atoi(p + 1);
*p = '\0';
}
int family = AF_INET;
if (strchr(addr, ':')) {
family = AF_INET6;
}
return sockaddr_set(ss, family, addr, sockaddr_port(ss));
return addr;
}
int main(int argc, char **argv)
{
struct sockaddr_storage addr;
sockaddr_set(&addr, AF_INET, "127.0.0.1", 53);
const char *addr = "127.0.0.1";
int port = 53;
/* Long options. */
int c = 0, li = 0, ret = 0;
......@@ -97,11 +91,7 @@ int main(int argc, char **argv)
switch (c)
{
case 'a':
ret = set_addr(&addr, optarg);
if (ret != 0) {
fprintf(stderr, "[system]: address '%s': %s\n", optarg, knot_strerror(ret));
return EXIT_FAILURE;
}
addr = set_addr(optarg, &port);
break;
case 'v':
printf("%s, version %s\n", "Knot DNS Resolver", PACKAGE_VERSION);
......@@ -153,24 +143,14 @@ int main(int argc, char **argv)
.loop = loop,
.mm = NULL
};
loop->data = &worker;
/* Bind to sockets. */
char addr_str[SOCKADDR_STRLEN] = {'\0'};
sockaddr_tostr(addr_str, sizeof(addr_str), &addr);
uv_udp_t udp_sock;
uv_udp_init(loop, &udp_sock);
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 == 0) {
printf("[system] listening on '%s/TCP'\n", addr_str);
ret = tcp_bind((uv_handle_t *)&tcp_sock, &worker, (struct sockaddr *)&addr);
}
/* Check results */
struct network net;
network_init(&net, loop);
ret = network_listen(&net, addr, (uint16_t)port, NET_UDP|NET_TCP);
if (ret != 0) {
fprintf(stderr, "[system] bind to '%s' %s\n", addr_str, knot_strerror(ret));
fprintf(stderr, "[system] bind to '%s#%d' %s\n", addr, port, knot_strerror(ret));
ret = EXIT_FAILURE;
} else {
/* Allocate TTY */
......@@ -181,6 +161,7 @@ int main(int argc, char **argv)
/* Interactive stdin */
if (!feof(stdin)) {
printf("[system] listening on '%s#%d'\n", addr, port);
printf("[system] started in interactive mode, type 'help()'\n");
tty_read(NULL, 0, NULL);
uv_read_start((uv_stream_t*) &pipe, tty_alloc, tty_read);
......@@ -191,8 +172,8 @@ int main(int argc, char **argv)
/* Cleanup. */
fprintf(stderr, "\n[system] quitting\n");
udp_unbind((uv_handle_t *)&udp_sock);
tcp_unbind((uv_handle_t *)&tcp_sock);
network_deinit(&net);
engine_deinit(&engine);
return ret;
}
/* Copyright (C) 2015 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 "daemon/network.h"
#include "daemon/worker.h"
#include "daemon/io.h"
void network_init(struct network *net, uv_loop_t *loop)
{
if (net != NULL) {
/* No multiplexing now, I/O in single thread. */
net->loop = loop;
net->endpoints = map_make();
}
}
/** Close endpoint protocols. */
static int close_endpoint(struct endpoint *ep)
{
if (ep->flags & NET_UDP) {
udp_unbind(ep);
}
if (ep->flags & NET_TCP) {
tcp_unbind(ep);
}
free(ep);
return kr_ok();
}
/** Endpoint visitor (see @file map.h) */
static int visit_key(const char *key, void *val, void *ext)
{
int (*callback)(struct endpoint *) = ext;
endpoint_array_t *ep_array = val;
for (size_t i = ep_array->len; i--;) {
callback(ep_array->at[i]);
}
return 0;
}
static int free_key(const char *key, void *val, void *ext)
{
endpoint_array_t *ep_array = val;
array_clear(*ep_array);
free(ep_array);
return kr_ok();
}
void network_deinit(struct network *net)
{
if (net != NULL) {
map_walk(&net->endpoints, visit_key, close_endpoint);
map_walk(&net->endpoints, free_key, 0);
map_clear(&net->endpoints);
}
}
/** Fetch or create endpoint array and insert endpoint. */
static int insert_endpoint(struct network *net, const char *addr, struct endpoint *ep)
{
/* Fetch or insert address into map */
endpoint_array_t *ep_array = map_get(&net->endpoints, addr);
if (ep_array == NULL) {
ep_array = malloc(sizeof(*ep_array));
if (ep_array == NULL) {
return kr_error(ENOMEM);
}
if (map_set(&net->endpoints, addr, ep_array) != 0) {
free(ep_array);
return kr_error(ENOMEM);
}
array_init(*ep_array);
}
return array_push(*ep_array, ep);
}
/** Open endpoint protocols. */
static int open_endpoint(struct network *net, struct endpoint *ep, struct sockaddr *sa, uint32_t flags)
{
if (flags & NET_UDP) {
uv_udp_init(net->loop, &ep->udp);
int ret = udp_bind(ep, sa);
if (ret != 0) {
return ret;
}
ep->flags |= NET_UDP;
}
if (flags & NET_TCP) {
uv_tcp_init(net->loop, &ep->tcp);
int ret = tcp_bind(ep, sa);
if (ret != 0) {
return ret;
}
ep->flags |= NET_TCP;
}
return kr_ok();
}
int network_listen(struct network *net, const char *addr, uint16_t port, uint32_t flags)
{
if (net == NULL || addr == 0 || port == 0) {
return kr_error(EINVAL);
}
/* Parse address. */
int ret = 0;
struct sockaddr_storage sa;
if (strchr(addr, ':') != NULL) {
ret = uv_ip6_addr(addr, port, (struct sockaddr_in6 *)&sa);
} else {
ret = uv_ip4_addr(addr, port, (struct sockaddr_in *)&sa);
}
if (ret != 0) {
return ret;
}
/* Bind interfaces */
struct endpoint *ep = malloc(sizeof(*ep));
memset(ep, 0, sizeof(*ep));
ep->flags = NET_DOWN;
ep->port = port;
ret = open_endpoint(net, ep, (struct sockaddr *)&sa, flags);
if (ret == 0) {
ret = insert_endpoint(net, addr, ep);
}
if (ret != 0) {
close_endpoint(ep);
}
return ret;
}
int network_close(struct network *net, const char *addr, uint16_t port)
{
endpoint_array_t *ep_array = map_get(&net->endpoints, addr);
if (ep_array == NULL) {
return kr_error(ENOENT);
}
/* Close endpoint in array. */
for (size_t i = ep_array->len; i--;) {
struct endpoint *ep = ep_array->at[i];
if (ep->port == port) {
close_endpoint(ep);
array_del(*ep_array, i);
break;
}
}
/* Collapse key if it has no endpoint. */
if (ep_array->len == 0) {
free(ep_array);
map_del(&net->endpoints, addr);
}
return kr_ok();
}
/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2015 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
......@@ -17,7 +17,35 @@
#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);
#include "lib/generic/array.h"
#include "lib/generic/map.h"
enum endpoint_flag {
NET_DOWN = 0 << 0,
NET_UDP = 1 << 0,
NET_TCP = 1 << 1
};
struct endpoint_data {
uv_buf_t buf;
};
struct endpoint {
uv_udp_t udp;
uv_tcp_t tcp;
uint16_t port;
uint16_t flags;
};
typedef array_t(struct endpoint*) endpoint_array_t;
struct network {
uv_loop_t *loop;
map_t endpoints;
};
void network_init(struct network *net, uv_loop_t *loop);
void network_deinit(struct network *net);
int network_listen(struct network *net, const char *addr, uint16_t port, uint32_t flags);
int network_close(struct network *net, const char *addr, uint16_t port);
/* 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->mm, 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->mm, 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;
/* 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->mm);
knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, worker->mm);
/* 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);
}
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