Muninn/muninn_daemon.c

360 lines
9.9 KiB
C
Raw Normal View History

#include "ngx_conf_file.h"
#ifdef __clang__
#pragma clang diagnostic ignored "-Wdangling-else"
#else
#pragma GCC diagnostic ignored "-Wdangling-else"
#pragma GCC diagnostic ignored "-Wmultistatement-macros"
#endif
#include "muninn.h"
#include <sys/socket.h>
#include <netinet/in.h>
// Configuration functions
static char *mun_conf_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *mun_conf_connection_ct(ngx_conf_t *cf, ngx_command_t *c, void *v);
// Connection lifecycle
static void mun_init_connection(ngx_connection_t *c);
static void mun_close_connection(ngx_connection_t *c);
static void mun_handle_conn_readable(ngx_event_t *ev);
static void mun_handle_conn_writeable(ngx_event_t *ev);
// Request lifecycle functions
static ngx_int_t mun_log_request(mun_dns_request *r);
// connection pool flag stored here
static ngx_int_t mun_daemon_dns_connection_pool_ct = MUN_DEFAULT_CONN_POOL_SZ;
// Module definitions
static ngx_command_t ngx_muninn_daemon_commands[] = {
{ ngx_string("dns_listen"),
NGX_MUNINN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
mun_conf_listen,
0, 0, NULL },
{ ngx_string("dns_connection_pool_count"),
NGX_MUNINN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
mun_conf_connection_ct,
0, 0, NULL },
ngx_null_command
};
static ngx_core_module_t ngx_muninn_daemon_module_ctx = {
ngx_string("muninn_daemon"), NULL, NULL
};
ngx_module_t ngx_muninn_daemon_module = {
NGX_MODULE_V1,
&ngx_muninn_daemon_module_ctx,
ngx_muninn_daemon_commands,
NGX_MUNINN_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
// Muninn request handling state machine
static ngx_int_t (*mun_read_handler_list[])(mun_dns_request *) = {
mun_log_request, // test req state machine logging handler
// TODO: request parsing
// TODO: request logging
// TODO: request cache lookup
// TODO: request record(s) fetch
// TODO: request response generation
// TODO: error handling phase
};
/* ADJACENCY LIST
* This matrix defines the entire request processing state machine and changes
* All numbers in this list should correspond to mun_read_handler_list indexes
* The entries in this list are linked to their same index in the handler list
*
* Entries:
* { NGX_OK next handler - intended to indidicate phase is complete
* NGX_ERROR next handler - intended to trap error states
* NGX_BUSY next handler - intended to trap waiting state
* NGX_DECLINED next handler } intended to trap service refusal
*
* Other handler returns:
* NGX_DONE: this return code means a request is ready to be finalized
* nothing further will be returned.
* NGX_AGAIN: this return code causes same function to be called again.
* primarily used when more input needed than what is available.
* NGX_ABORT: this return code causes all further action to stop for request
*
* For more information, see how this is used in mun_handle_conn_readable
*/
static ngx_uint_t mun_read_handler_adjacency[][4] = {
{0, 0, 0, 0}
};
static char *mun_conf_connection_ct(
ngx_conf_t *cf,
ngx_command_t *cmd,
void *conf
) {
ngx_int_t argc = cf->args->nelts;
ngx_str_t *argv = cf->args->elts;
ngx_int_t num;
if (argc < 1) {
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"dns_connection_pool_count requires at least one argument");
return NGX_CONF_ERROR;
}
if ((num = ngx_atoi(argv[1].data, argv[1].len)) == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid integer passed to dns_connection_pool_count");
return NGX_CONF_ERROR;
}
if (num < 1) {
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"dns_connection_pool_count must be set to at least 1");
return NGX_CONF_ERROR;
}
mun_daemon_dns_connection_pool_ct = num;
return NGX_CONF_OK;
}
static char *mun_conf_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *in) {
ngx_url_t *u;
ngx_int_t argc;
ngx_str_t *argv;
ngx_listening_t *listening;
argc = cf->args->nelts;
argv = cf->args->elts;
if (argc < 1) {
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"dns_listen needs at least one argument");
return NGX_CONF_ERROR;
}
if (!(u = ngx_pcalloc(cf->pool, sizeof(ngx_url_t)))) return NGX_CONF_ERROR;
u->url = argv[1];
u->listen = 1;
u->default_port = 53;
if (ngx_parse_url(cf->pool, u) != NGX_OK) {
if (u->err) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%s in \"%V\" of the \"dns_listen\" directive",
u->err, &u->url);
}
return NGX_CONF_ERROR;
}
listening = ngx_create_listening(cf, (struct sockaddr *) &u->sockaddr,
u->socklen);
if (!listening) return NGX_CONF_ERROR;
listening->addr_ntop = 1;
listening->pool_size = mun_daemon_dns_connection_pool_ct;
listening->handler = mun_init_connection;
listening->logp = cf->log;
listening->log.data = &listening->addr_text;
listening->log.handler = ngx_accept_log_error;
listening->sndbuf = 1;
listening->rcvbuf = 1;
listening->backlog = mun_daemon_dns_connection_pool_ct * 2;
listening->protocol = IPPROTO_UDP;
listening->type = SOCK_DGRAM;
// listener added to cycle at creation
return NGX_CONF_OK;
}
static void mun_init_connection(ngx_connection_t *c) {
mun_dns_request *r;
if (!(r = ngx_pcalloc(c->pool, sizeof(mun_dns_request)))) {
mun_close_connection(c);
return;
}
c->data = r;
c->log->connection = c->number;
c->log_error = NGX_ERROR_INFO;
c->read->handler = mun_handle_conn_readable;
c->write->handler = mun_handle_conn_writeable;
if (c->read->ready) ngx_post_event(c->read, &ngx_posted_events)
ngx_log_error(NGX_LOG_INFO, c->log, 0, "muninn: new request accepted");
}
static void mun_close_connection(ngx_connection_t *c) {
c->destroyed = 1;
ngx_close_connection(c);
ngx_destroy_pool(c->pool);
}
static void mun_handle_conn_readable(ngx_event_t *ev) {
ngx_connection_t *c;
mun_dns_request *r;
ngx_chain_t *cur;
ngx_int_t ret, idx, space, filled;
c = ev->data;
r = c->data;
ret = 0;
filled = 0;
if (r && r->finalized) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"muninn: read called for finalized request");
return;
}
if (ev->timedout) {
// TODO: handle timedout according to DNS proto
mun_close_connection(c);
return;
}
if (!r->in) {
if (!(r->in = ngx_alloc_chain_link(c->pool))) {
ret = NGX_ERROR;
goto content;
}
if (!(r->in->buf = ngx_create_temp_buf(c->pool,
MUN_DNS_LEGACY_UDP_SZ))) {
ret = NGX_ERROR;
goto content;
}
cur = r->in;
cur->buf->last_buf = 1;
cur->next = NULL;
space = MUN_DNS_LEGACY_UDP_SZ;
} else {
for (cur = r->in; cur && !cur->buf->last_buf; cur = cur->next) {}
if (!cur) {
ngx_log_error(NGX_LOG_ERR, r->conn->log, 0,
"muninn: failed to find next buffer to read into");
}
space = cur->buf->end - cur->buf->last;
}
if (!r->read_cur) {
r->read_cur = r->in;
}
while (1) {
filled = c->recv(c, cur->buf->last, space);
if (c->read->error || c->read->eof) {
// TODO: log
ret = NGX_ERROR;
goto content;
}
if (filled == space) {
cur->buf->last_buf = 0;
if (!(cur = (cur->next = ngx_alloc_chain_link(c->pool))) ||
!(cur->buf = ngx_create_temp_buf(c->pool,
MUN_DNS_LEGACY_UDP_SZ)) ||
!(space = MUN_DNS_LEGACY_UDP_SZ)) break;
cur->next = NULL;
cur->buf->last_buf = 1;
continue;
}
break;
}
content:
if (!ret) ret = mun_read_handler_list[r->read_state](r);
idx = mun_read_handler_adjacency[r->read_state][0 - ret];
r->read_state = idx;
switch (ret) {
case NGX_OK:
// we could still have read data to process in next stage
break;
case NGX_ERROR:
case NGX_BUSY:
case NGX_DECLINED:
ngx_post_event(c->write, &ngx_posted_events);
/* fallthrough */
case NGX_ABORT:
case NGX_AGAIN:
return;
case NGX_DONE:
// TODO: perform request finalization
r->finalized = 1;
return;
}
ngx_post_event(ev, &ngx_posted_events);
}
static void mun_handle_conn_writeable(ngx_event_t *ev) {
ngx_chain_t *tmp;
ngx_connection_t *c;
mun_dns_request *r;
c = ev->data;
r = c->data;
tmp = r->out;
/* this event posted for one of two reasons:
* 1. mun_handle_request_read has reached end of request processing
* 2. socket newly available after incomplete write
*/
// write wouldnt work here anyways. event will retrigger when ready
if (!ev->ready) return;
if (!r->out) {
ngx_log_debug(NGX_LOG_ALERT, r->conn->log, 0,
"muninn: request processed but no output");
return;
}
r->out = c->send_chain(c, tmp, MUN_DNS_LEGACY_UDP_SZ);
for (/* tmp */; tmp != r->out; tmp = tmp->next) {
if (tmp->buf->pos == tmp->buf->last) ngx_free_chain(c->pool, tmp);
}
// request lifecycle complete condition
if (!r->out) return mun_close_connection(c);
// event will retrigger when write possible
if (!ev->ready) return;
// more can be written but we want to yield to other events on task loop
ngx_post_event(ev, &ngx_posted_events);
}
static ngx_int_t mun_log_request(mun_dns_request *r) {
ngx_log_error(NGX_LOG_DEBUG, r->conn->log, 0,
"muninn: request state machine entered");
return NGX_DONE;
}