#include "ngx_event.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; /* TODO: (in muninn.h) * need a global tree structure for storing records. * should store both nameservers, empty non-leaves, and leaf node DNS records. * * if possible some text hashing function for fast lookup. * * cache lookup should return closest matching record in tree * * data structure needs to be atomic * * STORE IN SHARED MEMORY */ /* TODO: (in muninn.h) * mun_dns_request should store queue of resources (mun_dns_requests) it depends on * mun_dns_request should store an array of mun_dns_requests that depend on it * mun_dns_request should store the handler of the next phase to call * mun_dns_request should have an event in it whos handler calls stored phase of request * mun_dns_request should store an error counter * mun_dns_request should store the result of a record cache lookup */ // 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) { /* TODO: * 1. init new pool * 2. init new request obj * 3. track request source in request object here * 4. parse requested DNS resource * 5. put a new event in the dns request (handler = cache lookup handler) * 6. event->data = mun_dns_request * 7. post this event */ } // TODO: put this decl into muninn.h void mun_dns_cache_lookup(ngx_event_t *ev) { /* 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. */ } // TODO: put this decl into muninn.h void mun_dns_prep_record_fetch(ngx_event_t *ev) { /* 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. */ } // TODO: put this decl into muninn.h void mun_dns_record_fetch(ngx_event_t *ev) { /* 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. */ } // TODO: put this decl into muninn.h void mun_postpone_request(ngx_event_t *ev) { /* 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. */ } // TODO: put this decl into muninn.h void mun_dns_record_response(ngx_event_t *ev) { /* TODO: output phase * write the records to client. * next phase is teardown. * reschedule. */ } // TODO: put this decl into muninn.h void mun_handle_request_error(ngx_event_t *ev) { /* 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. */ } // TODO: put this decl into muninn.h void mun_request_teardown(ngx_event_t *ev) { /* 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 */ }