From a2dcd9c0191c1f27439e228f72029ee83c36f1c3 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Fri, 13 Feb 2009 15:26:37 +0000 Subject: [PATCH] forward command for unbound-control. git-svn-id: file:///svn/unbound/trunk@1482 be551aaa-1e26-0410-a405-d3ace91eadb9 --- daemon/remote.c | 131 +++++++++++++++++++++++++++++-------- daemon/worker.c | 11 +++- doc/Changelog | 6 ++ doc/unbound-control.8.in | 21 ++++++ iterator/iter_fwd.c | 24 +++++++ iterator/iter_fwd.h | 25 ++++++- iterator/iter_utils.c | 6 -- iterator/iterator.c | 14 ++-- iterator/iterator.h | 3 - libunbound/libworker.c | 9 ++- smallapp/unbound-control.c | 3 + util/module.h | 4 ++ util/net_help.c | 13 ++++ util/net_help.h | 11 ++++ 14 files changed, 231 insertions(+), 50 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 6cd52e0ba..8d569bb7c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -63,6 +63,8 @@ #include "validator/val_kcache.h" #include "validator/val_kentry.h" #include "iterator/iterator.h" +#include "iterator/iter_fwd.h" +#include "iterator/iter_delegpt.h" #include "services/outbound_list.h" #include "services/outside_network.h" @@ -1221,24 +1223,102 @@ do_flush_name(SSL* ssl, struct worker* worker, char* arg) /** print root forwards */ static int -print_root_fwds(SSL* ssl, struct config_file* cfg) +print_root_fwds(SSL* ssl, struct iter_forwards* fwds, uint8_t* root) { - struct config_stub* s; - if(!ssl_printf(ssl, "root-forward:")) - return 0; - for(s = cfg->forwards; s; s = s->next) { - if(s->name && strcmp(s->name, ".") == 0) { - struct config_strlist* p; - for(p = s->hosts; p; p = p->next) - if(!ssl_printf(ssl, " %s", p->str)) - return 0; - for(p = s->addrs; p; p = p->next) - if(!ssl_printf(ssl, " %s", p->str)) - return 0; - return ssl_printf(ssl, "\n"); + char buf[257]; + struct delegpt* dp; + struct delegpt_ns* ns; + struct delegpt_addr* a; + int f = 0; + dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN); + if(!dp) + return ssl_printf(ssl, "off (using root hints)\n"); + /* if dp is returned it must be the root */ + log_assert(query_dname_compare(dp->name, root)==0); + for(ns = dp->nslist; ns; ns = ns->next) { + dname_str(ns->name, buf); + if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf)) + return 0; + f = 1; + } + for(a = dp->target_list; a; a = a->next_target) { + addr_to_str(&a->addr, a->addrlen, buf, sizeof(buf)); + if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf)) + return 0; + f = 1; + } + return ssl_printf(ssl, "\n"); +} + +/** parse args into delegpt */ +static struct delegpt* +parse_delegpt(SSL* ssl, struct regional* region, char* args, uint8_t* root) +{ + /* parse args and add in */ + char* p = args; + char* todo; + struct delegpt* dp = delegpt_create(region); + struct sockaddr_storage addr; + socklen_t addrlen; + if(!dp || !delegpt_set_name(dp, region, root)) { + (void)ssl_printf(ssl, "error out of memory\n"); + return NULL; + } + while(p) { + todo = p; + p = strchr(p, ' '); /* find next spot, if any */ + if(p) { + *p++ = 0; /* end this spot */ + p = skipwhite(p); /* position at next spot */ + } + /* parse address */ + if(!extstrtoaddr(todo, &addr, &addrlen)) { + (void)ssl_printf(ssl, "error cannot parse" + " IP address '%s'\n", todo); + return NULL; + } + /* add address */ + if(!delegpt_add_addr(dp, region, &addr, addrlen, 0, 1)) { + (void)ssl_printf(ssl, "error out of memory\n"); + return NULL; } } - return ssl_printf(ssl, " no (using root hints)\n"); + return dp; +} + +/** do the status command */ +static void +do_forward(SSL* ssl, struct worker* worker, char* args) +{ + struct iter_forwards* fwd = worker->env.fwds; + uint8_t* root = (uint8_t*)"\000"; + if(!fwd) { + (void)ssl_printf(ssl, "error: structure not allocated\n"); + return; + } + if(args == NULL || args[0] == 0) { + (void)print_root_fwds(ssl, fwd, root); + return; + } + /* set root forwards for this thread. since we are in remote control + * the actual mesh is not running, so we can freely edit it. */ + /* delete all the existing queries first */ + mesh_delete_all(worker->env.mesh); + /* reset the fwd structure ; the cfg is unchanged (shared by threads)*/ + /* this reset frees up memory */ + forwards_apply_cfg(fwd, worker->env.cfg); + if(strcmp(args, "off") == 0) { + forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root); + } else { + struct delegpt* dp; + if(!(dp = parse_delegpt(ssl, fwd->region, args, root))) + return; + if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) { + (void)ssl_printf(ssl, "error out of memory\n"); + return; + } + } + send_ok(ssl); } /** do the status command */ @@ -1261,8 +1341,6 @@ do_status(SSL* ssl, struct worker* worker) } if(!ssl_printf(ssl, " ]\n")) return; - if(!print_root_fwds(ssl, worker->env.cfg)) - return; uptime = (time_t)time(NULL) - (time_t)worker->daemon->time_boot.tv_sec; if(!ssl_printf(ssl, "uptime: %u seconds\n", (unsigned)uptime)) return; @@ -1310,21 +1388,11 @@ get_mesh_status(struct mesh_area* mesh, struct mesh_state* m, if(ol->first == NULL) snprintf(buf, len, " (empty_list)"); for(e = ol->first; e; e = e->next) { - int af = (int)((struct sockaddr_in*)&e->qsent->addr) - ->sin_family; - void* sinaddr = &((struct sockaddr_in*)&e->qsent->addr) - ->sin_addr; - if(addr_is_ip6(&e->qsent->addr, e->qsent->addrlen)) - sinaddr = &((struct sockaddr_in6*) - &e->qsent->addr)->sin6_addr; - snprintf(buf, len, " "); l = strlen(buf); buf += l; len -= l; - - if(inet_ntop(af, sinaddr, buf, (socklen_t)len) == 0) { - snprintf(buf, len, "(inet_ntop_error)"); - } + addr_to_str(&e->qsent->addr, e->qsent->addrlen, + buf, len); l = strlen(buf); buf += l; len -= l; } @@ -1434,6 +1502,11 @@ execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd, } else if(strncmp(p, "load_cache", 10) == 0) { if(load_cache(ssl, worker)) send_ok(ssl); return; + } else if(strncmp(p, "forward", 7) == 0) { + /* must always distribute this cmd */ + if(rc) distribute_cmd(rc, ssl, cmd); + do_forward(ssl, worker, skipwhite(p+7)); + return; } else if(strncmp(p, "flush_stats", 11) == 0) { /* must always distribute this cmd */ if(rc) distribute_cmd(rc, ssl, cmd); diff --git a/daemon/worker.c b/daemon/worker.c index e3ab440fd..08ea72a8d 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -65,6 +65,7 @@ #include "util/data/dname.h" #include "util/fptr_wlist.h" #include "util/tube.h" +#include "iterator/iter_fwd.h" #ifdef HAVE_SYS_TYPES_H # include @@ -158,7 +159,8 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker), + sizeof(worker->rndstate) + regional_get_mem(worker->scratchpad) + sizeof(*worker->env.scratch_buffer) - + ldns_buffer_capacity(worker->env.scratch_buffer); + + ldns_buffer_capacity(worker->env.scratch_buffer) + + forwards_get_mem(worker->env.fwds); if(cur_serv) { me += serviced_get_mem(cur_serv); } @@ -1119,6 +1121,12 @@ worker_init(struct worker* worker, struct config_file *cfg, worker->env.kill_sub = &mesh_state_delete; worker->env.detect_cycle = &mesh_detect_cycle; worker->env.scratch_buffer = ldns_buffer_new(cfg->msg_buffer_size); + if(!(worker->env.fwds = forwards_create()) || + !forwards_apply_cfg(worker->env.fwds, cfg)) { + log_err("Could not set forward zones"); + worker_delete(worker); + return 0; + } if(!worker->env.mesh || !worker->env.scratch_buffer) { worker_delete(worker); return 0; @@ -1151,6 +1159,7 @@ worker_delete(struct worker* worker) } mesh_delete(worker->env.mesh); ldns_buffer_free(worker->env.scratch_buffer); + forwards_delete(worker->env.fwds); listen_delete(worker->front); outside_network_delete(worker->back); comm_signal_delete(worker->comsig); diff --git a/doc/Changelog b/doc/Changelog index ba9280153..79d42011d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,9 @@ +13 February 2009: Wouter + - forwarder information now per-thread duplicated. + This keeps it read only for speed, with no locking necessary. + - forward command for unbound control to change forwarders to use + on the fly. + 12 February 2009: Wouter - call setusercontext if available (on BSD). - small refactor of stats clearing. diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 3b0471e69..cb6e9fa56 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -134,6 +134,27 @@ such as a higher verbosity level. Show what is worked on. Prints all queries that the server is currently working on. Prints the time that users have been waiting. For internal requests, no time is printed. And then prints out the module status. +.TP +.B forward [off | addr ... ] +Setup forwarding mode. Configures if the server should ask other upstream +nameservers, should go to the internet root nameservers itself, or show +the current config. You could pass the nameservers after a DHCP update. +.IP +Without arguments the current list of addresses used to forward all queries +to is printed. On startup this is from the forward-zone "." configuration. +Afterwards it shows the status. It prints off when no forwarding is used. +.IP +If \fIoff\fR is passed, forwarding is disabled and the root nameservers +are used. This can be used to avoid to avoid buggy or non-DNSSEC supporting +nameservers returned from DHCP. But may not work in hotels or hotspots. +.IP +If one or more IPv4 or IPv6 addresses are given, those are then used to forward +queries to. The addresses must be separated with spaces. With '@port' the +port number can be set explicitly (default port is 53 (DNS)). +.IP +By default the forwarder information from the config file for the root "." is +used. The config file is not changed, so after a reload these changes are +gone. Other forward zones from the config file are not affected by this command. .SH "EXIT CODE" The unbound-control program exits with status code 1 on error, 0 on success. .SH "SET UP" diff --git a/iterator/iter_fwd.c b/iterator/iter_fwd.c index d7485f624..94ca78f82 100644 --- a/iterator/iter_fwd.c +++ b/iterator/iter_fwd.c @@ -292,3 +292,27 @@ forwards_get_mem(struct iter_forwards* fwd) return sizeof(*fwd) + sizeof(*fwd->tree) + regional_get_mem(fwd->region); } + +int +forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp) +{ + if(!forwards_insert(fwd, c, dp)) + return 0; + fwd_init_parents(fwd); + return 1; +} + +void +forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) +{ + struct iter_forward_zone key; + key.node.key = &key; + key.dclass = c; + key.name = nm; + key.namelabs = dname_count_size_labels(nm, &key.namelen); + if(!rbtree_search(fwd->tree, &key)) + return; /* nothing to do */ + (void)rbtree_delete(fwd->tree, &key); + fwd_init_parents(fwd); +} + diff --git a/iterator/iter_fwd.h b/iterator/iter_fwd.h index dca4e648b..6ea2d1230 100644 --- a/iterator/iter_fwd.h +++ b/iterator/iter_fwd.h @@ -43,7 +43,6 @@ #ifndef ITERATOR_ITER_FWD_H #define ITERATOR_ITER_FWD_H #include "util/rbtree.h" -struct iter_env; struct config_file; struct delegpt; struct regional; @@ -128,4 +127,28 @@ size_t forwards_get_mem(struct iter_forwards* fwd); /** compare two fwd entries */ int fwd_cmp(const void* k1, const void* k2); +/** + * Add zone to forward structure. For external use since it recalcs + * the tree parents. + * @param fwd: the forward data structure + * @param c: class of zone + * @param dp: delegation point with name and target nameservers for new + * forward zone. This delegation point and all its data must be + * malloced in the fwd->region. (then it is freed when the fwd is + * deleted). + * @return false on failure (out of memory); + */ +int forwards_add_zone(struct iter_forwards* fwd, uint16_t c, + struct delegpt* dp); + +/** + * Remove zone from forward structure. For external use since it + * recalcs the tree parents. Does not actually release any memory, the region + * is unchanged. + * @param fwd: the forward data structure + * @param c: class of zone + * @param nm: name of zone (in uncompressed wireformat). + */ +void forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm); + #endif /* ITERATOR_ITER_FWD_H */ diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 2133be755..c35b276f1 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -112,12 +112,6 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) log_err("Could not set root or stub hints"); return 0; } - if(!iter_env->fwds) - iter_env->fwds = forwards_create(); - if(!iter_env->fwds || !forwards_apply_cfg(iter_env->fwds, cfg)) { - log_err("Could not set forward zones"); - return 0; - } if(!iter_env->donotq) iter_env->donotq = donotq_create(); if(!iter_env->donotq || !donotq_apply_cfg(iter_env->donotq, cfg)) { diff --git a/iterator/iterator.c b/iterator/iterator.c index dac8581e0..878c5c92e 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -89,7 +89,6 @@ iter_deinit(struct module_env* env, int id) free(iter_env->target_fetch_policy); priv_delete(iter_env->priv); hints_delete(iter_env->hints); - forwards_delete(iter_env->fwds); donotq_delete(iter_env->donotq); free(iter_env); env->modinfo[id] = NULL; @@ -775,16 +774,14 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id) * * @param qstate: query state. * @param iq: iterator query state. - * @param ie: iterator shared global environment. * @return true if the request is forwarded, false if not. * If returns true but, iq->dp is NULL then a malloc failure occurred. */ static int -forward_request(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie) +forward_request(struct module_qstate* qstate, struct iter_qstate* iq) { - struct delegpt* dp = forwards_lookup(ie->fwds, iq->qchase.qname, - iq->qchase.qclass); + struct delegpt* dp = forwards_lookup(qstate->env->fwds, + iq->qchase.qname, iq->qchase.qclass); if(!dp) return 0; /* send recursion desired to forward addr */ @@ -892,7 +889,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, } /* attempt to forward the request */ - if(forward_request(qstate, iq, ie)) + if(forward_request(qstate, iq)) { if(!iq->dp) { log_err("alloc failure for forward dp"); @@ -2129,8 +2126,7 @@ iter_get_mem(struct module_env* env, int id) if(!ie) return 0; return sizeof(*ie) + sizeof(int)*((size_t)ie->max_dependency_depth+1) - + hints_get_mem(ie->hints) + forwards_get_mem(ie->fwds) - + donotq_get_mem(ie->donotq); + + hints_get_mem(ie->hints) + donotq_get_mem(ie->donotq); } /** diff --git a/iterator/iterator.h b/iterator/iterator.h index a3f839bf9..c70dadd9c 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -87,9 +87,6 @@ struct iter_env { /** A flag to indicate whether or not we have an IPv6 route */ int supports_ipv6; - /** Mapping of forwarding zones to targets. */ - struct iter_forwards* fwds; - /** A set of inetaddrs that should never be queried. */ struct iter_donotq* donotq; diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 10baff79a..5f90bf249 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -61,6 +61,7 @@ #include "util/data/msgreply.h" #include "util/data/msgencode.h" #include "util/tube.h" +#include "iterator/iter_fwd.h" /** handle new query command for bg worker */ static void handle_newq(struct libworker* w, uint8_t* buf, uint32_t len); @@ -76,6 +77,7 @@ libworker_delete(struct libworker* w) !w->is_bg || w->is_bg_thread); ldns_buffer_free(w->env->scratch_buffer); regional_destroy(w->env->scratch); + forwards_delete(w->env->fwds); ub_randfree(w->env->rnd); free(w->env); } @@ -114,10 +116,15 @@ libworker_setup(struct ub_ctx* ctx, int is_bg) } w->env->scratch = regional_create_custom(cfg->msg_buffer_size); w->env->scratch_buffer = ldns_buffer_new(cfg->msg_buffer_size); + w->env->fwds = forwards_create(); + if(w->env->fwds && !forwards_apply_cfg(w->env->fwds, cfg)) { + forwards_delete(w->env->fwds); + w->env->fwds = NULL; + } if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } - if(!w->env->scratch || !w->env->scratch_buffer) { + if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds) { libworker_delete(w); return NULL; } diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index 2d90a05e7..9cfd46a24 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -83,6 +83,9 @@ usage() printf(" flush_stats flush statistics, make zero\n"); printf(" flush_requestlist drop queries that are worked on\n"); printf(" dump_requestlist show what is worked on\n"); + printf(" forward [off | addr ...] without arg show forward setup\n"); + printf(" or off to turn off root forwarding\n"); + printf(" or give list of ip addresses\n"); printf("Version %s\n", PACKAGE_VERSION); printf("BSD licensed, see LICENSE in source package for details.\n"); printf("Report bugs to %s\n", PACKAGE_BUGREPORT); diff --git a/util/module.h b/util/module.h index 216828490..736956716 100644 --- a/util/module.h +++ b/util/module.h @@ -58,6 +58,7 @@ struct mesh_area; struct mesh_state; struct val_anchors; struct val_neg_cache; +struct iter_forwards; /** Maximum number of modules in operation */ #define MAX_MODULE 5 @@ -208,6 +209,9 @@ struct module_env { /** negative cache, configured by the validator. if not NULL, * contains NSEC record lookup trees. */ struct val_neg_cache* neg_cache; + /** Mapping of forwarding zones to targets. + * iterator forwarder information. per-thread, created by worker */ + struct iter_forwards* fwds; /** module specific data. indexed by module id. */ void* modinfo[MAX_MODULE]; }; diff --git a/util/net_help.c b/util/net_help.c index 7a901c990..c77ccdc7c 100644 --- a/util/net_help.c +++ b/util/net_help.c @@ -461,3 +461,16 @@ addr_in_common(struct sockaddr_storage* addr1, int net1, if(match > min) match = min; return match; } + +void +addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen, + char* buf, size_t len) +{ + int af = (int)((struct sockaddr_in*)addr)->sin_family; + void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr; + if(addr_is_ip6(addr, addrlen)) + sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr; + if(inet_ntop(af, sinaddr, buf, (socklen_t)len) == 0) { + snprintf(buf, len, "(inet_ntop_error)"); + } +} diff --git a/util/net_help.h b/util/net_help.h index 0ad88b412..0a60cae98 100644 --- a/util/net_help.h +++ b/util/net_help.h @@ -258,4 +258,15 @@ void addr_mask(struct sockaddr_storage* addr, socklen_t len, int net); int addr_in_common(struct sockaddr_storage* addr1, int net1, struct sockaddr_storage* addr2, int net2, socklen_t addrlen); +/** + * Put address into string, works for IPv4 and IPv6. + * @param addr: address + * @param addrlen: length of address + * @param buf: result string stored here + * @param len: length of buf. + * On failure a string with "error" is stored inside. + */ +void addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen, + char* buf, size_t len); + #endif /* NET_HELP_H */ -- 2.47.3