1 /*------------------------------------------------------------------------------
2  *
3  * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4  * The YADIFA TM software product is provided under the BSD 3-clause license:
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  *        * Redistributions of source code must retain the above copyright
11  *          notice, this list of conditions and the following disclaimer.
12  *        * Redistributions in binary form must reproduce the above copyright
13  *          notice, this list of conditions and the following disclaimer in the
14  *          documentation and/or other materials provided with the distribution.
15  *        * Neither the name of EURid nor the names of its contributors may be
16  *          used to endorse or promote products derived from this software
17  *          without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  *------------------------------------------------------------------------------
32  *
33  */
34 
35 /** @defgroup
36  *  @ingroup dnsdb
37  *  @brief
38  *
39  *
40  *
41  * @{
42  */
43 /*------------------------------------------------------------------------------
44  *
45  * USE INCLUDES */
46 #include "dnsdb/dnsdb-config.h"
47 #include <stdio.h>
48 #include <stdlib.h>
49 
50 #include <dnscore/dnscore.h>
51 #include <dnscore/logger.h>
52 #include <dnscore/timems.h>
53 #include <dnscore/rfc.h>
54 
55 #include "dnsdb/zdb_types.h"
56 #include "dnsdb/zdb_icmtl.h"
57 
58 #include "dnsdb/zdb-packed-ttlrdata.h"
59 
60 #include "dnsdb/dnssec.h"
61 #include "dnsdb/zdb_record.h"
62 #include "dnsdb/dnssec-keystore.h"
63 #include <dnscore/dnskey-signature.h>
64 #include <dnscore/base32hex.h>
65 
66 #include "dnsdb/zdb_zone_label_iterator.h"
67 
68 #include "dnsdb/zdb-zone-maintenance.h"
69 
70 extern logger_handle* g_database_logger;
71 #define MODULE_MSG_HANDLE g_database_logger
72 
73 #define ZDB_ZONE_MAINTENANCE_DETAILED_LOG           0       /// @note 20180615 edf -- heavy, use with care
74 #define ZDB_ZONE_MAINTENANCE_IGNORE_TIME_QUOTA      0
75 #define DEBUG_SIGNATURE_REFRESH 0
76 
77 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
78 #pragma message("WARNING: ZDB_ZONE_MAINTENANCE_DETAILED_LOG is not set to 0")
79 #endif
80 
81 #if ZDB_ZONE_MAINTENANCE_IGNORE_TIME_QUOTA
82 #pragma message("WARNING: ZDB_ZONE_MAINTENANCE_IGNORE_TIME_QUOTA is not set to 0")
83 #endif
84 
85 #define ZDB_ZONE_MAINTENANCE_SAME_PASS_CLOSE        1
86 #define ZDB_ZONE_MAINTENANCE_RRSIG_COUNT_THRESHOLD  256     // after that many signatures, stop processing labels
87 #define ZDB_MAINTENANCE_BATCH_TIME_US_MAX 20000             // 10ms
88 
89 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
90 #pragma message("WARNING: ZDB_ZONE_MAINTENANCE_DETAILED_LOG IS NOT SET TO 0.  SET IT BACK TO 0 IT IF YOU DON'T NEED IT!")
91 #pragma message("WARNING: ZDB_ZONE_MAINTENANCE_DETAILED_LOG IS NOT SET TO 0.  SET IT BACK TO 0 IT IF YOU DON'T NEED IT!")
92 #pragma message("WARNING: ZDB_ZONE_MAINTENANCE_DETAILED_LOG IS NOT SET TO 0.  SET IT BACK TO 0 IT IF YOU DON'T NEED IT!")
93 #endif
94 
95 static bool
zdb_zone_maintenance_validate_sign_chain_store(zdb_zone_maintenance_ctx * mctx,zone_diff * diff,zdb_zone * zone,ptr_vector * rrset_to_sign,ptr_vector * remove,ptr_vector * add)96 zdb_zone_maintenance_validate_sign_chain_store(zdb_zone_maintenance_ctx *mctx, zone_diff *diff, zdb_zone *zone, ptr_vector *rrset_to_sign, ptr_vector *remove, ptr_vector *add)
97 {
98     ya_result ret;
99 
100     log_debug("maintenance: validate-sign-chain-store: %{dnsname}", zone->origin);
101 
102 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
103     for(int i = 0; i <= ptr_vector_last_index(remove); ++i)
104     {
105         zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(remove, i);
106         rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
107 
108         log_debug("before-validate: %{dnsname}: - %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
109     }
110 
111     for(int i = 0; i <= ptr_vector_last_index(add); ++i)
112     {
113         zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add, i);
114         rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
115 
116         log_debug("before-validate: %{dnsname}: + %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
117     }
118 #endif
119 
120     if(ISOK(ret = zone_diff_validate(diff)))
121     {
122 #if DEBUG
123         log_debug("maintenance: %{dnsname}: diff validated", diff->origin);
124 #endif
125         // store changes in vectors and get the RR sets to sign
126 
127 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
128         for(int i = 0; i <= ptr_vector_last_index(remove); ++i)
129         {
130             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(remove, i);
131             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
132 
133             log_debug("before-get-changes: %{dnsname}: - %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
134         }
135 
136         for(int i = 0; i <= ptr_vector_last_index(add); ++i)
137         {
138             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add, i);
139             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
140 
141             log_debug("before-get-changes: %{dnsname}: + %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
142         }
143 #endif
144         ptr_vector ksks = PTR_VECTOR_EMPTY;
145         ptr_vector zsks = PTR_VECTOR_EMPTY;
146 
147         s32 mandatory_changes = zone_diff_get_changes(diff, rrset_to_sign, &ksks, &zsks, remove, add);
148 
149         // sign the records, store the changes in vectors
150 
151 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
152         for(int i = 0; i <= ptr_vector_last_index(remove); ++i)
153         {
154             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(remove, i);
155             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
156 
157             log_debug("before-diff-sign: %{dnsname}: - %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
158         }
159 
160         for(int i = 0; i <= ptr_vector_last_index(add); ++i)
161         {
162             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add, i);
163             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
164 
165             log_debug("before-diff-sign: %{dnsname}: + %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
166         }
167 #endif
168 
169         bool relevant_update = (mandatory_changes > 0);
170 
171         if(relevant_update)
172         {
173             /*
174              * Signs the RRSETs using the ksk & zsk, applies the changes in remove and add vectors
175              */
176 
177             dnssec_chain_store_diff(&mctx->nsec_chain_updater, diff, &zsks, remove, add);
178 
179             zone_diff_sign(diff, zone, rrset_to_sign, &ksks, &zsks, remove, add);
180 
181 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
182             for(int i = 0; i <= ptr_vector_last_index(remove); ++i)
183             {
184                 zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(remove, i);
185                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
186 
187                 log_debug("before-nsec3-chain-store-diff: %{dnsname}: - [%02x] %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->state, rr->fqdn, rr->ttl, &rd);
188             }
189 
190             for(int i = 0; i <= ptr_vector_last_index(add); ++i)
191             {
192                 zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add, i);
193                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
194 
195                 log_debug("before-nsec3-chain-store-diff: %{dnsname}: + [%02x] %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->state, rr->fqdn, rr->ttl, &rd);
196             }
197 #endif
198             // zone_diff_get_chain_changes(diff, &dc);
199 
200             dnssec_chain_store_diff(&mctx->nsec3_chains_updater, diff, &zsks, remove, add);
201 
202 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
203             for(int i = 0; i <= ptr_vector_last_index(remove); ++i)
204             {
205                 zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(remove, i);
206                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
207 
208                 log_debug("when-all-is-said-and-done: %{dnsname}: - [%02x] %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->state, rr->fqdn, rr->ttl, &rd);
209             }
210 
211             for(int i = 0; i <= ptr_vector_last_index(add); ++i)
212             {
213                 zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add, i);
214                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
215 
216                 log_debug("when-all-is-said-and-done: %{dnsname}: + [%02x] %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->state, rr->fqdn, rr->ttl, &rd);
217             }
218 #endif
219         }
220 
221         dnssec_keystore_release_keys_from_vector(&zsks);
222         dnssec_keystore_release_keys_from_vector(&ksks);
223 
224         ptr_vector_destroy(&zsks);
225         ptr_vector_destroy(&ksks);
226 
227         return relevant_update;
228     }
229     else
230     {
231         log_err("maintenance: %{dnsname}: could not validate the diff", diff->origin);
232 
233         return FALSE;
234     }
235 }
236 
237 static void
zdb_zone_maintenance_ctx_init(zdb_zone_maintenance_ctx * mctx,zdb_zone * zone)238 zdb_zone_maintenance_ctx_init(zdb_zone_maintenance_ctx *mctx, zdb_zone *zone)
239 {
240     log_debug("maintenance: %{dnsname}: context init", zone->origin);
241     ZEROMEMORY(mctx, sizeof(zdb_zone_maintenance_ctx));
242     mctx->zone = zone;
243 }
244 
245 static void
zdb_zone_maintenance_ctx_finalize(zdb_zone_maintenance_ctx * mctx)246 zdb_zone_maintenance_ctx_finalize(zdb_zone_maintenance_ctx *mctx)
247 {
248     log_debug("maintenance: %{dnsname}: context finalize", mctx->zone->origin);
249 
250     log_debug("maintenance: %{dnsname}: context finalize: clearing KSK", mctx->zone->origin);
251 
252     dnssec_keystore_release_keys_from_vector(&mctx->ksks);
253 
254     log_debug("maintenance: %{dnsname}: context finalize: clearing ZSK", mctx->zone->origin);
255 
256     dnssec_keystore_release_keys_from_vector(&mctx->zsks);
257 
258     ptr_vector_destroy(&mctx->ksks);
259     ptr_vector_destroy(&mctx->zsks);
260 
261     log_debug("maintenance: %{dnsname}: context finalize: finalising NSEC chain", mctx->zone->origin);
262 
263     dnssec_chain_finalize(&mctx->nsec_chain_updater);
264 
265     log_debug("maintenance: %{dnsname}: context finalize: finalising NSEC3 chain", mctx->zone->origin);
266 
267     dnssec_chain_finalize(&mctx->nsec3_chains_updater);
268 
269     log_debug("maintenance: %{dnsname}: context finalize: releasing active keys", mctx->zone->origin);
270 
271     zdb_zone_release_active_keys(mctx->keys);
272 
273     mctx->keys = NULL;
274 
275     log_debug("maintenance: %{dnsname}: context finalize done", mctx->zone->origin);
276 }
277 
278 /**
279  * Updates the signatures of a zone incrementally.
280  * Each call goes a bit further.
281  *
282  * @param zone
283  * @param signature_count_loose_limit
284  * @param present_signatures_are_verified
285  * @return
286  */
287 
288 static ya_result
zdb_zone_maintenance_from(zdb_zone * zone,u8 * from_fqdn,size_t from_fqdn_size,s64 maxus,s32 rrsigcountmax)289 zdb_zone_maintenance_from(zdb_zone* zone, u8 *from_fqdn, size_t from_fqdn_size, s64 maxus, s32 rrsigcountmax)
290 {
291     yassert(((from_fqdn != NULL) && (from_fqdn_size > 0)) || ((from_fqdn == NULL) && (from_fqdn_size == 0)));
292     //bool from_fqdn_is_binary_digest = FALSE;
293 
294     if(!zdb_zone_is_maintained(zone))
295     {
296         log_debug("maintenance: %{dnsname}: not maintained", zone->origin);
297         return ZDB_ERROR_ZONE_NOT_MAINTAINED;
298     }
299 
300     u8 current_owner;
301     u8 current_reserved_owner;
302 
303     while(!zdb_zone_try_double_lock_ex(zone, ZDB_ZONE_MUTEX_SIMPLEREADER, ZDB_ZONE_MUTEX_RRSIG_UPDATER, &current_owner, &current_reserved_owner))
304     {
305         log_debug("maintenance: %{dnsname}: cannot double-lock the zone for maintenance (%02x, %04x, %02x, %02x)", zone->origin, zone->_flags, zone->_status, current_owner, current_reserved_owner);
306 
307         if((current_owner == ZDB_ZONE_MUTEX_RRSIG_UPDATER) || (current_reserved_owner == ZDB_ZONE_MUTEX_RRSIG_UPDATER))
308         {
309             // the zone is already being signed
310             return ZDB_ERROR_ZONE_IS_ALREADY_BEING_SIGNED;
311         }
312         else
313         {
314             // wait for the condition
315             mutex_lock(&zone->lock_mutex);
316             cond_timedwait(&zone->lock_cond, &zone->lock_mutex, 1000);
317             mutex_unlock(&zone->lock_mutex);
318         }
319     }
320 
321     if((from_fqdn != NULL) && (from_fqdn[0] != 0))
322     {
323         log_debug("maintenance: %{dnsname}: starting from %{dnsname}", zone->origin, from_fqdn);
324     }
325     else
326     {
327         log_debug("maintenance: %{dnsname}: starting from the apex", zone->origin);
328         zdb_zone_clear_maintenance_queued(zone);
329     }
330 
331     s64 start_time = timeus();
332 
333     int maintenance_rrsig_count = 0;
334     int maintenance_nsec_count = 0;
335     int maintenance_nsec3_count = 0;
336     int maintenance_generate_nsec3_rrsig_count = 0;
337     int maintenance_generate_nsec3param_rrsig_count = 0;
338     bool nsecchainstate_changed = FALSE;
339 
340     zdb_zone_maintenance_ctx mctx;
341     zdb_zone_maintenance_ctx_init(&mctx, zone);
342 
343     u8 maintain_mode = zone_get_maintain_mode(zone);
344 
345     zone_diff diff;
346     zone_diff_init(&diff, zone, TRUE);
347 
348     switch(maintain_mode)
349     {
350         case ZDB_ZONE_MAINTAIN_NSEC3:
351         case ZDB_ZONE_MAINTAIN_NSEC3_OPTOUT:
352         {
353             diff.maintain_nsec3 = TRUE;
354             break;
355         }
356         case ZDB_ZONE_MAINTAIN_NSEC:
357         {
358             diff.maintain_nsec = TRUE;
359             break;
360         }
361         default:
362         {
363             break;
364         }
365     }
366 
367     dnssec_chain_init(&mctx.nsec_chain_updater, dynupdate_nsec_chain_get_vtbl(), &diff);
368     if((maintain_mode & ZDB_ZONE_MAINTAIN_NSEC3_OPTOUT) != ZDB_ZONE_MAINTAIN_NSEC3_OPTOUT)
369     {
370         dnssec_chain_init(&mctx.nsec3_chains_updater, dynupdate_nsec3_chain_get_vtbl(), &diff);
371     }
372     else
373     {
374         dnssec_chain_init(&mctx.nsec3_chains_updater, dynupdate_nsec3_optout_chain_get_vtbl(), &diff);
375     }
376 
377     // load all the active keys from the keyring of the zone
378     // compute a mask for all the keys that are private (usable)
379 
380     log_debug("maintenance: %{dnsname}: fetching keys", zone->origin);
381 
382     ptr_vector_init(&mctx.ksks);
383     ptr_vector_init(&mctx.zsks);
384     zone_diff_store_diff_dnskey_get_keys(&diff, &mctx.ksks, &mctx.zsks);
385 
386     u64 ksk_mask = 0;
387     u64 zsk_mask = 0;
388 
389     mctx.now = time(NULL);
390 
391     for(int i = 0; i <= ptr_vector_last_index(&mctx.ksks); ++i)
392     {
393         const dnssec_key *key = (const dnssec_key*)ptr_vector_get(&mctx.ksks, i);
394 
395         if(dnskey_is_private(key) && dnskey_is_activated(key, mctx.now))
396         {
397             ksk_mask |= 1ULL << i;
398             log_debug("maintenance: DNSKEY: %{dnsname}+%03d+%05d/%d is a private KSK", dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key), ntohs(dnskey_get_flags(key)));
399         }
400         else
401         {
402             log_debug("maintenance: DNSKEY: %{dnsname}+%03d+%05d/%d is not a private KSK", dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key), ntohs(dnskey_get_flags(key)));
403         }
404     }
405 
406     for(int i = 0; i <= ptr_vector_last_index(&mctx.zsks); ++i)
407     {
408         const dnssec_key *key = (const dnssec_key*)ptr_vector_get(&mctx.zsks, i);
409 
410         if(dnskey_is_private(key) && dnskey_is_activated(key, mctx.now))
411         {
412             zsk_mask |= 1ULL << i;
413             log_debug("maintenance: DNSKEY: %{dnsname}+%03d+%05d/%d is a private ZSK", dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key), ntohs(dnskey_get_flags(key)));
414         }
415         else
416         {
417             log_debug("maintenance: DNSKEY: %{dnsname}+%03d+%05d/%d is not a private ZSK", dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key), ntohs(dnskey_get_flags(key)));
418         }
419     }
420 
421     mctx.ksk_mask = ksk_mask;
422     mctx.zsk_mask = zsk_mask;
423     mctx.ksk_count = ptr_vector_size(&mctx.ksks);
424     mctx.zsk_count = ptr_vector_size(&mctx.zsks);
425 
426     log_debug("maintenance: DNSKEY: %{dnsname}: ksk mask=%p, count=%llx, zsk mask=%p, count = %i",
427             mctx.zone->origin,
428             mctx.ksk_mask, mctx.ksk_count, mctx.zsk_mask, mctx.zsk_count);
429 
430     if(mctx.zsk_mask == 0)
431     {
432         zdb_zone_double_unlock(zone, ZDB_ZONE_MUTEX_SIMPLEREADER, ZDB_ZONE_MUTEX_RRSIG_UPDATER);
433 
434         zone_diff_finalize(&diff);
435         zdb_zone_maintenance_ctx_finalize(&mctx);
436 
437         return ZDB_ERROR_ZONE_NO_ZSK_PRIVATE_KEY_FILE;
438     }
439 
440     ptr_vector remove = PTR_VECTOR_EMPTY;
441     ptr_vector add = PTR_VECTOR_EMPTY;
442 
443     int loop_iterations = 0; // prevents loops to break in first iteration when the system is too slow (forever retrying)
444 
445     bool diff_has_changes = FALSE;
446     bool actions_happened = FALSE;
447     bool last_label_of_zone_reached = FALSE;
448     //bool soa_updated = FALSE;
449 
450     diff.has_active_ksk = ksk_mask != 0;
451     diff.has_active_zsk = zsk_mask != 0;
452 
453     // the maximum number of labels we are allowed to process in one pass
454 
455     int labels_at_once = mctx.zone->progressive_signature_update.labels_at_once;
456 
457     ya_result ret;
458 
459     log_debug("maintenance: %{dnsname}: has %i KSKs and %i ZSKs"/* (%i and %i)"*/, zone->origin, mctx.ksk_count, mctx.zsk_count/*, mctx_ksk_count, mctx_zsk_count*/);
460 
461     // no rrset to sign, for now
462 
463     ptr_vector rrset_to_sign = PTR_VECTOR_EMPTY;
464 
465     // get a copy of the APEX
466 
467     zone_diff_fqdn *apex = zone_diff_add_static_fqdn(&diff, diff.origin, zone->apex);
468 
469     zdb_zone_label_iterator iter;
470 
471     // initialises the "start-from" iterator
472 
473     if(zone->progressive_signature_update.chain_index < 0)
474     {
475         if((from_fqdn == NULL) || (*from_fqdn == 0))
476         {
477             zdb_zone_label_iterator_init(&iter, zone);
478             // also reset the earliest resignature
479             mctx.zone->progressive_signature_update.earliest_signature_expiration = MAX_S32;
480             mctx.zone->progressive_signature_update.labels_at_once = ZDB_ZONE_MAINTENANCE_LABELS_AT_ONCE_DEFAULT;
481         }
482         else
483         {
484             zdb_zone_label_iterator_init_from(&iter, zone, from_fqdn);
485         }
486     }
487 
488     // get the nsec status
489 
490     nsec_zone_get_status(zone, &mctx.nsec_chain_status);
491     dnssec_chain_add_chain(&mctx.nsec_chain_updater, (dnssec_chain_head_t)zone->nsec.nsec, (mctx.nsec_chain_status & NSEC_ZONE_REMOVING) != 0);
492 
493     // get all nsec3 statuses
494 
495     mctx.nsec3_chain_count = MIN(nsec3_zone_get_chain_count(zone), ZDB_ZONE_MAINTENANCE_NSEC3CHAIN_MAX);
496     nsec3_zone_get_chains(zone, mctx.nsec3_chain, mctx.nsec3_chain_count);
497     for(u8 nsec3_chain_index = 0; nsec3_chain_index < mctx.nsec3_chain_count; ++nsec3_chain_index)
498     {
499         const u8 *nsec3param_rdata = mctx.nsec3_chain[nsec3_chain_index]->rdata;
500         nsec3_zone_get_status_from_rdata(zone, nsec3param_rdata, NSEC3_ZONE_RDATA_SIZE(mctx.nsec3_chain[nsec3_chain_index]), &mctx.nsec3_chain_status[nsec3_chain_index]);
501         dnssec_chain_add_chain(&mctx.nsec3_chains_updater, (dnssec_chain_head_t)mctx.nsec3_chain[nsec3_chain_index], (mctx.nsec3_chain_status[nsec3_chain_index] & NSEC3_ZONE_REMOVING) != 0);
502     }
503 
504     /**************************************************************************************************************
505      *
506      * "Normal" database (chain_index < 0)
507      *
508      **************************************************************************************************************/
509 
510     ptr_vector rrset_vector = PTR_VECTOR_EMPTY;
511     ptr_vector candidates = PTR_VECTOR_EMPTY;
512     ptr_vector chain_candidates = PTR_VECTOR_EMPTY;
513 
514     int labels_to_process_count = labels_at_once;
515 
516     u8 from_digest[256];
517 
518     if(zone->progressive_signature_update.chain_index < 0)
519     {
520         for(;;)
521         {
522             if(zdb_zone_label_iterator_hasnext(&iter))
523             {
524                 memcpy(&mctx.fqdn_stack.labels[0], &iter.dnslabels[0], (iter.top + 1) * sizeof(u8*));
525 
526                 mctx.fqdn_stack.size = iter.top;    // MUST be called before zdb_zone_label_iterator_next(&iter)
527 
528                 zdb_rr_label *rr_label = zdb_zone_label_iterator_next(&iter);
529 
530                 // if we are under delegation : don't stop (resuming from under a delegation would require to fetch more data
531 
532                 bool under_delegation = ZDB_LABEL_UNDERDELEGATION(rr_label);
533 
534                 dnsname_stack_to_dnsname(&mctx.fqdn_stack, mctx.fqdn);
535 
536                 // if too many iterations : break
537 
538                 s64 now = timeus();
539 
540                 --labels_to_process_count;
541 
542                 if(!under_delegation)
543                 {
544                     if(labels_to_process_count < 0)
545                     {
546 #if DEBUG
547                         double dt = now - start_time;
548                         dt /= ONE_SECOND_US_F;
549                         log_debug("maintenance: %{dnsname}: time elapsed (%fs): fqdn quota spent: next one will be %{dnsnamestack}",
550                                   zone->origin, dt, &mctx.fqdn_stack);
551 #endif
552                         break;
553                     }
554 
555 #if !ZDB_ZONE_MAINTENANCE_IGNORE_TIME_QUOTA
556                     if((loop_iterations > 0) && (now - start_time >= maxus))
557                     {
558                         // too much time taken already
559 #if DEBUG
560                         double dt = now - start_time;
561                         dt /= ONE_SECOND_US_F;
562                         log_debug("maintenance: %{dnsname}: time elapsed (%fs): time spent: next one will be %{dnsnamestack}",
563                                   zone->origin, dt, &mctx.fqdn_stack);
564 #endif
565                         break;
566                     }
567 #endif
568                 }
569 
570                 ++loop_iterations;
571 #if DEBUG
572                 log_debug2("maintenance: %{dnsname}: at %{dnsnamestack}", zone->origin, &mctx.fqdn_stack);
573 #endif
574                 mctx.label = rr_label;
575 
576                 zone_diff_fqdn *diff_fqdn = zone_diff_add_static_fqdn(&diff, mctx.fqdn, rr_label);
577 
578                 maintenance_rrsig_count += zdb_zone_maintenance_rrsig(&mctx, diff_fqdn, &rrset_to_sign);
579                 maintenance_nsec_count += zdb_zone_maintenance_nsec(&mctx, diff_fqdn, &rrset_to_sign);
580                 maintenance_nsec3_count += zdb_zone_maintenance_nsec3(&mctx, diff_fqdn);                // only affects the chain structure, hence the lack of "rrset_to_sign"
581 
582                 if(maintenance_rrsig_count + maintenance_nsec_count + maintenance_nsec3_count > 0)
583                 {
584                     // ++labels_to_process_count;
585                     actions_happened = TRUE;
586 
587                     // if too many signatures are already to be generated, don't process more labels
588 
589                     if(maintenance_rrsig_count > rrsigcountmax)
590                     {
591                         labels_at_once = loop_iterations;   // update the amount of labels projected in this update
592                         labels_to_process_count = 0;        // prevents doing more labels
593                     }
594                 }
595             }
596             else
597             {
598 #if DEBUG
599                 log_debug("maintenance: %{dnsname}: reached the last label", zone->origin);
600 #endif
601                 if(mctx.nsec3_chain_count > 0)
602                 {
603                     // if the chain is not empty in the zone
604 
605                     if(mctx.zone->nsec.nsec3->items != NULL)
606                     {
607                         log_debug("maintenance: %{dnsname}: will process NSEC3 chain(s)", zone->origin);
608                         zone->progressive_signature_update.chain_index = 0;
609                         nsec3_node *first = nsec3_get_first(&mctx.zone->nsec.nsec3->items);
610 
611                         memcpy(from_digest, first->digest, first->digest[0] + 1);
612                         from_digest[first->digest[0] + 1] = 0;
613                         //from_fqdn_is_binary_digest = TRUE;
614 
615                         log_debug("maintenance: %{dnsname}: starting work on NSEC3 chain %i", zone->origin, zone->progressive_signature_update.chain_index);
616                     }
617                     else
618                     {
619 #if DEBUG
620                         log_debug("maintenance: %{dnsname}: no chain to process after the last label", zone->origin);
621 #endif
622                         last_label_of_zone_reached = TRUE;
623                     }
624                 }
625                 else
626                 {
627 #if DEBUG
628                     log_debug("maintenance: %{dnsname}: no chain in the zone to be processed after the last label", zone->origin);
629 #endif
630                     last_label_of_zone_reached = TRUE;
631                 }
632 
633                 break;
634             }
635         } // while has labels ..
636     }
637     else
638     {
639         // if we are working in the NSEC3 chains, the index is >= 0
640         // then we also want to decode the base32hex encoding to work on the binary digest
641 
642         from_digest[0] = base32hex_decode((const char*)&from_fqdn[1], from_fqdn[0], &from_digest[1]);
643         //from_fqdn_is_binary_digest = TRUE;
644 
645         log_debug("maintenance: %{dnsname}: starting work on NSEC3 chain %i", zone->origin, zone->progressive_signature_update.chain_index);
646     }
647 
648     // while no more normal labels are to be processed but NSEC3 chains are remaining
649 
650     /**************************************************************************************************************
651      *
652      * "NSEC3" database
653      *
654      **************************************************************************************************************/
655 
656     while(zone->progressive_signature_update.chain_index >= 0)
657     {
658         loop_iterations = 0;
659 
660         //yassert(from_fqdn_is_binary_digest);
661 
662         nsec3_zone *n3e = zdb_zone_get_nsec3chain(mctx.zone, zone->progressive_signature_update.chain_index);
663 
664         nsec3_iterator iter;
665         nsec3_iterator_init_from(&n3e->items, &iter, from_digest); // binary form
666 
667         for(;;)
668         {
669             if(nsec3_iterator_hasnext(&iter))
670             {
671                 nsec3_zone_item *item = nsec3_iterator_next_node(&iter);
672                 //u8 digest_len = NSEC3_NODE_DIGEST_SIZE(item);
673 
674                 // check signature
675                 //   mark signature for update
676                 //     actions_happened = TRUE;
677 
678                 s64 now = timeus();
679 
680                 if(item->rc <= 0)
681                 {
682                     double dt = now - start_time;
683                     dt /= ONE_SECOND_US_F;
684                     log_debug("maintenance: %{dnsname}: time elapsed (%fs): %{digest32h}.%{dnsname} has no owner (rc=%i, sc=%i)",
685                               zone->origin, dt, item->digest, zone->origin, item->rc, item->sc);
686                 }
687 
688                 --labels_to_process_count;
689 
690                 if(labels_to_process_count < 0)
691                 {
692 #if DEBUG
693                     double dt = now - start_time;
694                     dt /= ONE_SECOND_US_F;
695                     log_debug("maintenance: %{dnsname}: time elapsed (%fs): fqdn quota spent: next one will be %{digest32h}.%{dnsname}",
696                               zone->origin, dt, item->digest, zone->origin);
697 #endif
698                     u8 digest_len = NSEC3_NODE_DIGEST_SIZE(item);
699                     mctx.fqdn[0] = base32hex_encode(NSEC3_NODE_DIGEST_PTR(item), digest_len, (char*)&mctx.fqdn[1]);
700                     dnsname_copy(&mctx.fqdn[mctx.fqdn[0] + 1], zone->origin);
701 
702                     /************************************
703                      * GOTO, breaking two loops at once.*
704                      ************************************/
705 
706                     goto zdb_zone_maintenance_from_chain_iteration_loop_break;
707                 }
708 
709 #if !ZDB_ZONE_MAINTENANCE_IGNORE_TIME_QUOTA
710                 if((loop_iterations > 0) && (now - start_time >= maxus))
711                 {
712                     // too much time taken already
713 #if DEBUG
714                     double dt = now - start_time;
715                     dt /= ONE_SECOND_US_F;
716                     log_debug("maintenance: %{dnsname}: time elapsed (%fs): time spent: next one will be %{digest32h}.%{dnsname}",
717                               zone->origin, dt, item->digest, zone->origin);
718 #endif
719                     u8 digest_len = NSEC3_NODE_DIGEST_SIZE(item);
720                     mctx.fqdn[0] = base32hex_encode(NSEC3_NODE_DIGEST_PTR(item), digest_len, (char*)&mctx.fqdn[1]);
721                     dnsname_copy(&mctx.fqdn[mctx.fqdn[0] + 1], zone->origin);
722 
723                     /************************************
724                      * GOTO, breaking two loops at once.*
725                      ************************************/
726 
727                     goto zdb_zone_maintenance_from_chain_iteration_loop_break;
728                 }
729 #endif
730                 intptr nsec3_key_mask;
731 
732                 zdb_packed_ttlrdata *rrsig = item->rrsig;
733 
734                 bool delete_nsec3_rrsig = FALSE;
735 
736                 if(rrsig != NULL)
737                 {
738                     nsec3_key_mask = 0;
739                     u32 key_matched = 0;
740 
741                     do
742                     {
743                         s32 key_index = -2;
744 
745                         // returns TRUE iff the signature needs to be removed, that covers "key not found"
746 
747                         bool rrsig_should_remove_signature_from_rdata_result = rrsig_should_remove_signature_from_rdata(
748                             ZDB_PACKEDRECORD_PTR_RDATAPTR(rrsig), ZDB_PACKEDRECORD_PTR_RDATASIZE(rrsig),
749                             &mctx.zsks, mctx.now, zone->sig_validity_regeneration_seconds, &key_index);
750 #if DEBUG_SIGNATURE_REFRESH
751                         rdata_desc nsec3_rrsig_desc = {TYPE_RRSIG, ZDB_PACKEDRECORD_PTR_RDATASIZE(rrsig), ZDB_PACKEDRECORD_PTR_RDATAPTR(rrsig)};
752                         log_debug("maintenance: %{dnsname}: should-remove: %{digest32h}.%{dnsname} %{typerdatadesc}: %s (%i)", zone->origin, item->digest, zone->origin, &nsec3_rrsig_desc,
753                                   (rrsig_should_remove_signature_from_rdata_result?"yes":"no"), key_index);
754 #endif
755                         if(rrsig_should_remove_signature_from_rdata_result)
756                         {
757                             if(key_index >= 0)
758                             {
759                                 // RRSIG will be removed, use the key_index to update the mask
760                                 nsec3_key_mask |= 1LL << key_index;
761                             }
762                             else
763                             {
764                                 // the key was not found: remove the RRSIG
765                                 delete_nsec3_rrsig = TRUE; // this enables a "filter out"
766                             }
767                         }
768                         else
769                         {
770                             key_matched |= 1LL << key_index;
771                         }
772 
773                         rrsig = rrsig->next;
774                     }
775                     while(rrsig != NULL);
776 
777                     // keys whose signatures have not been found are added now
778 
779                     nsec3_key_mask |= key_matched ^ mctx.zsk_mask;
780                 }
781                 else
782                 {
783                     nsec3_key_mask = mctx.zsk_mask;
784                 }
785 #if DEBUG || DEBUG_SIGNATURE_REFRESH
786                 log_debug("maintenance: %{dnsname}: at %{digest32h}.%{dnsname} NSEC3: mask=%p/%p (has signature: %i, key-not-found: %i)",
787                         zone->origin, item->digest, zone->origin,
788                         nsec3_key_mask, mctx.zsk_mask, (rrsig != NULL), delete_nsec3_rrsig);
789 
790                 if(rrsig != NULL)
791                 {
792                     for(zdb_packed_ttlrdata *rr = rrsig; rr != NULL; rr = rr->next)
793                     {
794                         const void *rdata = ZDB_PACKEDRECORD_PTR_RDATAPTR(rr);
795                         u16 rdata_size = ZDB_PACKEDRECORD_PTR_RDATASIZE(rr);
796                         log_debug("maintenance: %{dnsname}: at %{digest32h}.%{dnsname} RRSIG: %5i %T -> %T",
797                                    zone->origin, item->digest, zone->origin,
798                                    rrsig_get_key_tag_from_rdata(rdata, rdata_size),
799                                    rrsig_get_valid_from_from_rdata(rdata, rdata_size),
800                                    rrsig_get_valid_until_from_rdata(rdata, rdata_size));
801                     }
802                 }
803 #endif
804                 if((nsec3_key_mask != 0) || delete_nsec3_rrsig)
805                 {
806                     // generate signature or delete signatures
807 
808                     zone_diff_fqdn_rr_set *nsec3_diff_rrset = NULL;
809 #if DEBUG
810                     zone_diff_fqdn* nsec3_diff_fqdn =
811 #endif
812                     zone_diff_add_nsec3_ex(&diff, &mctx.zsks, mctx.zone->nsec.nsec3, item, mctx.zone->min_ttl, &nsec3_diff_rrset, mctx.now, zone->sig_validity_regeneration_seconds);
813 
814                     if(nsec3_diff_rrset != NULL)
815                     {
816 #if DEBUG
817                         log_debug("maintenance: %{dnsname}: %{dnsname}: NSEC3 should be signed", zone->origin, nsec3_diff_fqdn->fqdn);
818 #endif
819                         nsec3_diff_rrset->key_mask = nsec3_key_mask;
820 
821                         ptr_vector_append(&rrset_to_sign, nsec3_diff_rrset);
822                         ++maintenance_generate_nsec3_rrsig_count;
823                         actions_happened = TRUE;
824                     }
825                 }
826 
827                 ++loop_iterations;
828             }
829             else // end of iteration
830             {
831                 // if there is another chain, take its first digest (binary form)
832 
833                 if(++zone->progressive_signature_update.chain_index < mctx.nsec3_chain_count)
834                 {
835                     n3e = n3e->next;
836 
837                     if(n3e != NULL)
838                     {
839                         log_debug("maintenance: %{dnsname}: starting work on NSEC3 chain %i", zone->origin, zone->progressive_signature_update.chain_index);
840 
841                         nsec3_node *first = nsec3_get_first(&mctx.zone->nsec.nsec3->items);
842                         memcpy(from_digest, first->digest, first->digest[0] + 1);
843                         from_digest[first->digest[0] + 1] = 0;
844                         //from_fqdn_is_binary_digest = TRUE;
845 
846                         break;
847                     }
848                     else
849                     {
850                         log_err("maintenance: %{dnsname}: NSEC3 chain smaller than expected", zone->origin);
851 #if DEBUG
852                         log_debug("maintenance: %{dnsname}: no more chain after last label", zone->origin);
853 #endif
854                         zone->progressive_signature_update.chain_index = -1;
855                         last_label_of_zone_reached = TRUE;
856                         mctx.fqdn[0] = 0;
857 
858                         /************************************
859                          * GOTO, breaking two loops at once.*
860                          ************************************/
861 
862                         goto zdb_zone_maintenance_from_chain_iteration_loop_break; // nothing else
863                     }
864                 }
865                 else
866                 {
867                     zone->progressive_signature_update.chain_index = -1;
868 #if DEBUG
869                     log_debug("maintenance: %{dnsname}: nothing to do after last label (NSEC3 rrsig to do: %i)", zone->origin, maintenance_generate_nsec3_rrsig_count);
870 #endif
871                     last_label_of_zone_reached = TRUE;
872                     mctx.fqdn[0] = 0;
873 
874                     /************************************
875                      * GOTO, breaking two loops at once.*
876                      ************************************/
877 
878                     goto zdb_zone_maintenance_from_chain_iteration_loop_break; // nothing else
879                 }
880             }
881         } // for
882     } // while
883 
884 zdb_zone_maintenance_from_chain_iteration_loop_break:
885 
886 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
887     log_debug("maintenance: %{dnsname}: last_label_of_zone_reached: %i, rrset_to_sign contains %i items",
888               zone->origin, last_label_of_zone_reached, ptr_vector_size(&rrset_to_sign));
889 #endif
890 
891     diff_has_changes |= zone_diff_has_changes(&diff, &rrset_to_sign);
892 
893 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
894     log_debug("maintenance: %{dnsname}: diff_has_changes: %i, last_label_of_zone_reached: %i, rrset_to_sign contains %i items",
895               zone->origin, diff_has_changes, last_label_of_zone_reached, ptr_vector_size(&rrset_to_sign));
896 #endif
897 
898     if(diff_has_changes && !last_label_of_zone_reached)
899     {
900 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
901         for(int i = 0; i <= ptr_vector_last_index(&remove); ++i)
902         {
903             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&remove, i);
904             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
905 
906             log_debug("before-validate-sign: %{dnsname}: - %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
907         }
908 
909         for(int i = 0; i <= ptr_vector_last_index(&add); ++i)
910         {
911             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&add, i);
912             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
913 
914             log_debug("before-validate-sign: %{dnsname}: + %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
915         }
916 #endif
917     }
918     else // not diff_has_changes
919     {
920         log_debug("maintenance: %{dnsname}: no changes", zone->origin);
921     }
922 
923     if(last_label_of_zone_reached)
924     {
925         log_debug("maintenance: %{dnsname}: closing edited chains", zone->origin);
926 
927         // add missing NSEC3PARAM
928         // remove TYPE65282
929         // update NSEC3
930         // update signatures
931 
932         bool updated = FALSE;
933 
934         if(zone_diff_will_have_rrset_type(apex, TYPE_NSEC3CHAINSTATE))
935         {
936 #if DEBUG
937             log_debug("maintenance: %{dnsname}: NSEC3CHAINSTATE will be present", zone->origin);
938 #endif
939             // NSEC3PARAM
940 
941             zdb_packed_ttlrdata* nsec3chainstate = zdb_record_find(&zone->apex->resource_record_set, TYPE_NSEC3CHAINSTATE);
942 
943             if(nsec3chainstate != NULL)
944             {
945                 do
946                 {
947                     // find the nsec3param matching nsec3chainstate in nsec3param_rrset
948                     // if no match has been found, add a new nsec3param
949 
950                     zone_diff_label_rr *rr = zone_diff_record_add(&diff, zone->apex, zone->origin, TYPE_NSEC3PARAM, 0,
951                             ZDB_PACKEDRECORD_PTR_RDATASIZE(nsec3chainstate) - 1,
952                             ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec3chainstate)
953                             );
954 
955                     // clears the opt-out flag that exists in the NSEC3CHAINSTATE record
956 
957                     u8* flagsp = &((u8*)rr->rdata)[1];
958                     *flagsp = 0;
959 
960                     nsec3chainstate = nsec3chainstate->next;
961                 }
962                 while(nsec3chainstate != NULL);
963 
964                 yassert(apex != NULL);
965                 zone_diff_fqdn_rr_set *nsec3chainstate_rrset = zone_diff_fqdn_rr_set_get(apex, TYPE_NSEC3CHAINSTATE);
966                 yassert(nsec3chainstate_rrset != NULL);
967 
968                 zone_diff_fqdn_rr_set_set_state(nsec3chainstate_rrset, ZONE_DIFF_RR_REMOVE);
969                 zone_diff_fqdn_rr_set *nsec3param_rrset = zone_diff_fqdn_rr_set_get(apex, TYPE_NSEC3PARAM);
970                 yassert(nsec3param_rrset != NULL);
971 #if DEBUG
972                 log_debug("maintenance: %{dnsname}: NSEC3PARAM should be signed", zone->origin);
973 #endif
974                 nsec3param_rrset->key_mask = zsk_mask;
975                 ptr_vector_append(&rrset_to_sign, nsec3param_rrset);
976 
977                 ++maintenance_generate_nsec3param_rrsig_count;
978                 updated = TRUE;
979                 actions_happened = TRUE;
980             }
981         }
982 
983         if(zone_diff_will_have_rrset_type(apex, TYPE_NSECCHAINSTATE))
984         {
985             yassert(apex != NULL);
986             zone_diff_fqdn_rr_set *nsecchainstate_rrset = zone_diff_fqdn_rr_set_get(apex, TYPE_NSECCHAINSTATE);
987             yassert(nsecchainstate_rrset != NULL);
988             zone_diff_fqdn_rr_set_set_state(nsecchainstate_rrset, ZONE_DIFF_RR_REMOVE);
989 
990             updated = TRUE;
991             nsecchainstate_changed = TRUE;
992             actions_happened = TRUE;
993         }
994 
995         if(updated)
996         {
997             diff_has_changes = TRUE;
998 
999             zdb_zone_maintenance_nsec(&mctx, apex, NULL);
1000             zdb_zone_maintenance_nsec3(&mctx, apex);
1001         }
1002     }
1003 
1004     // if remove contains more than +- SOA and +- SOA RRSIG ...
1005 
1006     if(actions_happened)
1007     {
1008         u32 soa_serial = 0;
1009         zdb_packed_ttlrdata* soa = zdb_record_find(&zone->apex->resource_record_set, TYPE_SOA);
1010         if(soa != NULL)
1011         {
1012             //zone_diff_record_remove(&diff, zone->apex, zone->origin, TYPE_SOA, soa->ttl, ZDB_PACKEDRECORD_PTR_RDATASIZE(soa), ZDB_PACKEDRECORD_PTR_RDATAPTR(soa));
1013             zone_diff_record_remove_automated(&diff, zone->apex, zone->origin, TYPE_SOA, soa->ttl, ZDB_PACKEDRECORD_PTR_RDATASIZE(soa), ZDB_PACKEDRECORD_PTR_RDATAPTR(soa));
1014             rr_soa_get_serial(ZDB_PACKEDRECORD_PTR_RDATAPTR(soa), ZDB_PACKEDRECORD_PTR_RDATASIZE(soa), &soa_serial);
1015         }
1016 
1017         log_debug("maintenance: %{dnsname}: serial=%u actions: rrsig=%i nsec=%i nsec3=%i nsec3-rrsig=%i nsec3param=%i chain-state=%i", zone->origin,
1018                 soa_serial,
1019                 maintenance_rrsig_count,
1020                 maintenance_nsec_count,
1021                 maintenance_nsec3_count,
1022                 maintenance_generate_nsec3_rrsig_count,
1023                 maintenance_generate_nsec3param_rrsig_count,
1024                 nsecchainstate_changed);
1025 
1026         if(ISOK(zone_diff_set_soa(&diff, zone->apex)))
1027         {
1028 #if DEBUG
1029             log_debug1("maintenance: %{dnsname}: SOA should be signed", zone->origin);
1030 #endif
1031             yassert(apex != NULL);
1032             zone_diff_fqdn_rr_set *soa_rrset = zone_diff_fqdn_rr_set_get(apex, TYPE_SOA);
1033             soa_rrset->key_mask = zsk_mask;
1034             yassert(soa_rrset != NULL);
1035             ptr_vector_append(&rrset_to_sign, soa_rrset);
1036         }
1037 
1038 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
1039         for(int i = 0; i <= ptr_vector_last_index(&remove); ++i)
1040         {
1041             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&remove, i);
1042             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
1043             log_debug("before-validate-sign: %{dnsname}: - %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
1044         }
1045 
1046         for(int i = 0; i <= ptr_vector_last_index(&add); ++i)
1047         {
1048             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&add, i);
1049             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
1050             log_debug("before-validate-sign: %{dnsname}: + %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
1051         }
1052 #endif
1053         /*
1054          * Does validation checks on the update.
1055          * Generates signatures for the specified RRSETs.
1056          */
1057 
1058         diff_has_changes = zdb_zone_maintenance_validate_sign_chain_store(&mctx, &diff, zone, &rrset_to_sign, &remove, &add);
1059 
1060         if(!diff_has_changes)
1061         {
1062             log_debug("maintenance: %{dnsname}: update contains nothing", zone->origin);
1063         }
1064 
1065 #if ZDB_ZONE_MAINTENANCE_DETAILED_LOG
1066         for(int i = 0; i <= ptr_vector_last_index(&remove); ++i)
1067         {
1068             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&remove, i);
1069             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
1070             log_debug("after-validate-sign: %{dnsname}: - %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
1071         }
1072 
1073         for(int i = 0; i <= ptr_vector_last_index(&add); ++i)
1074         {
1075             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&add, i);
1076             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
1077             log_debug("after-validate-sign: %{dnsname}: + %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
1078         }
1079 #endif
1080     }
1081 
1082     ptr_vector_destroy(&rrset_to_sign);
1083 
1084     ptr_vector_destroy(&chain_candidates);
1085     ptr_vector_destroy(&candidates);
1086     ptr_vector_destroy(&rrset_vector);
1087 
1088     if(diff_has_changes)
1089     {
1090         log_debug("maintenance: %{dnsname}: writing and playing transaction", zone->origin);
1091 
1092         ret = dynupdate_diff_write_to_journal_and_replay(zone, ZDB_ZONE_MUTEX_RRSIG_UPDATER, &remove, &add);
1093     }
1094     else
1095     {
1096         ret = 0;
1097     }
1098 
1099     ptr_vector_destroy(&remove);
1100     ptr_vector_destroy(&add);
1101 
1102     if(ISOK(ret))
1103     {
1104         if(ret < 32768)
1105         {
1106             if(ret > 0)
1107             {
1108                 double r = 32768.0;
1109                 r /= ret;
1110                 r *= labels_at_once;
1111                 if(r > 65535.0)
1112                 {
1113                     r = 65535;
1114                 }
1115                 mctx.zone->progressive_signature_update.labels_at_once = (u16)r;
1116             }
1117             else
1118             {
1119                 mctx.zone->progressive_signature_update.labels_at_once = MAX_U16;
1120             }
1121 
1122             log_debug("maintenance: %{dnsname}: adjusting up batch size to %i", zone->origin, mctx.zone->progressive_signature_update.labels_at_once);
1123         }
1124         else
1125         {
1126 
1127             if(mctx.zone->progressive_signature_update.labels_at_once > 1)
1128             {
1129                 mctx.zone->progressive_signature_update.labels_at_once /= 2;
1130                 log_debug("maintenance: %{dnsname}: adjusting down batch size to %i", zone->origin, mctx.zone->progressive_signature_update.labels_at_once);
1131             }
1132         }
1133     }
1134 
1135     log_debug("maintenance: %{dnsname}: releasing resources", zone->origin);
1136 
1137     zdb_zone_double_unlock(zone, ZDB_ZONE_MUTEX_SIMPLEREADER, ZDB_ZONE_MUTEX_RRSIG_UPDATER);
1138 
1139     zdb_zone_maintenance_ctx_finalize(&mctx);
1140 
1141     zone_diff_finalize(&diff);
1142 
1143     if(last_label_of_zone_reached)
1144     {
1145         if(ISOK(ret))
1146         {
1147 #if ZDB_ZONE_MAINTENANCE_SAME_PASS_CLOSE
1148             log_info("maintenance: %{dnsname}: done", zone->origin);
1149 
1150             ret = 0; // so the caller do not try to call again right away.
1151             mctx.fqdn[0] = '\0';
1152 #else
1153             if(!diff_has_changes) // the end and no changes : closure must have happened
1154             {
1155                 log_debug("maintenance: %{dnsname}: done, for now", zone->origin);
1156 
1157                 ret = 0; // so the caller do not try to call again right away.
1158                 mctx.fqdn[0] = '\0';
1159             }
1160             else
1161             {
1162                 log_debug("maintenance: %{dnsname}: done, but will allow for one last pass", zone->origin);
1163 
1164                 ret = 1; // else the potential closing of the chain will not happen
1165             }
1166 #endif
1167         }
1168         else
1169         {
1170             if(ret != ZDB_JOURNAL_MUST_SAFEGUARD_CONTINUITY)
1171             {
1172                 log_warn("maintenance: %{dnsname}: reached the end, but with an error code: %r", zone->origin, ret);
1173             }
1174             else
1175             {
1176                 log_debug("maintenance: %{dnsname}: reached the end, but zone must be stored", zone->origin);
1177             }
1178         }
1179     }
1180     else
1181     {
1182         if(ret == 0)
1183         {
1184             log_debug("maintenance: %{dnsname}: another pass will follow from %{dnsname}", zone->origin, mctx.fqdn);
1185 
1186             ret = 1;    // else the remaining will be postponed for a while
1187         }
1188     }
1189 
1190     if(ISOK(ret))
1191     {
1192         size_t fqdn_len = dnsname_len(mctx.fqdn);
1193 
1194         if((fqdn_len <= from_fqdn_size) && (from_fqdn != NULL))
1195         {
1196             log_debug("maintenance: %{dnsname}: saving the next node (%{dnsname})", zone->origin, mctx.fqdn);
1197             memcpy(from_fqdn, mctx.fqdn, fqdn_len); // if from_fqdn == 0, fqdl_len == 0
1198             //from_fqdn_is_binary_digest = FALSE;
1199         }
1200         else
1201         {
1202             log_debug("maintenance: %{dnsname}: cannot save the next node (%{dnsname}) as buffer size is too small (%llu < %llu)", zone->origin, mctx.fqdn, from_fqdn_size, fqdn_len);
1203         }
1204     }
1205     else
1206     {
1207         log_debug("maintenance: %{dnsname}: an error state occurred (%r), keeping previous state (%{dnsname}), ignoring next state (%{dnsname})", zone->origin, ret, from_fqdn, mctx.fqdn);
1208     }
1209     /*
1210     if(from_fqdn_is_binary_digest)
1211     {
1212         log_err("maintenance: %{dnsname}: next fqdn is binary digest %{digest32h}", zone->origin, from_fqdn);
1213     }
1214 
1215     yassert(!from_fqdn_is_binary_digest);
1216     */
1217     s64 stop_time = timeus();
1218 
1219     double dt = stop_time - start_time;
1220     if(dt < 0) dt = 0;
1221     dt /= ONE_SECOND_US_F;
1222 
1223     log_debug("rrsig: %{dnsname}: %r (%.3fs)", zone->origin, ret, dt);
1224 
1225     return ret;
1226 }
1227 
1228 /**
1229  * Will double-lock for reader & rrsig-updater
1230  *
1231  * @param zone
1232  * @param signature_count_loose_limit
1233  * @param present_signatures_are_verified
1234  * @return 0 : all done, >0 : some done (some could be 0), <0 error
1235  */
1236 
1237 ya_result
zdb_zone_maintenance(zdb_zone * zone)1238 zdb_zone_maintenance(zdb_zone* zone)
1239 {
1240     ya_result ret;
1241     u8 *prev_fqdn;
1242     s8 prev_chain_index;
1243     u8 in_out_fqdn[MAX_DOMAIN_LENGTH];
1244 
1245     if(zone->progressive_signature_update.current_fqdn != NULL)
1246     {
1247         log_debug("maintenance: %{dnsname}: resuming from %{dnsname}", zone->origin, zone->progressive_signature_update.current_fqdn);
1248 
1249         dnsname_copy(in_out_fqdn, zone->progressive_signature_update.current_fqdn);
1250         prev_fqdn = zone->progressive_signature_update.current_fqdn;
1251         prev_chain_index = zone->progressive_signature_update.chain_index;
1252         //dnsname_zfree(zone->progressive_signature_update.current_fqdn);
1253         zone->progressive_signature_update.current_fqdn = NULL;
1254     }
1255     else
1256     {
1257         prev_fqdn = NULL;
1258         prev_chain_index = -1;
1259         log_info("maintenance: %{dnsname}: zone maintenance started", zone->origin);
1260         in_out_fqdn[0] = 0;
1261 #if DEBUG
1262         memset(in_out_fqdn, 0, sizeof(in_out_fqdn));
1263 #endif
1264         zone->progressive_signature_update.current_fqdn = NULL;
1265         zone->progressive_signature_update.chain_index = -1;
1266 #if DEBUG
1267         log_debug("maintenance: %{dnsname}: interval=%is jitter=%is regeneration=%is (DEBUG)", zone->origin, zone->sig_validity_interval_seconds, zone->sig_validity_jitter_seconds, zone->sig_validity_regeneration_seconds);
1268 #endif
1269     }
1270 
1271     ret = zdb_zone_maintenance_from(zone, in_out_fqdn, sizeof(in_out_fqdn), ZDB_MAINTENANCE_BATCH_TIME_US_MAX, ZDB_ZONE_MAINTENANCE_RRSIG_COUNT_THRESHOLD);
1272 
1273     if(in_out_fqdn[0] != 0)
1274     {
1275         if(ISOK(ret))
1276         {
1277             zone->progressive_signature_update.current_fqdn = dnsname_zdup(in_out_fqdn);
1278             if(prev_fqdn != NULL)
1279             {
1280                 dnsname_zfree(prev_fqdn);
1281                 prev_fqdn = NULL;
1282             }
1283 
1284             log_debug("maintenance: %{dnsname}: pausing at %{dnsname} (%r)", zone->origin, zone->progressive_signature_update.current_fqdn, ret);
1285         }
1286         else
1287         {
1288             if(prev_fqdn != NULL)
1289             {
1290                 zone->progressive_signature_update.current_fqdn = prev_fqdn;
1291                 zone->progressive_signature_update.chain_index = prev_chain_index;
1292                 prev_fqdn = NULL;
1293                 // prev_chain_index = -1;
1294                 log_debug("maintenance: %{dnsname}: pausing at %{dnsname} (%r) (may try again)", zone->origin, zone->progressive_signature_update.current_fqdn, ret);
1295             }
1296             else
1297             {
1298                 log_debug("maintenance: %{dnsname}: pausing (%r) (may try again)", zone->origin, ret);
1299             }
1300         }
1301     }
1302     else
1303     {
1304         if(prev_fqdn != NULL)
1305         {
1306             dnsname_zfree(prev_fqdn);
1307             prev_fqdn = NULL;
1308         }
1309 
1310         log_debug("maintenance: %{dnsname}: done (%r)", zone->origin, ret);
1311     }
1312 
1313     if(ret == 0)
1314     {
1315         log_info("maintenance: %{dnsname}: zone maintenance finished", zone->origin);
1316     }
1317 
1318     return ret;
1319 }
1320 
1321 ya_result
zdb_zone_sign(zdb_zone * zone)1322 zdb_zone_sign(zdb_zone* zone)
1323 {
1324     ya_result ret = zdb_zone_maintenance_from(zone, NULL, 0, ZDB_MAINTENANCE_BATCH_TIME_US_MAX, ZDB_ZONE_MAINTENANCE_RRSIG_COUNT_THRESHOLD);
1325     return ret;
1326 }
1327 
1328 /** @} */
1329