From: Wouter Wijngaards Date: Mon, 4 Dec 2017 16:24:24 +0000 (+0000) Subject: lookup and transfer setup X-Git-Tag: release-1.7.0rc1~141 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1ed537dc52a4e4bbfc03248d08965f79954c2a86;p=thirdparty%2Funbound.git lookup and transfer setup git-svn-id: file:///svn/unbound/trunk@4417 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/services/authzone.c b/services/authzone.c index 80643ce35..819b8bb19 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -76,6 +76,8 @@ #define AUTH_PROBE_TIMEOUT 100 /* msec */ /** when to stop with SOA probes (when exponential timeouts exceed this) */ #define AUTH_PROBE_TIMEOUT_STOP 1000 /* msec */ +/* auth transfer timeout for TCP connections, in msec */ +#define AUTH_TRANSFER_TIMEOUT 10000 /* msec */ /** pick up nextprobe task to start waiting to perform transfer actions */ static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env, @@ -2820,6 +2822,24 @@ xfr_transfer_start_lookups(struct auth_xfer* xfr) xfr->task_transfer->lookup_aaaa = 0; } +/** move to the next lookup of hostname for task_transfer */ +static void +xfr_transfer_move_to_next_lookup(struct auth_xfer* xfr, struct module_env* env) +{ + if(!xfr->task_transfer->lookup_target) + return; /* already at end of list */ + if(!xfr->task_transfer->lookup_aaaa && env->cfg->do_ip6) { + /* move to lookup AAAA */ + xfr->task_transfer->lookup_aaaa = 1; + return; + } + xfr->task_transfer->lookup_target = + xfr->task_transfer->lookup_target->next; + xfr->task_transfer->lookup_aaaa = 0; + if(!env->cfg->do_ip4 && xfr->task_transfer->lookup_target!=NULL) + xfr->task_transfer->lookup_aaaa = 1; +} + /** start the lookups for task_probe */ static void xfr_probe_start_lookups(struct auth_xfer* xfr) @@ -2858,16 +2878,24 @@ xfr_transfer_start_list(struct auth_xfer* xfr, struct auth_master* spec) xfr->task_transfer->masters, spec->host); if(xfr->task_transfer->scan_specific) { xfr->task_transfer->scan_target = NULL; + xfr->task_transfer->scan_addr = NULL; + if(xfr->task_transfer->scan_specific->list) + xfr->task_transfer->scan_addr = + xfr->task_transfer->scan_specific->list; return; } } /* no specific (notified) host to scan */ xfr->task_transfer->scan_specific = NULL; + xfr->task_transfer->scan_addr = NULL; /* pick up first scan target */ xfr->task_transfer->scan_target = xfr->task_transfer->masters; + if(xfr->task_transfer->scan_target && xfr->task_transfer-> + scan_target->list) + xfr->task_transfer->scan_addr = + xfr->task_transfer->scan_target->list; } - /** start the iteration of the task_probe list of masters */ static void xfr_probe_start_list(struct auth_xfer* xfr, struct auth_master* spec) @@ -2894,7 +2922,16 @@ xfr_probe_start_list(struct auth_xfer* xfr, struct auth_master* spec) xfr->task_probe->scan_target->list; } -/** pick up the master that is being scanned right now */ +/** pick up the master that is being scanned right now, task_transfer */ +static struct auth_master* +xfr_transfer_current_master(struct auth_xfer* xfr) +{ + if(xfr->task_transfer->scan_specific) + return xfr->task_transfer->scan_specific; + return xfr->task_transfer->scan_target; +} + +/** pick up the master that is being scanned right now, task_probe */ static struct auth_master* xfr_probe_current_master(struct auth_xfer* xfr) { @@ -2903,6 +2940,14 @@ xfr_probe_current_master(struct auth_xfer* xfr) return xfr->task_probe->scan_target; } +/** true if at end of list, task_transfer */ +static int +xfr_transfer_end_of_list(struct auth_xfer* xfr) +{ + return !xfr->task_transfer->scan_specific && + !xfr->task_transfer->scan_target; +} + /** true if at end of list, task_probe */ static int xfr_probe_end_of_list(struct auth_xfer* xfr) @@ -2910,6 +2955,32 @@ xfr_probe_end_of_list(struct auth_xfer* xfr) return !xfr->task_probe->scan_specific && !xfr->task_probe->scan_target; } +/** move to next master in list, task_transfer */ +static int +xfr_transfer_nextmaster(struct auth_xfer* xfr) +{ + if(!xfr->task_transfer->scan_specific && + !xfr->task_transfer->scan_target) + return 0; + if(xfr->task_transfer->scan_addr) { + xfr->task_transfer->scan_addr = + xfr->task_transfer->scan_addr->next; + if(xfr->task_transfer->scan_addr) + return 1; + } + if(xfr->task_transfer->scan_specific) { + xfr->task_transfer->scan_specific = NULL; + xfr->task_transfer->scan_target = xfr->task_transfer->masters; + return 1; + } + if(!xfr->task_transfer->scan_target) + return 0; + if(!xfr->task_transfer->scan_target->next) + return 0; + xfr->task_transfer->scan_target = xfr->task_transfer->scan_target->next; + return 1; +} + /** move to next master in list, task_probe */ static int xfr_probe_nextmaster(struct auth_xfer* xfr) @@ -2997,7 +3068,8 @@ xfr_fd_for_master(struct module_env* env, struct sockaddr_storage* to_addr, /** create probe packet for xfr */ static void -xfr_create_probe_packet(struct auth_xfer* xfr, sldns_buffer* buf, int soa) +xfr_create_probe_packet(struct auth_xfer* xfr, sldns_buffer* buf, int soa, + uint16_t id) { struct query_info qinfo; uint32_t serial; @@ -3019,7 +3091,7 @@ xfr_create_probe_packet(struct auth_xfer* xfr, sldns_buffer* buf, int soa) } qinfo.qclass = xfr->dclass; qinfo_query_encode(buf, &qinfo); - sldns_buffer_write_at(buf, 0, &xfr->task_probe->id, 2); + sldns_buffer_write_at(buf, 0, &id, 2); /* append serial for IXFR */ if(qinfo.qtype == LDNS_RR_TYPE_IXFR) { @@ -3162,14 +3234,131 @@ xfr_transfer_disown(struct auth_xfer* xfr) xfr->task_transfer->env = NULL; } +/** lookup a host name for its addresses, if needed */ +static int +xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env) +{ + struct sockaddr_storage addr; + socklen_t addrlen = 0; + struct auth_master* master = xfr->task_transfer->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) return 0; + if(extstrtoaddr(master->host, &addr, &addrlen)) { + /* not needed, host is in IP addr format */ + return 0; + } + + /* use mesh_new_callback to probe for non-addr hosts, + * and then wait for them to be looked up (in cache, or query) */ + qinfo.qname_len = sizeof(dname); + if(sldns_str2wire_dname_buf(master->host, dname, &qinfo.qname_len) + != 0) { + log_err("cannot parse host name of master %s", master->host); + return 0; + } + qinfo.qname = dname; + qinfo.qclass = xfr->dclass; + qinfo.qtype = LDNS_RR_TYPE_A; + if(xfr->task_transfer->lookup_aaaa) + qinfo.qtype = LDNS_RR_TYPE_AAAA; + qinfo.local_alias = NULL; + 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_transfer", buf2); + log_query_info(VERB_ALGO, buf, &qinfo); + } + edns.edns_present = 1; + edns.ext_rcode = 0; + edns.edns_version = 0; + edns.bits = EDNS_DO; + edns.opt_list = NULL; + if(sldns_buffer_capacity(buf) < 65535) + edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); + else edns.udp_size = 65535; + + if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, + &auth_xfer_transfer_lookup_callback, xfr)) { + log_err("out of memory lookup up master %s", master->host); + free(qinfo.qname); + return 0; + } + free(qinfo.qname); + return 1; +} + +/** initiate TCP to the target and fetch zone. + * returns true if that was successfully started, and timeout setup. */ +static int +xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env) +{ + struct sockaddr_storage addr; + socklen_t addrlen = 0; + struct auth_master* master = xfr->task_transfer->master; + if(!master) return 0; + + /* get master addr */ + if(xfr->task_transfer->scan_addr) { + addrlen = xfr->task_transfer->scan_addr->addrlen; + memmove(&addr, &xfr->task_transfer->scan_addr->addr, addrlen); + } else { + if(!extstrtoaddr(master->host, &addr, &addrlen)) { + char zname[255+1]; + dname_str(xfr->name, zname); + log_err("%s: cannot parse master %s", zname, + master->host); + return 0; + } + } + + /* always new cp? TODO */ + /* TODO: set REUSEADDR and tcp nonblock, and call connect on fd */ + if(!xfr->task_transfer->cp) { + int fd = xfr_fd_for_master(env, &addr, addrlen, master->host); + if(fd == -1) { + char zname[255+1]; + dname_str(xfr->name, zname); + verbose(VERB_ALGO, "cannot create fd for " + "xfr %s to %s", zname, master->host); + return 0; + } + xfr->task_transfer->cp = comm_point_create_tcp_out( + env->worker_base, 65552, + auth_xfer_transfer_tcp_callback, xfr); + if(!xfr->task_transfer->cp) { + close(fd); + log_err("malloc failure"); + return 0; + } + /* set timeout on TCP connection */ + comm_point_start_listening(xfr->task_transfer->cp, fd, + AUTH_TRANSFER_TIMEOUT); + } else { + comm_point_start_listening(xfr->task_transfer->cp, -1, + AUTH_TRANSFER_TIMEOUT); + } + + /* set the packet to be written */ + /* create new ID */ + xfr->task_transfer->id = (uint16_t)(ub_random(env->rnd)&0xffff); + xfr_create_probe_packet(xfr, xfr->task_transfer->cp->buffer, 0, + xfr->task_transfer->id); + + return 1; +} + /** perform next lookup, next transfer TCP, or end and resume wait time task */ static void xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env) { log_assert(xfr->task_transfer->worker == env->worker); - /* TODO */ -#if 0 /* are we performing lookups? */ while(xfr->task_transfer->lookup_target) { if(xfr_transfer_lookup_host(xfr, env)) { @@ -3186,6 +3375,7 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env) /* initiate TCP and fetch the zone from the master */ /* and set timeout on it */ while(!xfr_transfer_end_of_list(xfr)) { + xfr->task_transfer->master = xfr_transfer_current_master(xfr); if(xfr_transfer_init_fetch(xfr, env)) { /* successfully started, wait for callback */ return; @@ -3195,7 +3385,6 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env) break; } } -#endif lock_basic_lock(&xfr->lock); /* we failed to fetch the zone, move to wait task @@ -3207,6 +3396,107 @@ xfr_transfer_nexttarget_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_master_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_transfer lookup of host name, of A or AAAA */ +void auth_xfer_transfer_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_transfer); + env = xfr->task_transfer->env; + + /* 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_transfer->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_master_add_addrs(xfr->task_transfer-> + lookup_target, answer, wanted_qtype); + } + } + } + + /* move to lookup AAAA after A lookup, move to next hostname lookup, + * or move to fetch the zone, or, if nothing to do, end task_transfer */ + xfr_transfer_nexttarget_or_end(xfr, env); +} + +/** callback for task_transfer tcp connections */ +int +auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* repinfo) +{ + struct auth_xfer* xfr = (struct auth_xfer*)arg; + struct module_env* env; + log_assert(xfr->task_probe); + env = xfr->task_probe->env; + + /* TODO */ + (void)xfr; + (void)env; + (void)repinfo; + (void)c; + (void)err; + return 0; +} + + /** start transfer task by this worker , xfr is locked. */ static void xfr_start_transfer(struct auth_xfer* xfr, struct module_env* env, @@ -3276,7 +3566,8 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env, * this means we'll accept replies to previous retries to same ip */ if(timeout == AUTH_PROBE_TIMEOUT) xfr->task_probe->id = (uint16_t)(ub_random(env->rnd)&0xffff); - xfr_create_probe_packet(xfr, env->scratch_buffer, 1); + xfr_create_probe_packet(xfr, env->scratch_buffer, 1, + xfr->task_probe->id); if(!xfr->task_probe->cp) { int fd = xfr_fd_for_master(env, &addr, addrlen, master->host); if(fd == -1) { @@ -3503,51 +3794,6 @@ 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 rcode, sldns_buffer* buf, enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus)) @@ -3573,7 +3819,7 @@ void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf, struct ub_packed_rrset_key* answer = reply_find_answer_rrset(&rq, rep); if(answer) { - xfr_probe_add_addrs(xfr->task_probe-> + xfr_master_add_addrs(xfr->task_probe-> lookup_target, answer, wanted_qtype); } } diff --git a/services/authzone.h b/services/authzone.h index 187bd32e7..2ebc5f97e 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -342,6 +342,9 @@ struct auth_transfer { /** scan tries all the upstream masters. the scan current target. * or NULL if not working on sequential scan */ struct auth_master* scan_target; + /** what address we are scanning for the master, or NULL if the + * master is in IP format itself */ + struct auth_addr* scan_addr; /** the zone transfer in progress (or NULL if in scan). It is * from this master */ struct auth_master* master; @@ -353,6 +356,8 @@ struct auth_transfer { * with axfr is done. */ int ixfr_fail; + /** dns id of AXFR query */ + uint16_t id; /** the transfer (TCP) to the master. * on the workers event base. */ struct comm_point* cp; @@ -528,11 +533,17 @@ void auth_xfer_timer(void* arg); /** callback for commpoint udp replies to task_probe */ int auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err, struct comm_reply* repinfo); +/** callback for task_transfer tcp connections */ +int auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* repinfo); /** xfer probe timeout callback, part of task_probe */ void auth_xfer_probe_timer_callback(void* arg); /** mesh callback for task_probe on lookup of host names */ void auth_xfer_probe_lookup_callback(void* arg, int rcode, struct sldns_buffer* buf, enum sec_status sec, char* why_bogus); +/** mesh callback for task_transfer on lookup of host names */ +void auth_xfer_transfer_lookup_callback(void* arg, int rcode, + struct sldns_buffer* buf, enum sec_status sec, char* why_bogus); /* * Compares two 32-bit serial numbers as defined in RFC1982. Returns diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 4601b47d9..48ca36357 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1441,6 +1441,15 @@ struct comm_point* comm_point_create_udp(struct comm_base *ATTR_UNUSED(base), return NULL; } +struct comm_point* comm_point_create_tcp_out(struct comm_base* + ATTR_UNUSED(base), size_t ATTR_UNUSED(bufsize), + comm_point_callback_type* ATTR_UNUSED(callback), + void* ATTR_UNUSED(callback_arg)) +{ + /* used by authzone transfers */ + return NULL; +} + int comm_point_send_udp_msg(struct comm_point *ATTR_UNUSED(c), sldns_buffer* ATTR_UNUSED(packet), struct sockaddr* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen)) diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index eb3f4b73d..32a6ba02e 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -99,6 +99,7 @@ fptr_whitelist_comm_point(comm_point_callback_type *fptr) else if(fptr == &outnet_tcp_cb) return 1; else if(fptr == &tube_handle_listen) return 1; else if(fptr == &auth_xfer_probe_udp_callback) return 1; + else if(fptr == &auth_xfer_transfer_tcp_callback) return 1; return 0; } @@ -514,6 +515,7 @@ int fptr_whitelist_mesh_cb(mesh_cb_func_type fptr) else if(fptr == &libworker_event_done_cb) return 1; else if(fptr == &probe_answer_cb) return 1; else if(fptr == &auth_xfer_probe_lookup_callback) return 1; + else if(fptr == &auth_xfer_transfer_lookup_callback) return 1; return 0; }