diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index 0b355e0d3..7a07b9976 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -983,7 +983,7 @@ cachedb_get_mem(struct module_env* env, int id) */ static struct module_func_block cachedb_block = { "cachedb", - &cachedb_init, &cachedb_deinit, &cachedb_operate, + NULL, NULL, &cachedb_init, &cachedb_deinit, &cachedb_operate, &cachedb_inform_super, &cachedb_clear, &cachedb_get_mem }; diff --git a/config.h.in b/config.h.in index 2ffb487a5..88347fe4d 100644 --- a/config.h.in +++ b/config.h.in @@ -406,6 +406,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_H +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_PFVAR_H + /* Define this to use nghttp2 client. */ #undef HAVE_NGHTTP2 diff --git a/configure b/configure index f343fac9f..9dc603045 100755 --- a/configure +++ b/configure @@ -24317,7 +24317,21 @@ printf "%s\n" "#define USE_IPSET 1" >>confdefs.h IPSET_OBJ="ipset.lo" - # mnl + # BSD's pf + for ac_header in net/pfvar.h +do : + ac_fn_c_check_header_compile "$LINENO" "net/pfvar.h" "ac_cv_header_net_pfvar_h" " + #include + #include + +" +if test "x$ac_cv_header_net_pfvar_h" = xyes +then : + printf "%s\n" "#define HAVE_NET_PFVAR_H 1" >>confdefs.h + +else $as_nop + + # mnl # Check whether --with-libmnl was given. if test ${with_libmnl+y} @@ -24327,13 +24341,13 @@ else $as_nop withval="yes" fi - found_libmnl="no" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libmnl" >&5 + found_libmnl="no" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libmnl" >&5 printf %s "checking for libmnl... " >&6; } - if test x_$withval = x_ -o x_$withval = x_yes; then + if test x_$withval = x_ -o x_$withval = x_yes; then withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" - fi - for dir in $withval ; do + fi + for dir in $withval ; do if test -f "$dir/include/libmnl/libmnl.h" -o -f "$dir/include/libmnl/libmnl/libmnl.h"; then found_libmnl="yes" extralibmnl="" @@ -24351,10 +24365,14 @@ printf "%s\n" "found in $dir" >&6; } LIBS="$LIBS -lmnl" break; fi - done - if test x_$found_libmnl != x_yes; then - as_fn_error $? "Could not find libmnl, libmnl.h" "$LINENO" 5 - fi + done + if test x_$found_libmnl != x_yes; then + as_fn_error $? "Could not find libmnl, libmnl.h" "$LINENO" 5 + fi + +fi + +done ;; no|*) # nothing diff --git a/configure.ac b/configure.ac index 88c4ff1cc..5597abb88 100644 --- a/configure.ac +++ b/configure.ac @@ -1931,15 +1931,17 @@ case "$enable_ipset" in IPSET_OBJ="ipset.lo" AC_SUBST(IPSET_OBJ) - # mnl - AC_ARG_WITH(libmnl, AS_HELP_STRING([--with-libmnl=path],[specify explicit path for libmnl.]), + # BSD's pf + AC_CHECK_HEADERS([net/pfvar.h], [], [ + # mnl + AC_ARG_WITH(libmnl, AS_HELP_STRING([--with-libmnl=path],[specify explicit path for libmnl.]), [ ],[ withval="yes" ]) - found_libmnl="no" - AC_MSG_CHECKING(for libmnl) - if test x_$withval = x_ -o x_$withval = x_yes; then + found_libmnl="no" + AC_MSG_CHECKING(for libmnl) + if test x_$withval = x_ -o x_$withval = x_yes; then withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" - fi - for dir in $withval ; do + fi + for dir in $withval ; do if test -f "$dir/include/libmnl/libmnl.h" -o -f "$dir/include/libmnl/libmnl/libmnl.h"; then found_libmnl="yes" dnl assume /usr is in default path. @@ -1957,10 +1959,14 @@ case "$enable_ipset" in LIBS="$LIBS -lmnl" break; fi - done - if test x_$found_libmnl != x_yes; then - AC_MSG_ERROR([Could not find libmnl, libmnl.h]) - fi + done + if test x_$found_libmnl != x_yes; then + AC_MSG_ERROR([Could not find libmnl, libmnl.h]) + fi + ], [ + #include + #include + ]) ;; no|*) # nothing diff --git a/daemon/daemon.c b/daemon/daemon.c index dbb6db060..d81bec844 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -344,7 +344,7 @@ static int setup_acl_for_ports(struct acl_list* list, return 1; } -int +int daemon_open_shared_ports(struct daemon* daemon) { log_assert(daemon); @@ -444,6 +444,19 @@ daemon_open_shared_ports(struct daemon* daemon) return 1; } +int +daemon_privileged(struct daemon* daemon) +{ + daemon->env->cfg = daemon->cfg; + daemon->env->alloc = &daemon->superalloc; + daemon->env->worker = NULL; + if(!modstack_call_startup(&daemon->mods, daemon->cfg->module_conf, + daemon->env)) { + fatal_exit("failed to startup modules"); + } + return 1; +} + /** * Setup modules. setup module stack. * @param daemon: the daemon @@ -453,11 +466,15 @@ static void daemon_setup_modules(struct daemon* daemon) daemon->env->cfg = daemon->cfg; daemon->env->alloc = &daemon->superalloc; daemon->env->worker = NULL; - daemon->env->need_to_validate = 0; /* set by module init below */ - if(!modstack_setup(&daemon->mods, daemon->cfg->module_conf, - daemon->env)) { - fatal_exit("failed to setup modules"); + if(daemon->mods_inited) { + modstack_call_deinit(&daemon->mods, daemon->env); } + daemon->env->need_to_validate = 0; /* set by module init below */ + if(!modstack_call_init(&daemon->mods, daemon->cfg->module_conf, + daemon->env)) { + fatal_exit("failed to init modules"); + } + daemon->mods_inited = 1; log_edns_known_options(VERB_ALGO, daemon->env); } @@ -861,7 +878,7 @@ daemon_cleanup(struct daemon* daemon) daemon->views = NULL; if(daemon->env->auth_zones) auth_zones_cleanup(daemon->env->auth_zones); - /* key cache is cleared by module desetup during next daemon_fork() */ + /* key cache is cleared by module deinit during next daemon_fork() */ daemon_remote_clear(daemon->rc); for(i=0; inum; i++) worker_delete(daemon->workers[i]); @@ -891,7 +908,9 @@ daemon_delete(struct daemon* daemon) size_t i; if(!daemon) return; - modstack_desetup(&daemon->mods, daemon->env); + modstack_call_deinit(&daemon->mods, daemon->env); + modstack_call_destartup(&daemon->mods, daemon->env); + modstack_free(&daemon->mods); daemon_remote_delete(daemon->rc); for(i = 0; i < daemon->num_ports; i++) listening_ports_free(daemon->ports[i]); diff --git a/daemon/daemon.h b/daemon/daemon.h index 57665446d..a6b6391cc 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -115,6 +115,8 @@ struct daemon { struct module_env* env; /** stack of module callbacks */ struct module_stack mods; + /** The module stack has been inited */ + int mods_inited; /** access control, which client IPs are allowed to connect */ struct acl_list* acl; /** access control, which interfaces are allowed to connect */ @@ -162,6 +164,15 @@ struct daemon* daemon_init(void); */ int daemon_open_shared_ports(struct daemon* daemon); +/** + * Do daemon setup that needs privileges + * like opening privileged ports or opening device files. + * The cfg member pointer must have been set for the daemon. + * @param daemon: the daemon. + * @return: false on error. + */ +int daemon_privileged(struct daemon* daemon); + /** * Fork workers and start service. * When the routine exits, it is no longer forked. diff --git a/daemon/unbound.c b/daemon/unbound.c index 64d4236d3..306fe6caf 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -473,7 +473,11 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode, #endif #ifdef HAVE_GETPWNAM struct passwd *pwd = NULL; +#endif + if(!daemon_privileged(daemon)) + fatal_exit("could not do privileged setup"); +#ifdef HAVE_GETPWNAM if(cfg->username && cfg->username[0]) { if((pwd = getpwnam(cfg->username)) == NULL) fatal_exit("user '%s' does not exist.", cfg->username); diff --git a/dns64/dns64.c b/dns64/dns64.c index 83fb02779..3a43698a8 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -1044,8 +1044,8 @@ dns64_get_mem(struct module_env* env, int id) */ static struct module_func_block dns64_block = { "dns64", - &dns64_init, &dns64_deinit, &dns64_operate, &dns64_inform_super, - &dns64_clear, &dns64_get_mem + NULL, NULL, &dns64_init, &dns64_deinit, &dns64_operate, + &dns64_inform_super, &dns64_clear, &dns64_get_mem }; /** diff --git a/dynlibmod/dynlibmod.c b/dynlibmod/dynlibmod.c index 1e040a30e..c94115492 100644 --- a/dynlibmod/dynlibmod.c +++ b/dynlibmod/dynlibmod.c @@ -297,8 +297,8 @@ inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type */ static struct module_func_block dynlibmod_block = { "dynlib", - &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super, - &dynlibmod_clear, &dynlibmod_get_mem + NULL, NULL, &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, + &dynlibmod_inform_super, &dynlibmod_clear, &dynlibmod_get_mem }; struct module_func_block* dynlibmod_get_funcblock(void) diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index 1dff429ac..ead720f34 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -995,7 +995,8 @@ subnetmod_get_mem(struct module_env *env, int id) * The module function block */ static struct module_func_block subnetmod_block = { - "subnetcache", &subnetmod_init, &subnetmod_deinit, &subnetmod_operate, + "subnetcache", + NULL, NULL, &subnetmod_init, &subnetmod_deinit, &subnetmod_operate, &subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem }; diff --git a/ipsecmod/ipsecmod.c b/ipsecmod/ipsecmod.c index 19549d4ee..76f9b1965 100644 --- a/ipsecmod/ipsecmod.c +++ b/ipsecmod/ipsecmod.c @@ -615,7 +615,7 @@ ipsecmod_get_mem(struct module_env* env, int id) */ static struct module_func_block ipsecmod_block = { "ipsecmod", - &ipsecmod_init, &ipsecmod_deinit, &ipsecmod_operate, + NULL, NULL, &ipsecmod_init, &ipsecmod_deinit, &ipsecmod_operate, &ipsecmod_inform_super, &ipsecmod_clear, &ipsecmod_get_mem }; diff --git a/ipset/ipset.c b/ipset/ipset.c index af55de8d6..1ad2c09f4 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -17,9 +17,19 @@ #include "sldns/wire2str.h" #include "sldns/parseutil.h" +#ifdef HAVE_NET_PFVAR_H +#include +#include +#include +#include +#include +typedef intptr_t filter_dev; +#else #include #include #include +typedef struct mnl_socket * filter_dev; +#endif #define BUFF_LEN 256 @@ -41,24 +51,95 @@ static int error_response(struct module_qstate* qstate, int id, int rcode) { return 0; } -static struct mnl_socket * open_mnl_socket() { - struct mnl_socket *mnl; +#ifdef HAVE_NET_PFVAR_H +static void * open_filter() { + filter_dev dev; - mnl = mnl_socket_open(NETLINK_NETFILTER); - if (!mnl) { + dev = open("/dev/pf", O_RDWR); + if (dev == -1) { + log_err("open(\"/dev/pf\") failed: %s", strerror(errno)); + return NULL; + } + else + return (void *)dev; +} +#else +static void * open_filter() { + filter_dev dev; + + dev = mnl_socket_open(NETLINK_NETFILTER); + if (!dev) { log_err("ipset: could not open netfilter."); return NULL; } - if (mnl_socket_bind(mnl, 0, MNL_SOCKET_AUTOPID) < 0) { - mnl_socket_close(mnl); + if (mnl_socket_bind(dev, 0, MNL_SOCKET_AUTOPID) < 0) { + mnl_socket_close(dev); log_err("ipset: could not bind netfilter."); return NULL; } - return mnl; + return (void *)dev; } +#endif -static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void *ipaddr, int af) { +#ifdef HAVE_NET_PFVAR_H +static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) { + struct pfioc_table io; + struct pfr_addr addr; + const char *p; + int i; + + bzero(&io, sizeof(io)); + bzero(&addr, sizeof(addr)); + + p = strrchr(setname, '/'); + if (p) { + i = p - setname; + if (i >= PATH_MAX) { + errno = ENAMETOOLONG; + return -1; + } + memcpy(io.pfrio_table.pfrt_anchor, setname, i); + if (i < PATH_MAX) + io.pfrio_table.pfrt_anchor[i] = '\0'; + p++; + } + else + p = setname; + + if (strlen(p) >= PF_TABLE_NAME_SIZE) { + errno = ENAMETOOLONG; + return -1; + } + strlcpy(io.pfrio_table.pfrt_name, p, PF_TABLE_NAME_SIZE); + + io.pfrio_buffer = &addr; + io.pfrio_size = 1; + io.pfrio_esize = sizeof(addr); + + switch (af) { + case AF_INET: + addr.pfra_ip4addr = *(struct in_addr *)ipaddr; + addr.pfra_net = 32; + break; + case AF_INET6: + addr.pfra_ip6addr = *(struct in6_addr *)ipaddr; + addr.pfra_net = 128; + break; + default: + errno = EAFNOSUPPORT; + return -1; + } + addr.pfra_af = af; + + if (ioctl(dev, DIOCRADDADDRS, &io) == -1) { + log_err("ioctl failed: %s", strerror(errno)); + return -1; + } + return 0; +} +#else +static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) { struct nlmsghdr *nlh; struct nfgenmsg *nfg; struct nlattr *nested[2]; @@ -91,14 +172,15 @@ static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void mnl_attr_nest_end(nlh, nested[1]); mnl_attr_nest_end(nlh, nested[0]); - if (mnl_socket_sendto(mnl, nlh, nlh->nlmsg_len) < 0) { + if (mnl_socket_sendto(dev, nlh, nlh->nlmsg_len) < 0) { return -1; } return 0; } +#endif static void -ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl, +ipset_add_rrset_data(struct ipset_env *ie, struct packed_rrset_data *d, const char* setname, int af, const char* dname) { @@ -123,12 +205,16 @@ ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl, snprintf(ip, sizeof(ip), "(inet_ntop_error)"); verbose(VERB_QUERY, "ipset: add %s to %s for %s", ip, setname, dname); } - ret = add_to_ipset(mnl, setname, rr_data + 2, af); + ret = add_to_ipset((filter_dev)ie->dev, setname, rr_data + 2, af); if (ret < 0) { log_err("ipset: could not add %s into %s", dname, setname); - mnl_socket_close(mnl); - ie->mnl = NULL; +#if HAVE_NET_PFVAR_H + /* don't close as we might not be able to open again due to dropped privs */ +#else + mnl_socket_close((filter_dev)ie->dev); + ie->dev = NULL; +#endif break; } } @@ -137,8 +223,8 @@ ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl, static int ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, - struct mnl_socket *mnl, struct ub_packed_rrset_key *rrset, - const char *qname, const int qlen, const char *setname, int af) + struct ub_packed_rrset_key *rrset, const char *qname, int qlen, + const char *setname, int af) { static char dname[BUFF_LEN]; const char *ds, *qs; @@ -152,11 +238,20 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, log_err("bad domain name"); return -1; } + if (dname[dlen - 1] == '.') { + dlen--; + } + if (qname[qlen - 1] == '.') { + qlen--; + } for (p = env->cfg->local_zones_ipset; p; p = p->next) { ds = NULL; qs = NULL; plen = strlen(p->str); + if (p->str[plen - 1] == '.') { + plen--; + } if (dlen == plen || (dlen > plen && dname[dlen - plen - 1] == '.' )) { ds = dname + (dlen - plen); @@ -167,8 +262,7 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, if ((ds && strncasecmp(p->str, ds, plen) == 0) || (qs && strncasecmp(p->str, qs, plen) == 0)) { d = (struct packed_rrset_data*)rrset->entry.data; - ipset_add_rrset_data(ie, mnl, d, setname, - af, dname); + ipset_add_rrset_data(ie, d, setname, af, dname); break; } } @@ -178,7 +272,6 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, static int ipset_update(struct module_env *env, struct dns_msg *return_msg, struct query_info qinfo, struct ipset_env *ie) { - struct mnl_socket *mnl; size_t i; const char *setname; struct ub_packed_rrset_key *rrset; @@ -186,15 +279,17 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, static char qname[BUFF_LEN]; int qlen; - mnl = (struct mnl_socket *)ie->mnl; - if (!mnl) { +#ifdef HAVE_NET_PFVAR_H +#else + if (!ie->dev) { /* retry to create mnl socket */ - mnl = open_mnl_socket(); - if (!mnl) { + ie->dev = open_filter(); + if (!ie->dev) { + log_warn("ipset open_filter failed"); return -1; } - ie->mnl = mnl; } +#endif qlen = sldns_wire2str_dname_buf(qinfo.qname, qinfo.qname_len, qname, BUFF_LEN); @@ -217,8 +312,8 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, } if (setname) { - if(ipset_check_zones_for_rrset(env, ie, mnl, rrset, - qname, qlen, setname, af) == -1) + if(ipset_check_zones_for_rrset(env, ie, rrset, qname, + qlen, setname, af) == -1) return -1; } } @@ -226,7 +321,7 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, return 0; } -int ipset_init(struct module_env* env, int id) { +int ipset_startup(struct module_env* env, int id) { struct ipset_env *ipset_env; ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env)); @@ -237,7 +332,43 @@ int ipset_init(struct module_env* env, int id) { env->modinfo[id] = (void *)ipset_env; - ipset_env->mnl = NULL; +#ifdef HAVE_NET_PFVAR_H + ipset_env->dev = open_filter(); + if (!ipset_env->dev) { + log_err("ipset open_filter failed"); + return 0; + } +#else + ipset_env->dev = NULL; +#endif + return 1; +} + +void ipset_destartup(struct module_env* env, int id) { + filter_dev dev; + struct ipset_env *ipset_env; + + if (!env || !env->modinfo[id]) { + return; + } + ipset_env = (struct ipset_env*)env->modinfo[id]; + + dev = (filter_dev)ipset_env->dev; + if (dev) { +#if HAVE_NET_PFVAR_H + close(dev); +#else + mnl_socket_close(dev); +#endif + ipset_env->dev = NULL; + } + + free(ipset_env); + env->modinfo[id] = NULL; +} + +int ipset_init(struct module_env* env, int id) { + struct ipset_env *ipset_env = env->modinfo[id]; ipset_env->name_v4 = env->cfg->ipset_name_v4; ipset_env->name_v6 = env->cfg->ipset_name_v6; @@ -253,24 +384,8 @@ int ipset_init(struct module_env* env, int id) { return 1; } -void ipset_deinit(struct module_env *env, int id) { - struct mnl_socket *mnl; - struct ipset_env *ipset_env; - - if (!env || !env->modinfo[id]) { - return; - } - - ipset_env = (struct ipset_env *)env->modinfo[id]; - - mnl = (struct mnl_socket *)ipset_env->mnl; - if (mnl) { - mnl_socket_close(mnl); - ipset_env->mnl = NULL; - } - - free(ipset_env); - env->modinfo[id] = NULL; +void ipset_deinit(struct module_env *ATTR_UNUSED(env), int ATTR_UNUSED(id)) { + /* nothing */ } static int ipset_new(struct module_qstate* qstate, int id) { @@ -376,8 +491,8 @@ size_t ipset_get_mem(struct module_env *env, int id) { */ static struct module_func_block ipset_block = { "ipset", - &ipset_init, &ipset_deinit, &ipset_operate, - &ipset_inform_super, &ipset_clear, &ipset_get_mem + &ipset_startup, &ipset_destartup, &ipset_init, &ipset_deinit, + &ipset_operate, &ipset_inform_super, &ipset_clear, &ipset_get_mem }; struct module_func_block * ipset_get_funcblock(void) { diff --git a/ipset/ipset.h b/ipset/ipset.h index f60a8be8c..195c7db93 100644 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -37,7 +37,7 @@ extern "C" { #endif struct ipset_env { - void* mnl; + void* dev; int v4_enabled; int v6_enabled; @@ -50,6 +50,10 @@ struct ipset_qstate { int dummy; }; +/** Startup the ipset module */ +int ipset_startup(struct module_env* env, int id); +/** Destartup the ipset module */ +void ipset_destartup(struct module_env* env, int id); /** Init the ipset module */ int ipset_init(struct module_env* env, int id); /** Deinit the ipset module */ diff --git a/iterator/iterator.c b/iterator/iterator.c index 5a99ab0d7..cddb02717 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -4489,8 +4489,8 @@ iter_get_mem(struct module_env* env, int id) */ static struct module_func_block iter_block = { "iterator", - &iter_init, &iter_deinit, &iter_operate, &iter_inform_super, - &iter_clear, &iter_get_mem + NULL, NULL, &iter_init, &iter_deinit, &iter_operate, + &iter_inform_super, &iter_clear, &iter_get_mem }; struct module_func_block* diff --git a/libunbound/context.c b/libunbound/context.c index a319f59cd..05f57987a 100644 --- a/libunbound/context.c +++ b/libunbound/context.c @@ -75,7 +75,9 @@ context_finalize(struct ub_ctx* ctx) ctx->pipe_pid = getpid(); cfg_apply_local_port_policy(cfg, 65536); config_apply(cfg); - if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env)) + if(!modstack_call_startup(&ctx->mods, cfg->module_conf, ctx->env)) + return UB_INITFAIL; + if(!modstack_call_init(&ctx->mods, cfg->module_conf, ctx->env)) return UB_INITFAIL; listen_setup_locks(); log_edns_known_options(VERB_ALGO, ctx->env); diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c index 3c8955149..9c6a3e309 100644 --- a/libunbound/libunbound.c +++ b/libunbound/libunbound.c @@ -188,7 +188,9 @@ ub_ctx_create(void) int e = errno; ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); - modstack_desetup(&ctx->mods, ctx->env); + modstack_call_deinit(&ctx->mods, ctx->env); + modstack_call_destartup(&ctx->mods, ctx->env); + modstack_free(&ctx->mods); listen_desetup_locks(); edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); @@ -202,7 +204,9 @@ ub_ctx_create(void) tube_delete(ctx->qq_pipe); ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); - modstack_desetup(&ctx->mods, ctx->env); + modstack_call_deinit(&ctx->mods, ctx->env); + modstack_call_destartup(&ctx->mods, ctx->env); + modstack_free(&ctx->mods); listen_desetup_locks(); edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); @@ -360,7 +364,9 @@ ub_ctx_delete(struct ub_ctx* ctx) } libworker_delete_event(ctx->event_worker); - modstack_desetup(&ctx->mods, ctx->env); + modstack_call_deinit(&ctx->mods, ctx->env); + modstack_call_destartup(&ctx->mods, ctx->env); + modstack_free(&ctx->mods); a = ctx->alloc_list; while(a) { na = a->super; diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index b8f2d62fb..e231ad079 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -777,8 +777,8 @@ size_t pythonmod_get_mem(struct module_env* env, int id) */ static struct module_func_block pythonmod_block = { "python", - &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super, - &pythonmod_clear, &pythonmod_get_mem + NULL, NULL, &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, + &pythonmod_inform_super, &pythonmod_clear, &pythonmod_get_mem }; struct module_func_block* pythonmod_get_funcblock(void) diff --git a/respip/respip.c b/respip/respip.c index 942e082b9..db48f176e 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -1259,8 +1259,8 @@ respip_get_mem(struct module_env* env, int id) */ static struct module_func_block respip_block = { "respip", - &respip_init, &respip_deinit, &respip_operate, &respip_inform_super, - &respip_clear, &respip_get_mem + NULL, NULL, &respip_init, &respip_deinit, &respip_operate, + &respip_inform_super, &respip_clear, &respip_get_mem }; struct module_func_block* diff --git a/services/modstack.c b/services/modstack.c index a90d7178c..6c8af0505 100644 --- a/services/modstack.c +++ b/services/modstack.c @@ -95,6 +95,16 @@ modstack_init(struct module_stack* stack) stack->mod = NULL; } +void +modstack_free(struct module_stack* stack) +{ + if(!stack) + return; + stack->num = 0; + free(stack->mod); + stack->mod = NULL; +} + int modstack_config(struct module_stack* stack, const char* module_conf) { @@ -222,18 +232,59 @@ module_func_block* module_factory(const char** str) return NULL; } -int -modstack_setup(struct module_stack* stack, const char* module_conf, +int +modstack_call_startup(struct module_stack* stack, const char* module_conf, struct module_env* env) { int i; if(stack->num != 0) - modstack_desetup(stack, env); + fatal_exit("unexpected already initialised modules"); /* fixed setup of the modules */ if(!modstack_config(stack, module_conf)) { return 0; } + for(i=0; inum; i++) { + if(stack->mod[i]->startup == NULL) + continue; + verbose(VERB_OPS, "startup module %d: %s", + i, stack->mod[i]->name); + fptr_ok(fptr_whitelist_mod_startup(stack->mod[i]->startup)); + if(!(*stack->mod[i]->startup)(env, i)) { + log_err("module startup for module %s failed", + stack->mod[i]->name); + return 0; + } + } + return 1; +} + +int +modstack_call_init(struct module_stack* stack, const char* module_conf, + struct module_env* env) +{ + int i, changed = 0; env->need_to_validate = 0; /* set by module init below */ + for(i=0; inum; i++) { + while(*module_conf && isspace(*module_conf)) + module_conf++; + if(strncmp(stack->mod[i]->name, module_conf, + strlen(stack->mod[i]->name))) { + if(stack->mod[i]->startup || stack->mod[i]->destartup) { + log_err("changed module ordering during reload not supported, for module that needs startup"); + return 0; + } else { + changed = 1; + } + } + module_conf += strlen(stack->mod[i]->name); + } + if(changed) { + modstack_free(stack); + if(!modstack_config(stack, module_conf)) { + return 0; + } + } + for(i=0; inum; i++) { verbose(VERB_OPS, "init module %d: %s", i, stack->mod[i]->name); @@ -247,20 +298,29 @@ modstack_setup(struct module_stack* stack, const char* module_conf, return 1; } -void -modstack_desetup(struct module_stack* stack, struct module_env* env) +void +modstack_call_deinit(struct module_stack* stack, struct module_env* env) { int i; for(i=0; inum; i++) { fptr_ok(fptr_whitelist_mod_deinit(stack->mod[i]->deinit)); (*stack->mod[i]->deinit)(env, i); } - stack->num = 0; - free(stack->mod); - stack->mod = NULL; } -int +void +modstack_call_destartup(struct module_stack* stack, struct module_env* env) +{ + int i; + for(i=0; inum; i++) { + if(stack->mod[i]->destartup == NULL) + continue; + fptr_ok(fptr_whitelist_mod_destartup(stack->mod[i]->destartup)); + (*stack->mod[i]->destartup)(env, i); + } +} + +int modstack_find(struct module_stack* stack, const char* name) { int i; diff --git a/services/modstack.h b/services/modstack.h index 3ff01b54d..5674aefdd 100644 --- a/services/modstack.h +++ b/services/modstack.h @@ -60,6 +60,23 @@ struct module_stack { */ void modstack_init(struct module_stack* stack); +/** + * Free the stack of modules + * @param stack: stack that frees up memory. + */ +void modstack_free(struct module_stack* stack); + +/** + * Initialises modules and assignes ids. Calls module_startup(). + * @param stack: Expected empty, filled according to module_conf + * @param module_conf: string what modules to initialize + * @param env: module environment which is inited by the modules. + * environment should have a superalloc, cfg, + * @return on false a module init failed. + */ +int modstack_call_startup(struct module_stack* stack, const char* module_conf, + struct module_env* env); + /** * Read config file module settings and set up the modfunc block * @param stack: the stack of modules (empty before call). @@ -83,24 +100,31 @@ struct module_func_block* module_factory(const char** str); const char** module_list_avail(void); /** - * Setup modules. Assigns ids and calls module_init. - * @param stack: if not empty beforehand, it will be desetup()ed. - * It is then modstack_configged(). - * @param module_conf: string what modules to insert. + * Init modules. Calls module_init(). + * @param stack: It is modstack_setupped(). + * @param module_conf: module ordering to check against the ordering in stack. + * fails on changed ordering. * @param env: module environment which is inited by the modules. * environment should have a superalloc, cfg, * env.need_to_validate is set by the modules. * @return on false a module init failed. */ -int modstack_setup(struct module_stack* stack, const char* module_conf, +int modstack_call_init(struct module_stack* stack, const char* module_conf, struct module_env* env); /** - * Desetup the modules, deinit, delete. + * Deinit the modules. * @param stack: made empty. * @param env: module env for module deinit() calls. */ -void modstack_desetup(struct module_stack* stack, struct module_env* env); +void modstack_call_deinit(struct module_stack* stack, struct module_env* env); + +/** + * Destartup the modules, close, delete. + * @param stack: made empty. + * @param env: module env for module destartup() calls. + */ +void modstack_call_destartup(struct module_stack* stack, struct module_env* env); /** * Find index of module by name. diff --git a/smallapp/unbound-checkconf.c b/smallapp/unbound-checkconf.c index 508b40fc0..6cc5285ec 100644 --- a/smallapp/unbound-checkconf.c +++ b/smallapp/unbound-checkconf.c @@ -140,10 +140,13 @@ check_mod(struct config_file* cfg, struct module_func_block* fb) fatal_exit("out of memory"); if(!edns_known_options_init(&env)) fatal_exit("out of memory"); - if(!(*fb->init)(&env, 0)) { - fatal_exit("bad config for %s module", fb->name); - } + if(fb->startup && !(*fb->startup)(&env, 0)) + fatal_exit("bad config during startup for %s module", fb->name); + if(!(*fb->init)(&env, 0)) + fatal_exit("bad config during init for %s module", fb->name); (*fb->deinit)(&env, 0); + if(fb->destartup) + (*fb->destartup)(&env, 0); sldns_buffer_free(env.scratch_buffer); regional_destroy(env.scratch); edns_known_options_delete(&env); diff --git a/testcode/unitzonemd.c b/testcode/unitzonemd.c index 23c9f7010..81d92c102 100644 --- a/testcode/unitzonemd.c +++ b/testcode/unitzonemd.c @@ -287,9 +287,11 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, env.auth_zones = auth_zones_create(); if(!env.auth_zones) fatal_exit("out of memory"); - modstack_init(&mods); - if(!modstack_setup(&mods, env.cfg->module_conf, &env)) - fatal_exit("could not modstack_setup"); + memset(&mods, 0, sizeof(mods)); + if(!modstack_call_startup(&mods, env.cfg->module_conf, &env)) + fatal_exit("could not modstack_startup"); + if(!modstack_call_init(&mods, env.cfg->module_conf, &env)) + fatal_exit("could not modstack_call_init"); env.mesh = mesh_create(&mods, &env); if(!env.mesh) fatal_exit("out of memory"); @@ -327,7 +329,9 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, /* desetup test harness */ mesh_delete(env.mesh); - modstack_desetup(&mods, &env); + modstack_call_deinit(&mods, &env); + modstack_call_destartup(&mods, &env); + modstack_free(&mods); auth_zones_delete(env.auth_zones); anchors_delete(env.anchors); config_delete(env.cfg); diff --git a/testdata/ipset.tdir/ipset.pre b/testdata/ipset.tdir/ipset.pre index 42c94fac4..7c61e6468 100644 --- a/testdata/ipset.tdir/ipset.pre +++ b/testdata/ipset.tdir/ipset.pre @@ -8,6 +8,11 @@ PRE="../.." if grep "define USE_IPSET 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi +if grep "define HAVE_NET_PFVAR_H 1" $PRE/config.h; then + if test ! -f /dev/pf; then + skip_test "no /dev/pf" + fi +fi get_random_port 2 UNBOUND_PORT=$RND_PORT diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index a792a3429..b13988835 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -392,7 +392,7 @@ fptr_whitelist_modenv_detect_cycle(int (*fptr)( return 0; } -int +int fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)) { if(fptr == &iter_init) return 1; @@ -418,9 +418,10 @@ fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)) else if(fptr == &ipset_init) return 1; #endif return 0; + } -int +int fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id)) { if(fptr == &iter_deinit) return 1; @@ -448,6 +449,24 @@ fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id)) return 0; } +int +fptr_whitelist_mod_startup(int (*fptr)(struct module_env* env, int id)) +{ +#ifdef USE_IPSET + if(fptr == &ipset_startup) return 1; +#endif + return 0; +} + +int +fptr_whitelist_mod_destartup(void (*fptr)(struct module_env* env, int id)) +{ +#ifdef USE_IPSET + if(fptr == &ipset_destartup) return 1; +#endif + return 0; +} + int fptr_whitelist_mod_operate(void (*fptr)(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound)) diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index a0d986755..fb2475cce 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -278,6 +278,22 @@ int fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)); */ int fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id)); +/** + * Check function pointer whitelist for module startup call values. + * + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_mod_startup(int (*fptr)(struct module_env* env, int id)); + +/** + * Check function pointer whitelist for module destartup call values. + * + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_mod_destartup(void (*fptr)(struct module_env* env, int id)); + /** * Check function pointer whitelist for module operate call values. * diff --git a/util/module.h b/util/module.h index cf55e2c51..5bdb622a2 100644 --- a/util/module.h +++ b/util/module.h @@ -712,8 +712,29 @@ struct module_func_block { /** text string name of module */ const char* name; - /** - * init the module. Called once for the global state. + /** + * Set up the module for start. This is called only once at startup. + * Privileged operations like opening device files may be done here. + * The function ptr can be NULL, if it is not used. + * @param env: module environment. + * @param id: module id number. + * return: 0 on error + */ + int (*startup)(struct module_env* env, int id); + + /** + * Close down the module for stop. This is called only once before + * shutdown to free resources allocated during startup(). + * Closing privileged ports or files must be done here. + * The function ptr can be NULL, if it is not used. + * @param env: module environment. + * @param id: module id number. + */ + void (*destartup)(struct module_env* env, int id); + + /** + * Initialise the module. Called when restarting or reloading the + * daemon. * This is the place to apply settings from the config file. * @param env: module environment. * @param id: module id number. @@ -722,7 +743,8 @@ struct module_func_block { int (*init)(struct module_env* env, int id); /** - * de-init, delete, the module. Called once for the global state. + * Deinitialise the module, undo stuff done during init(). + * Called before reloading the daemon. * @param env: module environment. * @param id: module id number. */ diff --git a/validator/validator.c b/validator/validator.c index e608b9a0e..39ae58eae 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -3350,8 +3350,8 @@ val_get_mem(struct module_env* env, int id) */ static struct module_func_block val_block = { "validator", - &val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear, - &val_get_mem + NULL, NULL, &val_init, &val_deinit, &val_operate, &val_inform_super, + &val_clear, &val_get_mem }; struct module_func_block*