Commit 2877bd9a authored by Marek Vavrusa's avatar Marek Vavrusa

Ported TCP handler to new query handling API.

To do:
* XFR handling is synchronous, implement async XFR handler.
* Cleanup in UDP

Commit res #756.
parent c9dcc52b
......@@ -20,6 +20,7 @@
#include "dnslib/node.h"
#include "dnslib/zone.h"
#include "dnslib/utils.h"
#include "common/print.h"
/*!
* \brief Dumps RDATA of the given type.
......
......@@ -14,6 +14,7 @@
#include "dnslib/error.h"
#include "dnslib/debug.h"
/*!
* \brief Default sizes for response structure parts and steps for increasing
* them.
......
......@@ -75,7 +75,7 @@ DEBUG_DNSLIB_ZONE(
static void dnslib_zone_destroy_node_rrsets_from_tree(dnslib_node_t *node,
void *data)
{
int free_rdata_dnames = (int)data;
int free_rdata_dnames = (int)((intptr_t)data);
dnslib_node_free_rrsets(node, free_rdata_dnames);
}
......@@ -1130,14 +1130,14 @@ void dnslib_zone_deep_free(dnslib_zone_t **zone, int free_rdata_dnames)
TREE_POST_ORDER_APPLY((*zone)->nsec3_nodes, dnslib_node, avl,
dnslib_zone_destroy_node_rrsets_from_tree,
(void *)free_rdata_dnames);
(void *)((intptr_t)free_rdata_dnames));
TREE_POST_ORDER_APPLY((*zone)->nsec3_nodes, dnslib_node, avl,
dnslib_zone_destroy_node_owner_from_tree, NULL);
TREE_POST_ORDER_APPLY((*zone)->tree, dnslib_node, avl,
dnslib_zone_destroy_node_rrsets_from_tree,
(void *)free_rdata_dnames);
(void *)((intptr_t)free_rdata_dnames));
TREE_POST_ORDER_APPLY((*zone)->tree, dnslib_node, avl,
dnslib_zone_destroy_node_owner_from_tree, NULL);
......
......@@ -15,6 +15,7 @@
#include "knot/server/name-server.h"
#include "knot/other/error.h"
#include "knot/stat/stat.h"
#include "dnslib/packet.h"
/*! \brief TCP connection pool. */
typedef struct tcp_pool_t {
......@@ -44,84 +45,167 @@ static inline int tcp_pool_unlock(tcp_pool_t *pool)
}
/*!
* \brief TCP query answering function.
* \brief Send TCP message.
*
* Socket receives a single query a replies to it.
* \param fd Associated socket.
* \param msg Buffer for a query wireformat.
* \param msglen Buffer maximum size.
*
* \retval Number of sent data on success.
* \retval KNOT_ERROR on error.
*/
static inline int tcp_send(int fd, uint8_t *msg, size_t msglen)
{
/*! \brief TCP corking.
* \see http://vger.kernel.org/~acme/unbehaved.txt
*/
int cork = 1;
setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
/* Send message size. */
unsigned short pktsize = htons(msglen);
int sent = send(fd, &pktsize, sizeof(pktsize), 0);
if (sent < 0) {
return KNOT_ERROR;
}
/* Send message data. */
sent = send(fd, msg, msglen, 0);
if (sent < 0) {
return KNOT_ERROR;
}
/* Uncork. */
cork = 0;
setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
return sent;
}
/*!
* \brief Send TCP message.
*
* \param fd Associated socket.
* \param src Buffer for a received query.
* \param inbuf_sz Read buffer maximum size.
* \param dest Buffer for reply.
* \param outbuf_sz Reply buffer maximum size.
* \param pool Associated connection pool.
* \param buf Buffer for incoming bytestream.
* \param len Buffer maximum size.
* \param addr Source address.
*
* \retval Number of read bytes on success.
* \retval KNOT_ERROR on error.
* \retval KNOT_ENOMEM on potential buffer overflow.
*/
static inline int tcp_answer(int fd, uint8_t *src, int inbuf_sz, uint8_t *dest,
int outbuf_sz, tcp_pool_t *pool)
static inline int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr)
{
// Receive size
/* Receive size. */
unsigned short pktsize = 0;
int n = recv(fd, &pktsize, sizeof(unsigned short), 0);
if (n < 0) {
return KNOT_ERROR;
}
pktsize = ntohs(pktsize);
debug_net("tcp: incoming packet size on %d: %u buffer size: %u\n",
fd, (unsigned) pktsize, (unsigned) inbuf_sz);
// Receive payload
if (n > 0 && pktsize > 0) {
if (pktsize <= inbuf_sz) {
/*! \todo Check buffer overflow. */
n = recv(fd, src, pktsize, 0);
} else {
/*! \todo Buffer too small error code. */
n = 0;
return -1;
debug_net("tcp: incoming packet size on %d: %hu buffer size: %zu\n",
fd, pktsize, len);
// Check packet size
if (len < pktsize) {
return KNOT_ENOMEM;
}
/* Receive payload. */
n = recvfrom(fd, buf, pktsize, 0, addr->ptr, &addr->len);
if (n <= 0) {
return KNOT_ERROR;
}
return n;
}
/*!
* \brief TCP event handler function.
*
* Handle single TCP event.
*
* \param pool Associated connection pool.
* \param fd Associated socket.
* \param qbuf Buffer for a query wireformat.
* \param qbuf_maxlen Buffer maximum size.
*/
static inline int tcp_handle(tcp_pool_t *pool, int fd,
uint8_t *qbuf, size_t qbuf_maxlen)
{
sockaddr_t addr;
if (socket_initaddr(&addr, pool->handler->type) != KNOT_EOK) {
log_server_error("Socket type %d is not supported, "
"IPv6 support is probably disabled.\n",
pool->handler->type);
return KNOT_ENOTSUP;
}
/* Receive data. */
int n = tcp_recv(fd, qbuf, qbuf_maxlen, &addr);
/* Parse query. */
dnslib_response_t *resp = dnslib_response_new(qbuf_maxlen);
size_t resp_len = qbuf_maxlen; // 64K
/* Parse query. */
dnslib_query_t qtype = DNSLIB_QUERY_NORMAL;
int res = ns_parse_query(qbuf, n, resp, &qtype);
if (unlikely(res < 0)) {
/* Send error response. */
if (res != KNOT_EMALF ) {
uint16_t pkt_id = dnslib_packet_get_id(qbuf);
ns_error_response(pool->ns, pkt_id, res,
qbuf, &resp_len);
}
dnslib_response_free(&resp);
return res;
}
//! \todo Real address;
struct sockaddr_in faddr;
faddr.sin_family = AF_INET;
faddr.sin_port = htons(0);
faddr.sin_addr.s_addr = inet_addr("127.0.0.1");
stat_get_first(pool->stat, (struct sockaddr*)&faddr);
// Check read result
if (n > 0) {
// Send answer
size_t answer_size = outbuf_sz - sizeof(short);
int res = ns_answer_request(pool->ns, src, n,
dest + sizeof(short),
&answer_size);
debug_net("tcp: answer wire format (size %u, result %d).\n",
(unsigned) answer_size, res);
if (res >= 0) {
/*! \brief TCP corking.
* \see http://vger.kernel.org/~acme/unbehaved.txt
*/
int cork = 1;
setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
// Copy header
((unsigned short *) dest)[0] = htons(answer_size);
int sent = -1;
while (sent < 0) {
sent = send(fd, dest,
answer_size + sizeof(short),
0);
}
/* Handle query. */
ns_xfr_t xfr;
switch(qtype) {
case DNSLIB_QUERY_NORMAL:
res = ns_answer_normal(pool->ns, resp, qbuf, &resp_len);
break;
case DNSLIB_QUERY_AXFR:
/*! \todo Implement async axfr handler. */
xfr.response = resp;
xfr.send = tcp_send;
xfr.session = fd;
xfr.response_wire = qbuf;
xfr.rsize = resp_len;
res = ns_answer_axfr(pool->ns, &xfr);
break;
case DNSLIB_QUERY_IXFR:
case DNSLIB_QUERY_NOTIFY:
case DNSLIB_QUERY_UPDATE:
break;
}
debug_net("udp: got answer of size %zd.\n",
resp_len);
// Uncork
cork = 0;
setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
stat_get_second(pool->stat);
debug_net("tcp: sent answer to %d\n", fd);
dnslib_response_free(&resp);
/* Send answer. */
if (res == KNOT_EOK) {
assert(resp_len > 0);
res = tcp_send(fd, qbuf, resp_len);
/* Check result. */
if (res != (int)resp_len) {
debug_net("tcp: %s: failed: %d - %d.\n",
"socket_send()",
res, errno);
return res;
}
}
return 0;
return KNOT_EOK;
}
/*!
......@@ -297,6 +381,30 @@ static int tcp_pool_remove(tcp_pool_t* pool, int socket)
return 0;
}
/*!
* \brief Disconnect TCP client.
*
* \param pool Associated connection pool.
* \param fd Associated socket.
*
* \retval KNOT_EOK on success.
* \retval KNOT_ERROR on error.
*/
static inline int tcp_disconnect(tcp_pool_t *pool, int fd)
{
if (!pool || fd <= 0) {
return KNOT_ERROR;
}
debug_net("tcp: disconnected: %d\n", fd);
tcp_pool_lock(pool);
int ret = tcp_pool_remove(pool, fd);
--pool->evcount;
socket_close(fd);
tcp_pool_unlock(pool);
return ret;
}
/*!
* \brief TCP pool main function.
*
......@@ -309,19 +417,13 @@ static int tcp_pool_remove(tcp_pool_t* pool, int socket)
*/
static int tcp_pool(dthread_t *thread)
{
/*!
* \todo Use custom allocator.
* Although this is much cheaper,
* 16kB worth of buffers *may* pose a problem.
*/
tcp_pool_t *pool = (tcp_pool_t *)thread->data;
uint8_t buf[SOCKET_MTU_SZ];
uint8_t answer[SOCKET_MTU_SZ];
int nfds = 0;
debug_net("tcp: entered pool #%d\n", pool->epfd);
// Poll new data from clients
int nfds = 0;
uint8_t qbuf[64 * 1024]; // 64K buffer
while (pool->evcount > 0) {
// Poll sockets
......@@ -336,31 +438,28 @@ static int tcp_pool(dthread_t *thread)
break;
}
// Evaluate
debug_net("tcp: pool #%d, %d events (%d sockets).\n",
pool->epfd, nfds, pool->evcount);
int fd = 0;
for (int i = 0; i < nfds; ++i) {
// Get client fd
fd = pool->events[i].data.fd;
/* Get client fd. */
int fd = pool->events[i].data.fd;
/* Process. */
debug_net("tcp: pool #%d processing fd=%d.\n",
pool->epfd, fd);
tcp_answer(fd, buf, SOCKET_MTU_SZ,
answer, SOCKET_MTU_SZ,
pool);
/* Handle TCP request. */
if (pool->events[i].events & EPOLLIN) {
tcp_handle(pool, fd, qbuf, sizeof(qbuf));
} else {
// Disconnect
tcp_disconnect(pool, fd);
}
debug_net("tcp: pool #%d finished fd=%d (%d remain).\n",
pool->epfd, fd, pool->evcount);
// Disconnect
debug_net("tcp: disconnected: %d\n", fd);
tcp_pool_lock(pool);
tcp_pool_remove(pool, fd);
--pool->evcount;
socket_close(fd);
tcp_pool_unlock(pool);
}
}
......
......@@ -107,7 +107,7 @@ int udp_master(dthread_t *thread)
size_t resp_len = sizeof(qbuf);
/* Parse query. */
res = ns_parse_query(qbuf, sizeof(qbuf), resp, &qtype);
res = ns_parse_query(qbuf, n, resp, &qtype);
if (unlikely(res < 0)) {
/* Send error response. */
......@@ -127,11 +127,12 @@ int udp_master(dthread_t *thread)
res = ns_answer_normal(ns, resp, qbuf, &resp_len);
break;
case DNSLIB_QUERY_AXFR:
/*! \todo Enqueue xfr request. */
break;
case DNSLIB_QUERY_IXFR:
/*! \todo Send error, not available on UDP. */
break;
case DNSLIB_QUERY_NOTIFY:
case DNSLIB_QUERY_UPDATE:
/*! \todo Implement query notify/update. */
break;
}
......@@ -140,8 +141,8 @@ int udp_master(dthread_t *thread)
dnslib_response_free(&resp);
// Send answer
if (res == 0) {
/* Send answer. */
if (res == KNOT_EOK) {
assert(resp_len > 0);
debug_net("udp: answer wire format (size %zd):\n",
......
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