]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- auth-load-thread, process IXFR, by making a copy, adjust changes, swap in.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 1 Jul 2026 11:05:19 +0000 (13:05 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 1 Jul 2026 11:05:19 +0000 (13:05 +0200)
services/authload.c
services/authzone.c
services/authzone.h

index e248e13e1246c43da85d1cfd46caa81497093b69..610a47bbabe778e15ad748e8c12e0d16be5c8ef8 100644 (file)
@@ -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;
index 8e69f7bf384978255f9a18995ab18734dea803c2..9e601484a68f9ba29af139e5890e0e970e06fcfd 100644 (file)
@@ -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"
index b6f3845f3a5fb9c21a24c1c7f4449ed193e8f304..b8f4c55b3f55d8a259069d6a8d56ec6bf9286ffe 100644 (file)
@@ -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 */