From d88bb99957a9ba2f0881eda45d683338715e0b0c Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Fri, 1 Dec 2017 14:02:28 +0000 Subject: [PATCH] - auth zone work. probe hostname lookup. git-svn-id: file:///svn/unbound/trunk@4411 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 3 + libunbound/libworker.c | 21 +--- services/authzone.c | 247 ++++++++++++++++++++++++++++++++++------- services/authzone.h | 17 +++ util/data/msgreply.c | 19 ++++ util/data/msgreply.h | 4 + 6 files changed, 248 insertions(+), 63 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 66b79bbb2..070d49008 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +1 December 2017: Wouter + - auth zone work. + 30 November 2017: Wouter - Fix #3299 - forward CNAME daisy chain is not working diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 326603c2c..c991d5df3 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -421,25 +421,6 @@ int libworker_bg(struct ub_ctx* ctx) return UB_NOERROR; } -/** get msg reply struct (in temp region) */ -static struct reply_info* -parse_reply(sldns_buffer* pkt, struct regional* region, struct query_info* qi) -{ - struct reply_info* rep; - struct msg_parse* msg; - if(!(msg = regional_alloc(region, sizeof(*msg)))) { - return NULL; - } - memset(msg, 0, sizeof(*msg)); - sldns_buffer_set_position(pkt, 0); - if(parse_packet(pkt, msg, region) != 0) - return 0; - if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) { - return 0; - } - return rep; -} - /** insert canonname */ static int fill_canon(struct ub_result* res, uint8_t* s) @@ -513,7 +494,7 @@ libworker_enter_result(struct ub_result* res, sldns_buffer* buf, struct query_info rq; struct reply_info* rep; res->rcode = LDNS_RCODE_SERVFAIL; - rep = parse_reply(buf, temp, &rq); + rep = parse_reply_in_temp_region(buf, temp, &rq); if(!rep) { log_err("cannot parse buf"); return; /* error parsing buf, or out of memory */ diff --git a/services/authzone.c b/services/authzone.c index b04550d72..dc29385fe 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -1448,6 +1448,18 @@ auth_chunks_delete(struct auth_transfer* at) at->chunks_last = NULL; } +/** free master addr list */ +static void +auth_free_master_addrs(struct auth_addr* list) +{ + struct auth_addr *n; + while(list) { + n = list->next; + free(list); + list = n; + } +} + /** free the masters list */ static void auth_free_masters(struct auth_master* list) @@ -1455,6 +1467,7 @@ auth_free_masters(struct auth_master* list) struct auth_master* n; while(list) { n = list->next; + auth_free_master_addrs(list->list); free(list->host); free(list->file); free(list); @@ -2770,6 +2783,98 @@ xfr_master_start(struct auth_xfer* xfr) return 0; } +/** find master (from notify or probe) in list of masters */ +static struct auth_master* +find_master_by_host(struct auth_master* list, char* host) +{ + struct auth_master* p; + for(p=list; p; p=p->next) { + if(strcmp(p->host, host) == 0) + return p; + } + return NULL; +} + +/** start the lookups for task_probe */ +static void +xfr_probe_start_lookups(struct auth_xfer* xfr) +{ + struct auth_master* m; + /* delete all the looked up addresses in the list */ + for(m=xfr->task_probe->masters; m; m=m->next) { + if(m->list) { + auth_free_master_addrs(m->list); + m->list = NULL; + } + } + /* start lookup at the first master */ + xfr->task_probe->lookup_target = xfr->task_probe->masters; + xfr->task_probe->lookup_aaaa = 0; +} + +/** move to the next lookup of hostname for task_probe */ +static void +xfr_probe_move_to_next_lookup(struct auth_xfer* xfr, struct module_env* env) +{ + if(!xfr->task_probe->lookup_target) + return; /* already at end of list */ + if(!xfr->task_probe->lookup_aaaa && env->cfg->do_ip6) { + /* move to lookup AAAA */ + xfr->task_probe->lookup_aaaa = 1; + return; + } + xfr->task_probe->lookup_target = xfr->task_probe->lookup_target->next; + xfr->task_probe->lookup_aaaa = 0; + if(!env->cfg->do_ip4 && xfr->task_probe->lookup_target!=NULL) + xfr->task_probe->lookup_aaaa = 1; +} + +/** start the iteration of the task_transfer list of masters */ +static void +xfr_transfer_start_list(struct auth_xfer* xfr, struct auth_master* spec) +{ + if(spec) { + xfr->task_transfer->scan_specific = find_master_by_host( + xfr->task_transfer->masters, spec->host); + if(xfr->task_transfer->scan_specific) { + xfr->task_transfer->scan_target = NULL; + return; + } + } + /* no specific (notified) host to scan */ + xfr->task_transfer->scan_specific = NULL; + /* pick up first scan target */ + xfr->task_transfer->scan_target = xfr->task_transfer->masters; +} + + +/** start the iteration of the task_probe list of masters */ +static void +xfr_probe_start_list(struct auth_xfer* xfr, struct auth_master* spec) +{ + if(spec) { + xfr->task_probe->scan_specific = find_master_by_host( + xfr->task_probe->masters, spec->host); + if(xfr->task_probe->scan_specific) { + xfr->task_probe->scan_target = NULL; + return; + } + } + /* no specific (notified) host to scan */ + xfr->task_probe->scan_specific = NULL; + /* pick up first scan target */ + xfr->task_probe->scan_target = xfr->task_probe->masters; +} + +/** pick up the master that is being scanned right now */ +static struct auth_master* +xfr_probe_current_master(struct auth_xfer* xfr) +{ + if(xfr->task_probe->scan_specific) + return xfr->task_probe->scan_specific; + return xfr->task_probe->scan_target; +} + /** true if at end of list, task_probe */ static int xfr_probe_end_of_list(struct auth_xfer* xfr) @@ -3021,18 +3126,6 @@ xfr_serial_means_update(struct auth_xfer* xfr, uint32_t serial) return 0; } -/** find master (from notify or probe) in list of masters */ -static struct auth_master* -find_master_by_host(struct auth_master* list, char* host) -{ - struct auth_master* p; - for(p=list; p; p=p->next) { - if(strcmp(p->host, host) == 0) - return p; - } - return NULL; -} - /** start transfer task by this worker , xfr is locked. */ static void xfr_start_transfer(struct auth_xfer* xfr, struct module_env* env, @@ -3047,11 +3140,7 @@ xfr_start_transfer(struct auth_xfer* xfr, struct module_env* env, /* init transfer process */ /* find that master in the transfer's list of masters? */ - xfr->task_transfer->scan_specific = find_master_by_host( - xfr->task_transfer->masters, master->host); - if(xfr->task_transfer->scan_specific) - xfr->task_transfer->scan_target = NULL; - else xfr->task_transfer->scan_target = xfr->task_transfer->masters; + xfr_transfer_start_list(xfr, master); /* TODO initiate TCP, and set timeout on it */ } @@ -3080,8 +3169,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env, socklen_t addrlen = 0; struct timeval t; /* pick master */ - struct auth_master* master = xfr->task_probe->scan_specific; - if(!master) master = xfr->task_probe->scan_target; + struct auth_master* master = xfr_probe_current_master(xfr); if(!master) return 0; /* create packet */ @@ -3194,7 +3282,7 @@ auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err, /* if updated, start the transfer task, if needed */ if(xfr->task_transfer->worker == NULL) { xfr_start_transfer(xfr, env, - (xfr->task_probe->scan_specific?xfr->task_probe->scan_specific:xfr->task_probe->scan_target)); + xfr_probe_current_master(xfr)); } } else { /* if zone not updated, start the wait timer again */ @@ -3227,13 +3315,12 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) { struct sockaddr_storage addr; socklen_t addrlen = 0; - struct auth_master* master = xfr->task_probe->scan_specific; + struct auth_master* master = xfr->task_probe->lookup_target; struct query_info qinfo; uint16_t qflags = BIT_RD; uint8_t dname[LDNS_MAX_DOMAINLEN+1]; struct edns_data edns; sldns_buffer* buf = env->scratch_buffer; - if(!master) master = xfr->task_probe->scan_target; if(!master) return 0; if(extstrtoaddr(master->host, &addr, &addrlen)) { /* not needed, host is in IP addr format */ @@ -3251,9 +3338,17 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) qinfo.qname = dname; qinfo.qclass = xfr->dclass; qinfo.qtype = LDNS_RR_TYPE_A; - if(!env->cfg->do_ip4) qinfo.qtype = LDNS_RR_TYPE_AAAA; + if(xfr->task_probe->lookup_aaaa) + qinfo.qtype = LDNS_RR_TYPE_AAAA; qinfo.local_alias = NULL; - log_query_info(VERB_ALGO, "auth xfer master lookup", &qinfo); + if(verbosity >= VERB_ALGO) { + char buf[512]; + char buf2[LDNS_MAX_DOMAINLEN+1]; + dname_str(xfr->name, buf2); + snprintf(buf, sizeof(buf), "auth zone %s: master lookup" + " for task_probe", buf2); + log_query_info(VERB_ALGO, buf, &qinfo); + } edns.edns_present = 1; edns.ext_rcode = 0; edns.edns_version = 0; @@ -3277,12 +3372,21 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) static void xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env) { - while(!xfr_probe_end_of_list(xfr)) { + /* are we doing hostname lookups? */ + while(xfr->task_probe->lookup_target) { if(xfr_probe_lookup_host(xfr, env)) { - /* wait for lookup to finish */ + /* wait for lookup to finish, + * note that the hostname may be in unbound's cache + * and we may then get an instant cache response, + * and that calls the callback just like a full + * lookup and lookup failures also call callback */ return; } + xfr_probe_move_to_next_lookup(xfr, env); + } + /* send probe packets */ + while(!xfr_probe_end_of_list(xfr)) { if(xfr_probe_send_probe(xfr, env, AUTH_PROBE_TIMEOUT)) { /* successfully sent probe, wait for callback */ return; @@ -3303,27 +3407,84 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env) lock_basic_unlock(&xfr->lock); } +/** add addrs from A or AAAA rrset to the master */ +static void +xfr_probe_add_addrs(struct auth_master* m, struct ub_packed_rrset_key* rrset, + uint16_t rrtype) +{ + size_t i; + struct packed_rrset_data* data; + if(!m || !rrset) return; + data = (struct packed_rrset_data*)rrset->entry.data; + for(i=0; icount; i++) { + struct auth_addr* a; + size_t len = data->rr_len[i] - 2; + uint8_t* rdata = data->rr_data[i]+2; + if(rrtype == LDNS_RR_TYPE_A && len != INET_SIZE) + continue; /* wrong length for A */ + if(rrtype == LDNS_RR_TYPE_AAAA && len != INET6_SIZE) + continue; /* wrong length for AAAA */ + + /* add and alloc it */ + a = (struct auth_addr*)calloc(1, sizeof(*a)); + if(!a) { + log_err("out of memory"); + return; + } + if(rrtype == LDNS_RR_TYPE_A) { + struct sockaddr_in* sa; + a->addrlen = (socklen_t)sizeof(*sa); + sa = (struct sockaddr_in*)&a->addr; + sa->sin_family = AF_INET; + sa->sin_port = (in_port_t)htons(UNBOUND_DNS_PORT); + memmove(&sa->sin_addr, rdata, INET_SIZE); + } else { + struct sockaddr_in6* sa; + a->addrlen = (socklen_t)sizeof(*sa); + sa = (struct sockaddr_in6*)&a->addr; + sa->sin6_family = AF_INET6; + sa->sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT); + memmove(&sa->sin6_addr, rdata, INET6_SIZE); + } + /* append to list */ + a->next = m->list; + m->list = a; + } +} + /** callback for task_probe lookup of host name, of A or AAAA */ -void auth_xfer_probe_lookup_callback(void* arg, int ATTR_UNUSED(rcode), - sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(sec), - char* ATTR_UNUSED(why_bogus)) +void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf, + enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus)) { struct auth_xfer* xfr = (struct auth_xfer*)arg; struct module_env* env; log_assert(xfr->task_probe); env = xfr->task_probe->env; - - /* TODO: have A,AAAA phase bools stored in task_probe for this name */ - /* TODO process result */ + /* process result */ + if(rcode == LDNS_RCODE_NOERROR) { + uint16_t wanted_qtype = LDNS_RR_TYPE_A; + struct regional* temp = env->scratch; + struct query_info rq; + struct reply_info* rep; + if(xfr->task_probe->lookup_aaaa) + wanted_qtype = LDNS_RR_TYPE_AAAA; + memset(&rq, 0, sizeof(rq)); + rep = parse_reply_in_temp_region(buf, temp, &rq); + if(rep && rq.qtype == wanted_qtype && + FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) { + /* parsed successfully */ + struct ub_packed_rrset_key* answer = + reply_find_answer_rrset(&rq, rep); + if(answer) { + xfr_probe_add_addrs(xfr->task_probe-> + lookup_target, answer, wanted_qtype); + } + } + } - /* TODO: set previously looked up A and AAAAs before a scan starts - * to be fetched again */ - - /* TODO setup lookup of AAAA (unless this was the AAAA, or no ip6) */ - - /* if finished, resume send_or_end, set it to not start this again */ - /* TODO: set send_or_end to not call lookups on this master */ + /* move to lookup AAAA after A lookup, move to next hostname lookup, + * or move to send the probes, or, if nothing to do, end task_probe */ xfr_probe_send_or_end(xfr, env); } @@ -3364,10 +3525,10 @@ auth_xfer_timer(void* arg) xfr->task_probe->cp = NULL; /* start the task */ - /* timeout, no specific (notified) host to scan */ - xfr->task_probe->scan_specific = NULL; - /* pick up first scan target */ - xfr->task_probe->scan_target = xfr->task_probe->masters; + /* this was a timeout, so no specific first master to scan */ + xfr_probe_start_list(xfr, NULL); + /* setup to start the lookup of hostnames of masters afresh */ + xfr_probe_start_lookups(xfr); /* send the probe packet or next send, or end task */ xfr_probe_send_or_end(xfr, env); } else { diff --git a/services/authzone.h b/services/authzone.h index 7e2a57310..bf87d7c00 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -285,6 +285,11 @@ struct auth_probe { /** list of upstream masters for this zone, from config */ struct auth_master* masters; + /** for the hostname lookups, which master is current */ + struct auth_master* lookup_target; + /** are we looking up A or AAAA, first A, then AAAA (if ip6 enabled) */ + int lookup_aaaa; + /** once notified, or the timeout has been reached. a scan starts. */ /** the scan specific target (notify source), or NULL if none */ struct auth_master* scan_specific; @@ -346,6 +351,16 @@ struct auth_transfer { struct comm_point* cp; }; +/** list of addresses */ +struct auth_addr { + /** next in list */ + struct auth_addr* next; + /** IP address */ + struct sockaddr_storage addr; + /** addr length */ + socklen_t addrlen; +}; + /** auth zone master upstream, and the config settings for it */ struct auth_master { /** next master in list */ @@ -360,6 +375,8 @@ struct auth_master { int ixfr; /** use ssl for channel */ int ssl; + /** if the host is a hostname, the list of resolved addrs, if any*/ + struct auth_addr* list; }; /** auth zone master zone transfer data chunk */ diff --git a/util/data/msgreply.c b/util/data/msgreply.c index ae2fe02b6..153617692 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -896,6 +896,25 @@ reply_all_rrsets_secure(struct reply_info* rep) return 1; } +struct reply_info* +parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region, + struct query_info* qi) +{ + struct reply_info* rep; + struct msg_parse* msg; + if(!(msg = regional_alloc(region, sizeof(*msg)))) { + return NULL; + } + memset(msg, 0, sizeof(*msg)); + sldns_buffer_set_position(pkt, 0); + if(parse_packet(pkt, msg, region) != 0) + return 0; + if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) { + return 0; + } + return rep; +} + int edns_opt_append(struct edns_data* edns, struct regional* region, uint16_t code, size_t len, uint8_t* data) { diff --git a/util/data/msgreply.h b/util/data/msgreply.h index b66f344e1..60e6438a8 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -285,6 +285,10 @@ int parse_create_msg(struct sldns_buffer* pkt, struct msg_parse* msg, struct alloc_cache* alloc, struct query_info* qinf, struct reply_info** rep, struct regional* region); +/** get msg reply struct (in temp region) */ +struct reply_info* parse_reply_in_temp_region(struct sldns_buffer* pkt, + struct regional* region, struct query_info* qi); + /** * Sorts the ref array. * @param rep: reply info. rrsets must be filled in.