From: W.C.A. Wijngaards Date: Wed, 1 Jul 2026 11:05:19 +0000 (+0200) Subject: - auth-load-thread, process IXFR, by making a copy, adjust changes, swap in. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=56f66de89f43bb2c7b9dd998a0d7bb36b74b8a29;p=thirdparty%2Funbound.git - auth-load-thread, process IXFR, by making a copy, adjust changes, swap in. --- diff --git a/services/authload.c b/services/authload.c index e248e13e1..610a47bba 100644 --- a/services/authload.c +++ b/services/authload.c @@ -358,6 +358,11 @@ auth_load_process_http(struct auth_load_thread* thr) sldns_buffer_free(scratch_buffer); return 0; } + if(auth_load_thread_poll_for_quit(thr)) { + sldns_buffer_free(scratch_buffer); + auth_zone_delete_proxy(z); + return 0; + } xfr_http_preview(task->file, task->chunks_first); if(!xfr_http_syntax_check(task->name, task->namelen, task->dclass, @@ -366,6 +371,11 @@ auth_load_process_http(struct auth_load_thread* thr) auth_zone_delete_proxy(z); return 0; } + if(auth_load_thread_poll_for_quit(thr)) { + sldns_buffer_free(scratch_buffer); + auth_zone_delete_proxy(z); + return 0; + } if(!xfr_apply_http(task->name, task->namelen, task->host, task->file, task->chunks_first, z, scratch_buffer)) { sldns_buffer_free(scratch_buffer); @@ -375,9 +385,140 @@ auth_load_process_http(struct auth_load_thread* thr) sldns_buffer_free(scratch_buffer); if(z->rpz) rpz_finish_config(z->rpz); + if(auth_load_thread_poll_for_quit(thr)) { + sldns_buffer_free(scratch_buffer); + auth_zone_delete_proxy(z); + return 0; + } auth_load_swap_zone(thr, z); auth_zone_delete_proxy(z); + sldns_buffer_free(scratch_buffer); + return 1; +} + +/** Copy RRset and append it to the domain, update last pointer. */ +static int +rrset_append_copy(struct auth_data* domain, struct auth_rrset* rrset, + struct auth_rrset** last) +{ + struct auth_rrset* s = calloc(1, sizeof(*s)); + if(!s) + return 0; + s->type = rrset->type; + s->data = (struct packed_rrset_data*)memdup(rrset->data, + packed_rrset_sizeof(rrset->data)); + if(!s->data) + return 0; + packed_rrset_ptr_fixup(s->data); + if(!*last) + domain->rrsets = s; + else (*last)->next = s; + *last = s; + return 1; +} + +/** Copy the existing zone for modification */ +static int +auth_load_copy_into_zone(struct auth_load_thread* thr, struct auth_zone* proxyz) +{ + int count = 0; + struct auth_zone* z; + struct auth_data* d; + lock_rw_rdlock(&thr->task->worker->env.auth_zones->lock); + z = auth_zone_find(thr->task->worker->env.auth_zones, + thr->task->name, thr->task->namelen, thr->task->dclass); + if(!z) { + lock_rw_unlock(&thr->task->worker->env.auth_zones->lock); + verbose(VERB_ALGO, "auth zone missing for copy for IXFR."); + return 0; + } + lock_rw_rdlock(&z->lock); + lock_rw_unlock(&thr->task->worker->env.auth_zones->lock); + + /* Copy from z into proxyz. */ + RBTREE_FOR(d, struct auth_data*, &z->data) { + struct auth_rrset* rrset, *last = NULL; + struct auth_data* proxy_d = az_domain_create(proxyz, + d->name, d->namelen); + if(!proxy_d) { + log_err("out of memory"); + lock_rw_unlock(&z->lock); + return 0; + } + for(rrset = d->rrsets; rrset; rrset=rrset->next) { + if(!rrset_append_copy(proxy_d, rrset, &last)) { + log_err("out of memory"); + lock_rw_unlock(&z->lock); + return 0; + } + if((count++)%10000 == 0) { + if(auth_load_thread_poll_for_quit(thr)) { + lock_rw_unlock(&z->lock); + return 0; + } + } + } + if((count++)%10000 == 0) { + if(auth_load_thread_poll_for_quit(thr)) { + lock_rw_unlock(&z->lock); + return 0; + } + } + } + + lock_rw_unlock(&z->lock); + return 1; +} + +/** Process ixfr transfer */ +static int +auth_load_process_ixfr(struct auth_load_thread* thr) +{ + struct auth_load_task* task = thr->task; + struct sldns_buffer* scratch_buffer; + struct auth_zone* z; + + scratch_buffer = sldns_buffer_new(sldns_buffer_capacity( + thr->task->worker->env.scratch_buffer)); + if(!scratch_buffer) { + log_err("out of memory"); + return 0; + } + z = auth_zone_create_proxy(task->name, task->namelen, task->dclass); + if(!z) { + log_err("out of memory"); + sldns_buffer_free(scratch_buffer); + return 0; + } + if(auth_load_thread_poll_for_quit(thr)) { + sldns_buffer_free(scratch_buffer); + auth_zone_delete_proxy(z); + return 0; + } + + /* Copy the existing zone for modification, that uses a read lock. + * That then does not interrupt the service of threads. */ + if(!auth_load_copy_into_zone(thr, z)) { + sldns_buffer_free(scratch_buffer); + auth_zone_delete_proxy(z); + return 0; + } + if(!xfr_apply_ixfr(task->chunks_first, task->serial, z, + scratch_buffer)) { + sldns_buffer_free(scratch_buffer); + auth_zone_delete_proxy(z); + return 0; + } + if(auth_load_thread_poll_for_quit(thr)) { + sldns_buffer_free(scratch_buffer); + auth_zone_delete_proxy(z); + return 0; + } + + auth_load_swap_zone(thr, z); + auth_zone_delete_proxy(z); + sldns_buffer_free(scratch_buffer); return 1; } @@ -392,6 +533,8 @@ auth_load_thread_process(struct auth_load_thread* thr) if(!auth_load_process_http(thr)) return 0; } else if(task->on_ixfr && !task->on_ixfr_is_axfr) { + if(!auth_load_process_ixfr(thr)) + return 0; } else { } return 1; diff --git a/services/authzone.c b/services/authzone.c index 8e69f7bf3..9e601484a 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -593,7 +593,7 @@ auth_zone_set_fallback(struct auth_zone* z, char* fallbackstr) } /** create domain with the given name */ -static struct auth_data* +struct auth_data* az_domain_create(struct auth_zone* z, uint8_t* nm, size_t nmlen) { struct auth_data* n = (struct auth_data*)malloc(sizeof(*n)); @@ -4823,10 +4823,10 @@ http_parse_add_rr(const char* host, const char* file, struct auth_zone* z, /** RR list iterator, returns RRs from answer section one by one from the * dns packets in the chunklist */ static void -chunk_rrlist_start(struct auth_xfer* xfr, struct auth_chunk** rr_chunk, +chunk_rrlist_start(struct auth_chunk* chunk_list, struct auth_chunk** rr_chunk, int* rr_num, size_t* rr_pos) { - *rr_chunk = xfr->task_transfer->chunks_first; + *rr_chunk = chunk_list; *rr_num = 0; *rr_pos = 0; } @@ -4993,9 +4993,9 @@ ixfr_start_serial(struct auth_chunk* rr_chunk, int rr_num, size_t rr_pos, } /** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */ -static int -apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, - struct sldns_buffer* scratch_buffer) +int +xfr_apply_ixfr(struct auth_chunk* chunk_list, uint32_t xfr_serial, + struct auth_zone* z, struct sldns_buffer* scratch_buffer) { struct auth_chunk* rr_chunk; int rr_num; @@ -5011,7 +5011,7 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, int softfail = 0; /* start RR iterator over chunklist of packets */ - chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos); + chunk_rrlist_start(chunk_list, &rr_chunk, &rr_num, &rr_pos); while(!chunk_rrlist_end(rr_chunk, rr_num)) { if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos, &rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen, @@ -5042,7 +5042,7 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, if(!ixfr_start_serial(rr_chunk, rr_num, rr_pos, rr_dname, rr_type, rr_class, rr_ttl, rr_rdlen, rr_rdata, rr_nextpos, - transfer_serial, xfr->serial)) { + transfer_serial, xfr_serial)) { return 0; } } else if(transfer_serial == serial) { @@ -5063,7 +5063,8 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, * SOA 3 followed by add * SOA 3 end */ /* ended by SOA record */ - xfr->serial = transfer_serial; + /* xfr->serial is set by xfr_find_soa + * after this function. */ break; } } @@ -5131,12 +5132,13 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, /** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */ static int -xfr_apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, +apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, struct sldns_buffer* scratch_buffer) { xfr->num_ixfrs++; - return apply_ixfr(xfr, z, scratch_buffer); + return xfr_apply_ixfr(xfr->task_transfer->chunks_first, xfr->serial, z, + scratch_buffer); } /** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */ @@ -5164,7 +5166,8 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z, /* insert all RRs in to the zone */ /* insert the SOA only once, skip the last one */ /* start RR iterator over chunklist of packets */ - chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos); + chunk_rrlist_start(xfr->task_transfer->chunks_first, &rr_chunk, + &rr_num, &rr_pos); while(!chunk_rrlist_end(rr_chunk, rr_num)) { if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos, &rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen, @@ -5469,7 +5472,7 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env, } } else if(xfr->task_transfer->on_ixfr && !xfr->task_transfer->on_ixfr_is_axfr) { - if(!xfr_apply_ixfr(xfr, z, env->scratch_buffer)) { + if(!apply_ixfr(xfr, z, env->scratch_buffer)) { auth_zone_clear_data(z); lock_rw_unlock(&z->lock); verbose(VERB_ALGO, "xfr from %s: could not store IXFR" diff --git a/services/authzone.h b/services/authzone.h index b6f3845f3..b8f4c55b3 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -857,6 +857,10 @@ int chunkline_count_parens(struct sldns_buffer* buf, size_t start); /** Clear data in auth zone */ void auth_zone_clear_data(struct auth_zone* z); +/** create domain with the given name */ +struct auth_data* az_domain_create(struct auth_zone* z, uint8_t* nm, + size_t nmlen); + /** helper traverse to delete zones */ void auth_data_del(rbnode_type* n, void* arg); @@ -877,4 +881,8 @@ int xfr_apply_http(uint8_t* name, size_t namelen, const char* host, const char* file, struct auth_chunk* chunk_list, struct auth_zone* z, struct sldns_buffer* scratch_buffer); +/** Apply IXFR transfer to auth_zone */ +int xfr_apply_ixfr(struct auth_chunk* chunk_list, uint32_t xfr_serial, + struct auth_zone* z, struct sldns_buffer* scratch_buffer); + #endif /* SERVICES_AUTHZONE_H */