From: W.C.A. Wijngaards Date: Tue, 30 Jun 2026 14:53:27 +0000 (+0200) Subject: - auth-load-thread, process transfer content and swap result back in. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2fbaee225525d33a2d754ce485f298a30ac83c9f;p=thirdparty%2Funbound.git - auth-load-thread, process transfer content and swap result back in. --- diff --git a/services/authload.c b/services/authload.c index 74c7d2ad7..e248e13e1 100644 --- a/services/authload.c +++ b/services/authload.c @@ -50,6 +50,7 @@ #include "util/net_help.h" #include "util/log.h" #include "util/ub_event.h" +#include "util/data/dname.h" /** Auth load notification to string, for descriptive purposes. */ static const char* @@ -273,12 +274,127 @@ auth_load_thread_signal_worker(struct auth_load_thread* thr, int status) } } +/** Create proxy auth zone structure, that is used to hold the data + * that is processed. */ +static struct auth_zone* +auth_zone_create_proxy(uint8_t* nm, size_t nmlen, uint16_t dclass) +{ + struct auth_zone* z = (struct auth_zone*)calloc(1, sizeof(*z)); + if(!z) { + return NULL; + } + z->node.key = z; + z->dclass = dclass; + z->namelen = nmlen; + z->namelabs = dname_count_labels(nm); + z->name = memdup(nm, nmlen); + if(!z->name) { + free(z); + return NULL; + } + rbtree_init(&z->data, &auth_data_cmp); + return z; +} + +/** Delete proxy auth zone structure */ +static void +auth_zone_delete_proxy(struct auth_zone* z) +{ + if(!z) + return; + traverse_postorder(&z->data, auth_data_del, NULL); + if(z->rpz) + rpz_delete(z->rpz); + free(z->name); + free(z); +} + +/** Swap the final zone contents with the live zone */ +static void +auth_load_swap_zone(struct auth_load_thread* thr, struct auth_zone* proxyz) +{ + rbtree_type data; + struct rpz* rpz; + struct auth_zone* z; + 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 after auth load."); + return; + } + lock_rw_wrlock(&z->lock); + lock_rw_unlock(&thr->task->worker->env.auth_zones->lock); + + data = proxyz->data; + proxyz->data = z->data; + z->data = data; + + rpz = proxyz->rpz; + proxyz->rpz = z->rpz; + z->rpz = rpz; + + lock_rw_unlock(&z->lock); +} + +/** Process http transfer */ +static int +auth_load_process_http(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; + } + + xfr_http_preview(task->file, task->chunks_first); + if(!xfr_http_syntax_check(task->name, task->namelen, task->dclass, + task->host, task->file, task->chunks_first, scratch_buffer)) { + 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); + auth_zone_delete_proxy(z); + return 0; + } + sldns_buffer_free(scratch_buffer); + if(z->rpz) + rpz_finish_config(z->rpz); + + auth_load_swap_zone(thr, z); + auth_zone_delete_proxy(z); + return 1; +} + /** In the auth load thread, process the task */ static int auth_load_thread_process(struct auth_load_thread* thr) { - (void)thr; - return 0; + struct auth_load_task* task = thr->task; + + /* apply data */ + if(task->on_http) { + if(!auth_load_process_http(thr)) + return 0; + } else if(task->on_ixfr && !task->on_ixfr_is_axfr) { + } else { + } + return 1; } /** The auth load thread. The thread main function. */ diff --git a/services/authzone.c b/services/authzone.c index 02bf231d8..8e69f7bf3 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -386,7 +386,7 @@ auth_data_delete(struct auth_data* n) } /** helper traverse to delete zones */ -static void +void auth_data_del(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct auth_data* z = (struct auth_data*)n->key; @@ -4735,7 +4735,8 @@ chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos, * failure and return a string in the scratch buffer (first RR string) * on failure. */ static int -http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf) +http_zonefile_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass, + struct auth_chunk* chunk_list, struct sldns_buffer* buf) { uint8_t rr[LDNS_RR_BUF_SIZE]; size_t rr_len, dname_len = 0; @@ -4745,11 +4746,11 @@ http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf) int e; memset(&pstate, 0, sizeof(pstate)); pstate.default_ttl = 3600; - if(xfr->namelen < sizeof(pstate.origin)) { - pstate.origin_len = xfr->namelen; - memmove(pstate.origin, xfr->name, xfr->namelen); + if(namelen < sizeof(pstate.origin)) { + pstate.origin_len = namelen; + memmove(pstate.origin, name, namelen); } - chunk = xfr->task_transfer->chunks_first; + chunk = chunk_list; chunk_pos = 0; if(!chunkline_non_comment_RR(&chunk, &chunk_pos, buf, &pstate)) { return 0; @@ -4766,7 +4767,7 @@ http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf) return 0; } /* check that class is correct */ - if(sldns_wirerr_get_class(rr, rr_len, dname_len) != xfr->dclass) { + if(sldns_wirerr_get_class(rr, rr_len, dname_len) != dclass) { log_err("parse failure: first record in downloaded zonefile " "from wrong RR class"); return 0; @@ -4788,7 +4789,7 @@ chunklist_sum(struct auth_chunk* list) /** for http download, parse and add RR to zone */ static int -http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z, +http_parse_add_rr(const char* host, const char* file, struct auth_zone* z, sldns_buffer* buf, struct sldns_file_parse_state* pstate) { uint8_t rr[LDNS_RR_BUF_SIZE]; @@ -4802,9 +4803,7 @@ http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z, pstate->prev_rr_len?pstate->prev_rr:NULL, pstate->prev_rr_len); if(e != 0) { log_err("%s/%s parse failure RR[%d]: %s in '%s'", - xfr->task_transfer->master->host, - xfr->task_transfer->master->file, - LDNS_WIREPARSE_OFFSET(e), + host, file, LDNS_WIREPARSE_OFFSET(e), sldns_get_errorstr_parse(LDNS_WIREPARSE_ERROR(e)), line); return 0; @@ -5011,8 +5010,6 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, int delmode = 0; int softfail = 0; - xfr->num_ixfrs++; - /* start RR iterator over chunklist of packets */ chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos); while(!chunk_rrlist_end(rr_chunk, rr_num)) { @@ -5132,6 +5129,16 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, return 1; } +/** 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, + struct sldns_buffer* scratch_buffer) +{ + xfr->num_ixfrs++; + + return apply_ixfr(xfr, z, scratch_buffer); +} + /** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */ static int apply_axfr(struct auth_xfer* xfr, struct auth_zone* z, @@ -5198,60 +5205,65 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z, return 1; } -/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */ -static int -apply_http(struct auth_xfer* xfr, struct auth_zone* z, - struct sldns_buffer* scratch_buffer) +void +xfr_http_preview(const char* file, struct auth_chunk* chunk_list) { - /* parse data in chunks */ - /* parse RR's and read into memory. ignore $INCLUDE from the - * downloaded file*/ - struct sldns_file_parse_state pstate; - struct auth_chunk* chunk; - size_t chunk_pos; - int ret; - memset(&pstate, 0, sizeof(pstate)); - pstate.default_ttl = 3600; - if(xfr->namelen < sizeof(pstate.origin)) { - pstate.origin_len = xfr->namelen; - memmove(pstate.origin, xfr->name, xfr->namelen); - } - if(verbosity >= VERB_ALGO) verbose(VERB_ALGO, "http download %s of size %d", - xfr->task_transfer->master->file, - (int)chunklist_sum(xfr->task_transfer->chunks_first)); - if(xfr->task_transfer->chunks_first && verbosity >= VERB_ALGO) { + file, (int)chunklist_sum(chunk_list)); + if(chunk_list && verbosity >= VERB_ALGO) { char preview[1024]; - if(xfr->task_transfer->chunks_first->len+1 > sizeof(preview)) { - memmove(preview, xfr->task_transfer->chunks_first->data, - sizeof(preview)-1); + if(chunk_list->len+1 > sizeof(preview)) { + memmove(preview, chunk_list->data, sizeof(preview)-1); preview[sizeof(preview)-1]=0; } else { - memmove(preview, xfr->task_transfer->chunks_first->data, - xfr->task_transfer->chunks_first->len); - preview[xfr->task_transfer->chunks_first->len]=0; + memmove(preview, chunk_list->data, chunk_list->len); + preview[chunk_list->len]=0; } log_info("auth zone http downloaded content preview: %s", preview); } +} +int +xfr_http_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass, + const char* host, const char* file, struct auth_chunk* chunk_list, + struct sldns_buffer* scratch_buffer) +{ /* perhaps a little syntax check before we try to apply the data? */ - if(!http_zonefile_syntax_check(xfr, scratch_buffer)) { + if(!http_zonefile_syntax_check(name, namelen, dclass, chunk_list, + scratch_buffer)) { log_err("http download %s/%s does not contain a zonefile, " - "but got '%s'", xfr->task_transfer->master->host, - xfr->task_transfer->master->file, + "but got '%s'", host, file, sldns_buffer_begin(scratch_buffer)); return 0; } + return 1; +} + +/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */ +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) +{ + /* parse data in chunks */ + /* parse RR's and read into memory. ignore $INCLUDE from the + * downloaded file*/ + struct sldns_file_parse_state pstate; + struct auth_chunk* chunk; + size_t chunk_pos; + int ret; + memset(&pstate, 0, sizeof(pstate)); + pstate.default_ttl = 3600; + if(namelen < sizeof(pstate.origin)) { + pstate.origin_len = namelen; + memmove(pstate.origin, name, namelen); + } auth_zone_clear_data(z); - xfr->have_zone = 0; - xfr->serial = 0; - xfr->soa_zone_acquired = 0; - xfr->num_ixfrs = 0; - chunk = xfr->task_transfer->chunks_first; + chunk = chunk_list; chunk_pos = 0; pstate.lineno = 0; while(chunkline_get_line_collated(&chunk, &chunk_pos, scratch_buffer)) { @@ -5265,8 +5277,7 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z, if((ret=http_parse_origin(scratch_buffer, &pstate))!=0) { if(ret == 2) { verbose(VERB_ALGO, "error parsing ORIGIN on line [%s:%d] %s", - xfr->task_transfer->master->file, - pstate.lineno, + file, pstate.lineno, sldns_buffer_begin(scratch_buffer)); return 0; } @@ -5275,17 +5286,15 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z, if((ret=http_parse_ttl(scratch_buffer, &pstate))!=0) { if(ret == 2) { verbose(VERB_ALGO, "error parsing TTL on line [%s:%d] %s", - xfr->task_transfer->master->file, - pstate.lineno, + file, pstate.lineno, sldns_buffer_begin(scratch_buffer)); return 0; } continue; /* $TTL has been handled */ } - if(!http_parse_add_rr(xfr, z, scratch_buffer, &pstate)) { + if(!http_parse_add_rr(host, file, z, scratch_buffer, &pstate)) { verbose(VERB_ALGO, "error parsing line [%s:%d] %s", - xfr->task_transfer->master->file, - pstate.lineno, + file, pstate.lineno, sldns_buffer_begin(scratch_buffer)); return 0; } @@ -5293,6 +5302,28 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z, return 1; } +/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */ +static int +apply_http(struct auth_xfer* xfr, struct auth_zone* z, + struct sldns_buffer* scratch_buffer) +{ + xfr_http_preview(xfr->task_transfer->master->file, + xfr->task_transfer->chunks_first); + if(!xfr_http_syntax_check(xfr->name, xfr->namelen, xfr->dclass, + xfr->task_transfer->master->host, + xfr->task_transfer->master->file, + xfr->task_transfer->chunks_first, scratch_buffer)) + return 0; + xfr->have_zone = 0; + xfr->serial = 0; + xfr->soa_zone_acquired = 0; + xfr->num_ixfrs = 0; + return xfr_apply_http(xfr->name, xfr->namelen, + xfr->task_transfer->master->host, + xfr->task_transfer->master->file, + xfr->task_transfer->chunks_first, z, scratch_buffer); +} + /** write http chunks to zonefile to create downloaded file */ static int auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname) @@ -5438,7 +5469,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(!apply_ixfr(xfr, z, env->scratch_buffer)) { + if(!xfr_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" @@ -6357,6 +6388,44 @@ process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env) void xfr_process_load_end_transfer(struct auth_xfer* xfr, struct module_env* env, uint8_t status, int ixfr_fail) { + if(status) { + struct auth_zone* z = NULL; + lock_basic_unlock(&xfr->lock); + if(!xfr_process_reacquire_locks(xfr, env, &z)) { + /* the zone is gone, ignore xfr results */ + lock_basic_unlock(&xfr->lock); + return; + } + /* holding xfr and z locks */ + + if(xfr->task_transfer->master->http) { + xfr->num_ixfrs = 0; + } else if(xfr->task_transfer->on_ixfr && + !xfr->task_transfer->on_ixfr_is_axfr) { + xfr->num_ixfrs++; + } else { + /* AXFR */ + xfr->num_ixfrs = 0; + } + + xfr->zone_expired = 0; + z->zone_expired = 0; + + xfr->have_zone = 0; + xfr->serial = 0; + if(!xfr_find_soa(z, xfr)) { + verbose(VERB_ALGO, "xfr from %s: no SOA in zone after update" + " (or malformed RR)", xfr->task_transfer->master->host); + status = 0; + } + if(status) { + z->soa_zone_acquired = *env->now; + xfr->soa_zone_acquired = *env->now; + xfr->is_rpz = (z->rpz!=NULL); + } + lock_rw_unlock(&z->lock); + } + if(status) { /* it worked! */ xfr_process_transfer_success(xfr, env); diff --git a/services/authzone.h b/services/authzone.h index a49d9084c..b6f3845f3 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -857,8 +857,24 @@ 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); +/** helper traverse to delete zones */ +void auth_data_del(rbnode_type* n, void* arg); + /** Handle the end of an auth load task. */ void xfr_process_load_end_transfer(struct auth_xfer* xfr, struct module_env* env, uint8_t status, int ixfr_fail); +/** Log preview of http transfer */ +void xfr_http_preview(const char* file, struct auth_chunk* chunk_list); + +/** Check syntax of first part of the http download */ +int xfr_http_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass, + const char* host, const char* file, struct auth_chunk* chunk_list, + struct sldns_buffer* scratch_buffer); + +/** Apply http transfer to auth_zone */ +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); + #endif /* SERVICES_AUTHZONE_H */