diff --git a/daemon/cachedump.c b/daemon/cachedump.c index 6b04f10d6..2d800e5c3 100644 --- a/daemon/cachedump.c +++ b/daemon/cachedump.c @@ -48,6 +48,9 @@ #include "util/data/msgreply.h" #include "util/regional.h" #include "util/net_help.h" +#include "util/data/dname.h" +#include "iterator/iter_delegpt.h" +#include "iterator/iter_utils.h" /** convert to ldns rr */ static ldns_rr* @@ -776,3 +779,74 @@ load_cache(SSL* ssl, struct worker* worker) return 0; return read_fixed(ssl, worker->env.scratch_buffer, "EOF"); } + +int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm, + size_t nmlen, int ATTR_UNUSED(nmlabs)) +{ + /* deep links into the iterator module */ + struct delegpt* dp; + struct dns_msg* msg; + struct regional* region = worker->scratchpad; + char b[260]; + struct query_info qinfo; + size_t i, n_ns, n_miss, n_addr, n_res, n_avail; + regional_free_all(region); + qinfo.qname = nm; + qinfo.qname_len = nmlen; + qinfo.qtype = LDNS_RR_TYPE_A; + qinfo.qclass = LDNS_RR_CLASS_IN; + + dname_str(nm, b); + if(!ssl_printf(ssl, "The following name servers are used for lookup " + "of %s\n", b)) + return 0; + + while(1) { + dp = dns_cache_find_delegation(&worker->env, nm, nmlen, + qinfo.qtype, qinfo.qclass, region, &msg, + *worker->env.now); + if(!dp) { + return ssl_printf(ssl, "no delegation from " + "cache; goes to configured roots\n"); + } + /* print the dp */ + for(i=0; irep->rrset_count; i++) { + struct ub_packed_rrset_key* k = msg->rep->rrsets[i]; + struct packed_rrset_data* d = + (struct packed_rrset_data*)k->entry.data; + if(!dump_rrset(ssl, k, d, 0)) + return 0; + } + delegpt_count_ns(dp, &n_ns, &n_miss); + delegpt_count_addr(dp, &n_addr, &n_res, &n_avail); + /* since dp has not been used by iterator, all are available*/ + if(!ssl_printf(ssl, "Delegation with %d names, of which %d " + "have no addresses in cache.\n" + "It provides %d IP addresses.\n", + (int)n_ns, (int)n_miss, (int)n_addr)) + return 0; + /* go up? */ + if(iter_dp_is_useless(&qinfo, BIT_RD, dp)) { + if(!ssl_printf(ssl, "cache delegation was " + "useless (no IP addresses)\n")) + return 0; + if(dname_is_root(nm)) { + /* goes to root config */ + return ssl_printf(ssl, "no delegation from " + "cache; goes to configured roots\n"); + } else { + /* useless, goes up */ + nm = dp->name; + nmlen = dp->namelen; + dname_remove_label(&nm, &nmlen); + dname_str(nm, b); + if(!ssl_printf(ssl, "going up, lookup %s\n", b)) + return 0; + continue; + } + } else + break; + } + + return 1; +} diff --git a/daemon/cachedump.h b/daemon/cachedump.h index 61cc2a409..da9804ff1 100644 --- a/daemon/cachedump.h +++ b/daemon/cachedump.h @@ -91,4 +91,17 @@ int dump_cache(SSL* ssl, struct worker* worker); */ int load_cache(SSL* ssl, struct worker* worker); +/** + * Print the delegation used to lookup for this name. + * @param ssl: to read from + * @param worker: worker that is available (buffers, etc) and has + * ptrs to the caches. + * @param nm: name to lookup + * @param nmlen: length of name. + * @param nmlabs: labels in name. + * @return false on ssl error. + */ +int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm, + size_t nmlen, int nmlabs); + #endif /* DAEMON_DUMPCACHE_H */ diff --git a/daemon/remote.c b/daemon/remote.c index 845527ae9..16ca5c2c1 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -896,6 +896,7 @@ do_zone_add(SSL* ssl, struct worker* worker, char* arg) return; if(!local_zone_str2type(arg2, &t)) { ssl_printf(ssl, "error not a zone type. %s\n", arg2); + free(nm); return; } lock_quick_lock(&worker->daemon->local_zones->lock); @@ -905,6 +906,7 @@ do_zone_add(SSL* ssl, struct worker* worker, char* arg) lock_rw_wrlock(&z->lock); z->type = t; /* update type anyway */ lock_rw_unlock(&z->lock); + free(nm); lock_quick_unlock(&worker->daemon->local_zones->lock); send_ok(ssl); return; @@ -934,6 +936,7 @@ do_zone_remove(SSL* ssl, struct worker* worker, char* arg) nmlabs, LDNS_RR_CLASS_IN))) { /* present in tree */ local_zones_del_zone(worker->daemon->local_zones, z); + free(nm); } lock_quick_unlock(&worker->daemon->local_zones->lock); send_ok(ssl); @@ -962,9 +965,23 @@ do_data_remove(SSL* ssl, struct worker* worker, char* arg) return; local_zones_del_data(worker->daemon->local_zones, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN); + free(nm); send_ok(ssl); } +/** cache lookup of nameservers */ +static void +do_lookup(SSL* ssl, struct worker* worker, char* arg) +{ + uint8_t* nm; + int nmlabs; + size_t nmlen; + if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs)) + return; + (void)print_deleg_lookup(ssl, worker, nm, nmlen, nmlabs); + free(nm); +} + /** execute a remote control command */ static void execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd) @@ -991,6 +1008,8 @@ execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd) (void)dump_cache(ssl, rc->worker); } else if(strncmp(p, "load_cache", 10) == 0) { if(load_cache(ssl, rc->worker)) send_ok(ssl); + } else if(strncmp(p, "lookup", 6) == 0) { + do_lookup(ssl, rc->worker, skipwhite(p+6)); } else { (void)ssl_printf(ssl, "error unknown command '%s'\n", p); } diff --git a/doc/control_proto_spec.txt b/doc/control_proto_spec.txt index 1ad8d1cf9..0388acc44 100644 --- a/doc/control_proto_spec.txt +++ b/doc/control_proto_spec.txt @@ -45,10 +45,8 @@ flush_type flush_zone removes name and everything below that name from the cache. has to search through the cache item by item, so this is slow. -delegation - see what servers would be queried for the given name. -info - see data about the name. Slow, searches the cache item by item. +lookup + see what servers would be queried for a lookup of the given name. local_zone_remove the local-zone entry is removed. All data from the local zone is also deleted. diff --git a/doc/plan b/doc/plan index cd4cc0c61..b2b3b5f59 100644 --- a/doc/plan +++ b/doc/plan @@ -54,7 +54,7 @@ like dnswall does. Allow certain subdomains to do it, config options. addup stats over threads. not stats on SIGUSR1. perhaps also see which slow auth servers cause >1sec values. + remote control to add/remove localinfo, redirects. -* remote control to load/store cache contents ++ remote control to load/store cache contents + remote control to start, stop, reload. * remote control to flush names or domains (all under a name) from the cache. Include NSes. And the A, AAAA for its NSes. diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 85b5e8c4f..aa92b2792 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -91,6 +91,10 @@ redirect it to a file to store the cache in a file. The contents of the cache is loaded from stdin. Uses the same format as dump_cache uses. Loading the cache with old, or wrong data can result in old or wrong data returned to clients. +.TP +.B lookup \fIname +Print to stdout the name servers that would be used to look up the +name specified. .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_delegpt.c b/iterator/iter_delegpt.c index 4c17fc896..03f4ec8f1 100644 --- a/iterator/iter_delegpt.c +++ b/iterator/iter_delegpt.c @@ -157,8 +157,7 @@ delegpt_add_addr(struct delegpt* dp, struct regional* region, return 1; } -/** count NS and number missing */ -static void +void delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing) { struct delegpt_ns* ns; @@ -171,8 +170,7 @@ delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing) } } -/** count addresses, and number in result and available lists */ -static void +void delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres, size_t* numavail) { diff --git a/iterator/iter_delegpt.h b/iterator/iter_delegpt.h index 25e866571..8c122e520 100644 --- a/iterator/iter_delegpt.h +++ b/iterator/iter_delegpt.h @@ -228,6 +228,13 @@ struct delegpt_ns* delegpt_find_ns(struct delegpt* dp, uint8_t* name, */ void delegpt_log(enum verbosity_value v, struct delegpt* dp); +/** count NS and number missing for logging */ +void delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing); + +/** count addresses, and number in result and available lists, for logging */ +void delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres, + size_t* numavail); + /** * Add all usable targets to the result list. * @param dp: delegation point. diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index bbdc8135b..d9ea574b1 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -372,7 +372,8 @@ iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp) } int -iter_dp_is_useless(struct module_qstate* qstate, struct delegpt* dp) +iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, + struct delegpt* dp) { struct delegpt_ns* ns; /* check: @@ -385,18 +386,17 @@ iter_dp_is_useless(struct module_qstate* qstate, struct delegpt* dp) * o the query is for one of the nameservers in dp, * and that nameserver is a glue-name for this dp. */ - if(!(qstate->query_flags&BIT_RD)) + if(!(qflags&BIT_RD)) return 0; /* either available or unused targets */ if(dp->usable_list || dp->result_list) return 0; /* see if query is for one of the nameservers, which is glue */ - if( (qstate->qinfo.qtype == LDNS_RR_TYPE_A || - qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) && - dname_subdomain_c(qstate->qinfo.qname, dp->name) && - delegpt_find_ns(dp, qstate->qinfo.qname, - qstate->qinfo.qname_len)) + if( (qinfo->qtype == LDNS_RR_TYPE_A || + qinfo->qtype == LDNS_RR_TYPE_AAAA) && + dname_subdomain_c(qinfo->qname, dp->name) && + delegpt_find_ns(dp, qinfo->qname, qinfo->qname_len)) return 1; for(ns = dp->nslist; ns; ns = ns->next) { diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index 0edbb8ed4..7055fbaa6 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -135,10 +135,12 @@ void iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp); /** * See if delegation is useful or offers immediately no targets for * further recursion. - * @param qstate: query state with RD flag and query name. + * @param qinfo: query name and type + * @param qflags: query flags with RD flag * @param dp: delegpt to check. */ -int iter_dp_is_useless(struct module_qstate* qstate, struct delegpt* dp); +int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, + struct delegpt* dp); /** * See if delegation is expected to have DNSSEC information (RRSIGs) in diff --git a/iterator/iterator.c b/iterator/iterator.c index 98f8fb095..e2f4901f3 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -842,7 +842,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, * could be useless but lead to loops (bumping into the * same server reply) if useless-checked. */ - if(iter_dp_is_useless(qstate, iq->dp)) { + if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, + iq->dp)) { if(dname_is_root(iq->dp->name)) { /* use safety belt */ verbose(VERB_QUERY, "Cache has root NS but " diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index c1d04a585..a513db0c6 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -70,6 +70,7 @@ usage() printf(" local_data_remove [name] remove local RR data from name\n"); printf(" dump_cache print cache to stdout\n"); printf(" load_cache load cache from stdin\n"); + printf(" lookup [name] print nameservers for name\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);