From: Wouter Wijngaards Date: Fri, 20 Oct 2017 14:43:51 +0000 (+0000) Subject: authzone, handle probe return packets. X-Git-Tag: release-1.7.0rc1~174 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=15d892c62dcd9eea7b120f76bc567b390d128031;p=thirdparty%2Funbound.git authzone, handle probe return packets. git-svn-id: file:///svn/unbound/trunk@4384 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/Makefile.in b/Makefile.in index ab9ba3912..c009f0634 100644 --- a/Makefile.in +++ b/Makefile.in @@ -827,7 +827,9 @@ authzone.lo authzone.o: $(srcdir)/services/authzone.c config.h $(srcdir)/service $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgencode.h \ $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \ $(srcdir)/dnscrypt/cert.h $(srcdir)/util/config_file.h \ - $(srcdir)/util/module.h $(srcdir)/services/cache/dns.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h \ + $(srcdir)/util/module.h $(srcdir)/util/random.h $(srcdir)/services/cache/dns.h \ + $(srcdir)/services/outside_network.h \ + $(srcdir)/services/listen_dnsport.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h \ $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/parseutil.h $(srcdir)/validator/val_nsec3.h \ $(srcdir)/validator/val_secalgo.h fptr_wlist.lo fptr_wlist.o: $(srcdir)/util/fptr_wlist.c config.h $(srcdir)/util/fptr_wlist.h \ diff --git a/services/authzone.c b/services/authzone.c index 061c305d4..20318438e 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -2918,7 +2918,7 @@ check_packet_ok(sldns_buffer* pkt, uint16_t qtype, struct auth_xfer* xfr, uint32_t* serial) { uint16_t id; - /* TODO parse to see if packet worked, valid reply */ + /* parse to see if packet worked, valid reply */ /* check serial number of SOA */ if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) @@ -2994,6 +2994,65 @@ check_packet_ok(sldns_buffer* pkt, uint16_t qtype, struct auth_xfer* xfr, return 1; } +/** see if the serial means the zone has to be updated, i.e. the serial + * is newer than the zone serial, or we have no zone */ +static int +xfr_serial_means_update(struct auth_xfer* xfr, uint32_t serial) +{ + uint32_t zserial; + int have_zone, zone_expired; + lock_basic_lock(&xfr->lock); + zserial = xfr->serial; + have_zone = xfr->have_zone; + zone_expired = xfr->zone_expired; + lock_basic_unlock(&xfr->lock); + + if(!have_zone) + return 1; /* no zone, anything is better */ + if(zone_expired) + return 1; /* expired, the sent serial is better than expired + data */ + if(compare_serial(zserial, serial) < 0) + return 1; /* our serial is smaller than the sent serial, + the data is newer, fetch it */ + 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, + struct auth_master* master) +{ + log_assert(xfr->task_transfer != NULL); + log_assert(xfr->task_transfer->worker == NULL); + log_assert(xfr->task_transfer->chunks_first == NULL); + log_assert(xfr->task_transfer->chunks_last == NULL); + xfr->task_transfer->worker = env->worker; + xfr->task_transfer->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; + + /* TODO initiate TCP, and set timeout on it */ +} + /** callback for task_probe udp packets */ int auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err, @@ -3010,11 +3069,28 @@ auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err, /* TODO */ if(err == NETEVENT_NOERROR) { - uint32_t serial; + uint32_t serial = 0; if(check_packet_ok(c->buffer, LDNS_RR_TYPE_SOA, xfr, &serial)) { /* successful lookup */ - /* TODO */ + /* see if this serial indicates that the zone has + * to be updated */ + lock_basic_lock(&xfr->lock); + if(xfr_serial_means_update(xfr, serial)) { + /* 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)); + } + } else { + /* if zone not updated, start the wait timer again */ + if(xfr->task_nextprobe->worker == NULL) + xfr_set_timeout(xfr, env, 0); + } + lock_basic_unlock(&xfr->lock); + /* return, we don't sent a reply to this udp packet, + * and we setup the tasks to do next */ + return 0; } } @@ -3044,6 +3120,9 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env) if(!master) master = xfr->task_probe->scan_target; if(!master) return 0; + /* TODO: use mesh_new_callback to probe for non-addr hosts, + * and then wait for them to be looked up (in cache, or query) */ + /* create packet */ xfr_create_probe_packet(xfr, env, env->scratch_buffer, 1); if(!xfr->task_probe->cp) { @@ -3353,3 +3432,18 @@ xfer_set_masters(struct auth_master** list, struct config_auth* c) } return 1; } + +#define SERIAL_BITS 32 +int +compare_serial(uint32_t a, uint32_t b) +{ + const uint32_t cutoff = ((uint32_t) 1 << (SERIAL_BITS - 1)); + + if (a == b) { + return 0; + } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) { + return -1; + } else { + return 1; + } +} diff --git a/services/authzone.h b/services/authzone.h index c724a9a1e..f2de82893 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -306,6 +306,8 @@ struct auth_probe { struct auth_transfer { /* Worker pointer. NULL means unowned. */ struct worker* worker; + /* module env for this task */ + struct module_env* env; /** xfer data that has been transferred, the data is applied * once the transfer has completed correctly */ @@ -497,4 +499,12 @@ void auth_xfer_timer(void* arg); int auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err, struct comm_reply* repinfo); +/* + * Compares two 32-bit serial numbers as defined in RFC1982. Returns + * <0 if a < b, 0 if a == b, and >0 if a > b. The result is undefined + * if a != b but neither is greater or smaller (see RFC1982 section + * 3.2.). + */ +int compare_serial(uint32_t a, uint32_t b); + #endif /* SERVICES_AUTHZONE_H */ diff --git a/testcode/unitauth.c b/testcode/unitauth.c index f6c022aa0..6ca0be9d8 100644 --- a/testcode/unitauth.c +++ b/testcode/unitauth.c @@ -831,6 +831,24 @@ check_queries(const char* name, const char* zone, struct q_ans* queries) auth_zones_delete(az); } +/** Test authzone compare_serial */ +static void +authzone_compare_serial(void) +{ + if(vbmp) printf("Testing compare_serial\n"); + unit_assert(compare_serial(0, 1) < 0); + unit_assert(compare_serial(1, 0) > 0); + unit_assert(compare_serial(0, 0) == 0); + unit_assert(compare_serial(1, 1) == 0); + unit_assert(compare_serial(0xf0000000, 0xf0000000) == 0); + unit_assert(compare_serial(0, 0xf0000000) > 0); + unit_assert(compare_serial(0xf0000000, 0) < 0); + unit_assert(compare_serial(0xf0000000, 0xf0000001) < 0); + unit_assert(compare_serial(0xf0000002, 0xf0000001) > 0); + unit_assert(compare_serial(0x70000000, 0x80000000) < 0); + unit_assert(compare_serial(0x90000000, 0x70000000) > 0); +} + /** Test authzone read from file */ static void authzone_read_test(void) @@ -853,6 +871,7 @@ authzone_test(void) { unit_show_feature("authzone"); atexit(tmpfilecleanup); + authzone_compare_serial(); authzone_read_test(); authzone_query_test(); }