]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- auth-load-thread, process transfer content and swap result back in.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Tue, 30 Jun 2026 14:53:27 +0000 (16:53 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Tue, 30 Jun 2026 14:53:27 +0000 (16:53 +0200)
services/authload.c
services/authzone.c
services/authzone.h

index 74c7d2ad7d72562e3d8b7b531a620914ff9fd54b..e248e13e1246c43da85d1cfd46caa81497093b69 100644 (file)
@@ -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. */
index 02bf231d80d8161c956ef62b67dd7ada93e72189..8e69f7bf384978255f9a18995ab18734dea803c2 100644 (file)
@@ -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);
index a49d9084cc0d168f75aca5177958556fc0f6e18c..b6f3845f3a5fb9c21a24c1c7f4449ed193e8f304 100644 (file)
@@ -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 */