#include "ngx_event.h" #include "ngx_event_posted.h" #include "ngx_palloc.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 #include // 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); // request state machine static void mun_request_parse(ngx_connection_t *c); 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 }, // TODO: shared memory size cap // TODO: dns resource retry limit ngx_null_command }; static ngx_core_module_t ngx_muninn_daemon_module_ctx = { ngx_string("muninn_daemon"), NULL, NULL, /* TODO: * 1. init shm somewhere in here */ }; 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, // TODO: make bootstrapping request on a single worker here NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING }; 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_request_parse; 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 ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, "muninn DNS server enabled"); return NGX_CONF_OK; } static void mun_request_parse(ngx_connection_t *c) { ngx_pool_t *pool; mun_dns_request *r; if (!(pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, c->log))) return; if (!(r = ngx_pcalloc(pool, sizeof(mun_dns_request)))) { ngx_destroy_pool(pool); return; } if (!(r->ev = ngx_pcalloc(pool, sizeof(ngx_event_t)))) { ngx_destroy_pool(pool); return; } r->client = c->sockaddr; r->pool = pool; r->log = c->log; r->ev->data = r; r->ev->handler = mun_dns_cache_lookup; ngx_post_event(r->ev, &ngx_posted_events); /* TODO: * 4. parse requested DNS resource * 8. log new request */ } void mun_dns_cache_lookup(ngx_event_t *ev) { mun_dns_request *r = (mun_dns_request *) ev->data; /* TODO: request cache lookup handler * if hit, clear dependencies and set request next handler to postpone phase * if miss next handler is request records fetch phase AND attach cache returned closest node to tree * reschedule our request event. */ } void mun_dns_prep_record_fetch(ngx_event_t *ev) { mun_dns_request *r = (mun_dns_request *) ev->data; /* TODO: request record(s) prep fetch * find closest neighbor in request struct and figure out all resources we are missing * initialize linked requests for all dependencies. * each linked request should store a reference to this one as a dependent. * their handlers should all be set to fetch record (next) * * set this request next phase to postpone phase * dont reschedule. */ } void mun_dns_record_fetch(ngx_event_t *ev) { mun_dns_request *r = (mun_dns_request *) ev->data; /* TODO: fetch record phase * open a UDP connection or whatever the fuck to some godawful NS and get * the actual record. * finally store the record in the cache. * * next phase is postpone phase. * do reschedule. */ } void mun_postpone_request(ngx_event_t *ev) { mun_dns_request *r = (mun_dns_request *) ev->data; /* TODO: POSTPONE PHASE FOR LINKED REQUESTS * IFF there are still linked requests we depend on then return. * if there are requests depending on us then post their events now. * remove ourselves from their dependency linkedlist. * next phase is teardown, reschedule. * * if we have a request source (client wants to know) then set next to output phase * call output phase without rescheduling. */ } void mun_dns_record_response(ngx_event_t *ev) { mun_dns_request *r = (mun_dns_request *) ev->data; /* TODO: output phase * write the records to client. * next phase is teardown. * reschedule. */ } void mun_handle_request_error(ngx_event_t *ev) { mun_dns_request *r = (mun_dns_request *) ev->data; /* TODO: error handler phase * if dependent request then try to restart request processing for this. * (increment error counter in mun_dns_request) * if error counter >= max then set dependent request error counter the same * AND set dependent request phase handler to error handler * AND schedule dependent request * return without reschedule * * otherwise attempt retry * if not retriable then send some DNS error IDK * loop over dependencies and set their handlers to teardown. * MUST REMOVE THEM FROM QUEUE IF POSTED * re-schedule them all with teardown handlers. * set next phase to teardown. DONT reschedule. */ } void mun_request_teardown(ngx_event_t *ev) { mun_dns_request *r = (mun_dns_request *) ev->data; /* TODO: teardown phase * if dependencies then return awaiting full teardown * * if dependent request then remove ourselves from their dependencies * and schedule dependent request event * * log teardown * delete our pool */ }