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 dnsdbzone Zone related functions
36  *  @ingroup dnsdb
37  *  @brief Functions used to manipulate a zone
38  *
39  *  Functions used to manipulate a zone
40  *
41  * @{
42  */
43 
44 #define DEBUG_FORCE_INSANE_SIGNATURE_MAINTENANCE_PARAMETERS 0
45 #if DEBUG_FORCE_INSANE_SIGNATURE_MAINTENANCE_PARAMETERS
46 #pragma message("WARNING: DEBUG_FORCE_INSANE_SIGNATURE_MAINTENANCE_PARAMETERS enabled !")
47 #endif
48 
49 #define ZDB_JOURNAL_CODE 1
50 
51 #include "dnsdb/dnsdb-config.h"
52 #include <unistd.h>
53 #include <arpa/inet.h>
54 #include <stdint.h>
55 
56 #if DEBUG
57 #include <dnscore/format.h>
58 #endif
59 
60 #include <dnscore/dnscore.h>
61 #include <dnscore/logger.h>
62 #include <dnscore/threaded_dll_cw.h>
63 
64 #include "dnsdb/dnsdb-config.h"
65 #include "dnsdb/dnssec-keystore.h"
66 #include "dnsdb/zdb_icmtl.h"
67 #include "dnsdb/zdb.h"
68 
69 #include "dnsdb/zdb_zone.h"
70 #include "dnsdb/zdb_zone_label.h"
71 #include "dnsdb/zdb_rr_label.h"
72 #include "dnsdb/zdb_record.h"
73 
74 #include "dnsdb/zdb_utils.h"
75 #include "dnsdb/zdb_error.h"
76 
77 #include "dnsdb/dnsrdata.h"
78 
79 #include "dnsdb/journal.h"
80 #include "dnsdb/dynupdate-diff.h"
81 #include "dnsdb/dynupdate-message.h"
82 #include "dnsdb/zdb-zone-path-provider.h"
83 
84 #if HAS_DNSSEC_SUPPORT
85 #include "dnsdb/rrsig.h"
86 #if ZDB_HAS_NSEC_SUPPORT
87 #include "dnsdb/nsec.h"
88 #endif
89 #if ZDB_HAS_NSEC3_SUPPORT
90 #include "dnsdb/nsec3.h"
91 #endif
92 #endif
93 
94 #ifndef HAS_DYNUPDATE_DIFF_ENABLED
95 #error "HAS_DYNUPDATE_DIFF_ENABLED not defined"
96 #endif
97 
98 #if DEBUG
99 #define ZONE_MUTEX_LOG 0    // set this to 0 to disable in DEBUG
100 #else
101 #define ZONE_MUTEX_LOG 0
102 #endif
103 
104 extern logger_handle* g_database_logger;
105 #define MODULE_MSG_HANDLE g_database_logger
106 
107 #define TMPRDATA_TAG 0x4154414452504d54
108 
109 #if HAS_TRACK_ZONES_DEBUG_SUPPORT
110 smp_int g_zone_instanciated_count = SMP_INT_INITIALIZER;
111 ptr_set g_zone_instanciated_set = PTR_SET_PTR_EMPTY;
112 #endif
113 
zdb_zone_record_or_and_flags_to_subdomains(zdb_rr_label * rr_label,u16 orflags,u16 andflags)114 static void zdb_zone_record_or_and_flags_to_subdomains(zdb_rr_label *rr_label, u16 orflags, u16 andflags)
115 {
116     dictionary_iterator iter;
117     dictionary_iterator_init(&rr_label->sub, &iter);
118     while(dictionary_iterator_hasnext(&iter))
119     {
120         zdb_rr_label** sub_labelp = (zdb_rr_label**)dictionary_iterator_next(&iter);
121 
122         zdb_rr_label_flag_or_and(*sub_labelp, orflags, andflags),
123 
124         zdb_zone_record_or_and_flags_to_subdomains(*sub_labelp, orflags, andflags);
125     }
126 }
127 
128 /**
129  * @brief Adds a record to a zone
130  *
131  * Adds a record to a zone.
132  *
133  * @note Expects the full fqdn in the labels parameter. "." has a labels_top at -1
134  *
135  *
136  * @param[in] zone the zone
137  * @param[in] labels the stack of labels of the dns name
138  * @param[in] labels_top the index of the top of the stack (the level)
139  * @param[in] type the type of the record
140  * @param[in] ttlrdata the ttl and rdata of the record.  NOTE: the zone becomes its new owner !!!
141  */
142 
143 void
zdb_zone_record_add(zdb_zone * zone,dnslabel_vector_reference labels,s32 labels_top,u16 type,zdb_packed_ttlrdata * ttlrdata)144 zdb_zone_record_add(zdb_zone *zone, dnslabel_vector_reference labels, s32 labels_top, u16 type, zdb_packed_ttlrdata *ttlrdata)
145 {
146     zdb_rr_label *rr_label = zdb_rr_label_add(zone, labels, labels_top - zone->origin_vector.size - 1); // flow verified
147     /* This record will be put as it is in the DB */
148 
149 #if ZDB_HAS_NSEC_SUPPORT
150     /*
151      * At this point I could add empty nsec3 records, or schedule the nsec3 signature
152      */
153 #endif
154 
155     u16 flag_mask = 0;
156 
157     switch(type)
158     {
159         case TYPE_CNAME:
160         {
161             if(zdb_rr_label_flag_isset(rr_label, ZDB_RR_LABEL_DROPCNAME))
162             {
163                 log_err("zone %{dnsname}: ignoring CNAME add on non-CNAME", zone->origin);
164                 ZDB_RECORD_ZFREE(ttlrdata);
165                 return;
166             }
167             flag_mask = ZDB_RR_LABEL_HASCNAME;
168             break;
169         }
170         case TYPE_RRSIG:
171         {
172             if(!zdb_record_insert_checked_keep_ttl(&rr_label->resource_record_set, type, ttlrdata)) /* FB done */
173             {
174                 ZDB_RECORD_ZFREE(ttlrdata);
175                 return;
176             }
177 
178             zdb_rr_label_flag_or(rr_label, flag_mask);
179 
180             return;
181         }
182         case TYPE_NSEC:
183             break;
184         case TYPE_NS:
185         {
186             if(zdb_rr_label_flag_isset(rr_label, ZDB_RR_LABEL_HASCNAME))
187             {
188                 log_err("zone %{dnsname}: ignoring NS add on CNAME", zone->origin);
189                 ZDB_RECORD_ZFREE(ttlrdata);
190                 return;
191             }
192 
193             if(zdb_rr_label_is_not_apex(rr_label))
194             {
195                 // handle the sub-delegation case
196 
197                 if(zdb_rr_label_flag_isclear(rr_label, ZDB_RR_LABEL_UNDERDELEGATION))
198                 {
199                     flag_mask = ZDB_RR_LABEL_DELEGATION;
200 
201                     //if(!ZDB_LABEL_UNDERDELEGATION(flag_mask & ZDB_RR_LABEL_UNDERDELEGATION))
202                     {
203                         /* all labels under are "under delegation" */
204 
205                         //zdb_zone_record_or_flags_to_subdomains(rr_label, ZDB_RR_LABEL_UNDERDELEGATION);
206                         zdb_zone_record_or_and_flags_to_subdomains(rr_label, ZDB_RR_LABEL_UNDERDELEGATION, ~ZDB_RR_LABEL_DELEGATION);
207                     }
208                 }
209             }
210 
211             flag_mask |= ZDB_RR_LABEL_DROPCNAME /*| ZDB_RR_LABEL_HAS_NS*/;
212 
213             break;
214         }
215         case TYPE_DS:
216         {
217             if(zdb_rr_label_flag_isset(rr_label, ZDB_RR_LABEL_HASCNAME))
218             {
219                 log_err("zone %{dnsname}: ignoring non-CNAME add on CNAME", zone->origin);
220                 ZDB_RECORD_ZFREE(ttlrdata);
221                 return;
222             }
223 
224             flag_mask |= ZDB_RR_LABEL_DROPCNAME /*| ZDB_RR_LABEL_HAS_DS*/;
225             break;
226         }
227         default:
228         {
229             if(zdb_rr_label_flag_isset(rr_label, ZDB_RR_LABEL_HASCNAME))
230             {
231                 log_err("zone %{dnsname}: ignoring non-CNAME add on CNAME", zone->origin);
232                 ZDB_RECORD_ZFREE(ttlrdata);
233                 return;
234             }
235             flag_mask = ZDB_RR_LABEL_DROPCNAME;
236             break;
237         }
238     }
239 
240     if(!zdb_record_insert_checked(&rr_label->resource_record_set, type, ttlrdata)) /* FB done */
241     {
242         ZDB_RECORD_ZFREE(ttlrdata);
243         return;
244     }
245 
246     zdb_rr_label_flag_or(rr_label, flag_mask);
247 }
248 
249 /**
250  * @brief Search for a record in a zone
251  *
252  * Search for a record in a zone
253  *
254  * @param[in] zone the zone
255  * @param[in] labels the stack of labels of the dns name
256  * @param[in] labels_top the index of the top of the stack (the level)
257  * @param[in] type the type of the record
258  *
259  * @return a pointer to the RRSET or NULL if it was not found
260  */
261 
262 zdb_packed_ttlrdata*
zdb_zone_record_find(zdb_zone * zone,dnslabel_vector_reference labels,s32 labels_top,u16 type)263 zdb_zone_record_find(zdb_zone *zone, dnslabel_vector_reference labels, s32 labels_top, u16 type)
264 {
265     zdb_rr_label* rr_label = zdb_rr_label_find_exact(zone->apex, labels, labels_top);
266 
267     if(rr_label != NULL)
268     {
269         return zdb_record_find(&rr_label->resource_record_set, type);
270     }
271 
272     return NULL;
273 }
274 
275 /**
276  * Searches the zone for the zdb_rr_label of an fqdn if it exists.
277  *
278  * @param[in] zone the zone
279  * @parma[in] fqdn the fqdn of the the label
280  *
281  * @return a pointer the label, or NULL if it was not found.
282  */
283 
284 zdb_rr_label*
zdb_zone_find_label_from_fqdn(zdb_zone * zone,const u8 * fqdn)285 zdb_zone_find_label_from_fqdn(zdb_zone *zone, const u8 *fqdn)
286 {
287     dnslabel_vector labels;
288     s32 labels_top = dnsname_to_dnslabel_vector(fqdn, labels);
289     zdb_rr_label* label = zdb_rr_label_find_exact(zone->apex, labels, labels_top - zone->origin_vector.size - 1);
290     return label;
291 }
292 
293 static ya_result
zdb_default_query_access_filter(const message_data * mesg,const void * extension)294 zdb_default_query_access_filter(const message_data *mesg, const void *extension)
295 {
296     (void)mesg;
297     (void)extension;
298 
299     return SUCCESS;
300 }
301 
302 static
zdb_zone_get_struct_size(const u8 * origin)303 u32 zdb_zone_get_struct_size(const u8 *origin)
304 {
305     u32 zone_footprint = sizeof(zdb_zone) - sizeof(dnsname_vector) + sizeof(u8*) * (dnsname_getdepth(origin) + 1);
306 
307     return zone_footprint;
308 }
309 
310 zdb_zone*
zdb_zone_create(const u8 * origin)311 zdb_zone_create(const u8* origin)
312 {
313     zdb_zone *zone;
314     u32 zone_footprint = zdb_zone_get_struct_size(origin);
315     ZALLOC_ARRAY_OR_DIE(zdb_zone*, zone, zone_footprint, ZDB_ZONETAG);
316 
317 #if DEBUG
318     memset(zone, 0xac, zone_footprint);
319 #endif
320 
321 #if HAS_TRACK_ZONES_DEBUG_SUPPORT
322     smp_int_inc(&g_zone_instanciated_count);
323     mutex_lock(&g_zone_instanciated_count.mutex);
324     ptr_node *node = ptr_set_insert(&g_zone_instanciated_set, zone);
325     mutex_unlock(&g_zone_instanciated_count.mutex);
326     node->value = NULL;
327 #endif
328 
329     log_debug7("zdb_zone_create %{dnsname}@%p", origin, zone);
330 
331     zone->origin = dnsname_zdup(origin);
332 
333     dnsname_to_dnsname_vector(zone->origin, &zone->origin_vector);
334 
335 #if ZDB_RECORDS_MAX_CLASS > 1
336     zone->zclass = CLASS_IN;
337 #elif ZDB_RECORDS_MAX_CLASS <= 0
338 #error "ZDB_RECORDS_MAX_CLASS must be > 0"
339 #endif
340 
341     zone->axfr_timestamp = 1;
342     /* zone->axfr_serial = 0; implicit */
343 
344 #if ZDB_HAS_DNSSEC_SUPPORT
345     ZEROMEMORY(&zone->nsec, sizeof(dnssec_zone_extension));
346     zone->sig_validity_interval_seconds = 30*24*3600;       /* 1 month */
347     zone->sig_validity_regeneration_seconds = 7*24*3600;    /* 1 week */
348     zone->sig_validity_jitter_seconds = 86400;              /* 1 day */
349     zone->sig_quota = 100;
350 
351 
352 #if DEBUG_FORCE_INSANE_SIGNATURE_MAINTENANCE_PARAMETERS
353     zone->sig_validity_regeneration_seconds = 90;
354     zone->sig_validity_interval_seconds = 180;
355     zone->sig_validity_jitter_seconds = 5;
356 #endif
357 
358 #endif
359 
360     zone->alarm_handle = alarm_open(zone->origin);
361 
362     zone->apex = zdb_rr_label_new_instance(ROOT_LABEL);
363     zone->apex->_flags = ZDB_RR_LABEL_APEX;
364 
365     zone->query_access_filter = zdb_default_query_access_filter;
366     zone->acl = NULL;
367 #if ZDB_HAS_DNSSEC_SUPPORT
368     zone->progressive_signature_update.current_fqdn = NULL;
369     zone->progressive_signature_update.earliest_signature_expiration = MAX_S32;
370     zone->progressive_signature_update.labels_at_once = ZDB_ZONE_MAINTENANCE_LABELS_AT_ONCE_DEFAULT;
371 #endif
372     mutex_init(&zone->lock_mutex);
373     cond_init(&zone->lock_cond);
374     zone->rc = 1;
375     zone->lock_owner = ZDB_ZONE_MUTEX_NOBODY;
376     zone->lock_count = 0;
377     zone->lock_reserved_owner = ZDB_ZONE_MUTEX_NOBODY;
378     atomic_store(&zone->_status, 0);
379     zone->_flags = 0;
380 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
381     zone->lock_trace = NULL;
382     zone->lock_id = 0;
383     zone->lock_timestamp = 0;
384 #endif
385 #if ZDB_ZONE_HAS_JNL_REFERENCE
386     zone->journal = NULL;
387 #endif
388 
389     return zone;
390 }
391 
392 void
zdb_zone_invalidate(zdb_zone * zone)393 zdb_zone_invalidate(zdb_zone *zone)
394 {
395     yassert(zone != NULL);
396 
397     zdb_zone_set_invalid(zone);
398 }
399 
400 /**
401  * @brief Destroys a zone and all its content
402  *
403  * Destroys a zone and all its content
404  *
405  * @param[in] zone a pointer to the zone
406  */
407 
408 void
zdb_zone_truncate_invalidate(zdb_zone * zone)409 zdb_zone_truncate_invalidate(zdb_zone *zone)
410 {
411     if(zone != NULL)
412     {
413         // remove all alarms linked to the zone
414         alarm_close(zone->alarm_handle);
415         zone->alarm_handle = ALARM_HANDLE_INVALID;
416 
417         // empty the zone records
418         if(zone->apex != NULL)
419         {
420 #if ZDB_HAS_NSEC_SUPPORT
421             nsec_destroy_zone(zone);
422 #endif
423 
424 #if ZDB_HAS_NSEC3_SUPPORT
425             nsec3_destroy_zone(zone);
426 #endif
427 
428             // zdb_rr_label_destroy(zone, &zone->apex);
429 
430             /*
431              * Destroy ALL the content of the apex but not the apex itself.
432              */
433 
434             zdb_rr_label_truncate(zone, zone->apex);
435 
436             zdb_zone_set_invalid(zone);
437         }
438     }
439 }
440 
441 void
zdb_zone_destroy_nolock(zdb_zone * zone)442 zdb_zone_destroy_nolock(zdb_zone *zone)
443 {
444     if(zone != NULL)
445     {
446         assert(zone->rc == 0);
447 
448         log_debug("zone: %{dnsname}: releasing memory (nolock)", zone->origin);
449 
450         int rc = zone->rc;
451 
452         if(rc != 0)
453         {
454             log_debug("zone: %{dnsname}: rc=%i != 0 (nolock)", zone->origin, rc);
455         }
456 
457 #if HAS_TRACK_ZONES_DEBUG_SUPPORT
458         mutex_lock(&g_zone_instanciated_count.mutex);
459         bool known_zone = (ptr_set_find(&g_zone_instanciated_set, zone) != NULL);
460         yassert(known_zone);
461         ptr_set_delete(&g_zone_instanciated_set, zone);
462 
463         mutex_unlock(&g_zone_instanciated_count.mutex);
464         smp_int_dec(&g_zone_instanciated_count);
465         yassert(smp_int_get(&g_zone_instanciated_count) >= 0);
466 #endif
467 
468         if(zone->alarm_handle != ALARM_HANDLE_INVALID)
469         {
470             log_debug("zone: %{dnsname}: removing alarm events (%p) (nolock)", zone->origin, zone->alarm_handle);
471 
472             alarm_close(zone->alarm_handle);
473             zone->alarm_handle = ALARM_HANDLE_INVALID;
474         }
475 
476 #if ZDB_ZONE_HAS_JNL_REFERENCE
477         if(zone->journal != NULL)
478         {
479             journal *jh = zone->journal; // pointed for closing/releasing
480             journal_close(jh); // only authorised usage of this call
481             zone->journal = NULL;
482         }
483 #endif
484 
485 #if !DEBUG
486         // do not bother clearing the memory if it's for a shutdown (faster)
487         if(!dnscore_shuttingdown())
488 #endif
489         {
490             if(zone->apex != NULL)
491             {
492 
493 #if ZDB_HAS_NSEC_SUPPORT
494                 log_debug("zone: %{dnsname}: deleting NSEC chain (if any) (nolock)", zone->origin);
495 
496                 nsec_destroy_zone(zone);
497 #endif
498 
499 #if ZDB_HAS_NSEC3_SUPPORT
500                 log_debug("zone: %{dnsname}: deleting NSEC3 chain (if any) (nolock)", zone->origin);
501 
502                 nsec3_destroy_zone(zone);
503 #endif
504                 log_debug("zone: %{dnsname}: deleting records (nolock)", zone->origin);
505 
506                 zdb_rr_label_destroy(zone, &zone->apex);
507                 zone->apex = NULL;
508             }
509             else
510             {
511                 log_debug("zone: %{dnsname}: apex is empty (nolock)", zone->origin);
512             }
513         }
514 #if !DEBUG
515         else
516         {
517             log_debug("zone: %{dnsname}: will not spend time carefully releasing the memory used by the records and the chains (shutting down) (nolock)", zone->origin);
518         }
519 #endif
520 
521         u32 zone_footprint = zdb_zone_get_struct_size(zone->origin);
522 
523 #if HAS_DNSSEC_SUPPORT
524         if(zone->progressive_signature_update.current_fqdn != NULL)
525         {
526             log_debug("zone: %{dnsname}: releasing progressive signature update (nolock)", zone->origin);
527 
528             dnsname_zfree(zone->progressive_signature_update.current_fqdn);
529             zone->progressive_signature_update.current_fqdn = NULL;
530         }
531 #endif
532 
533 #if DEBUG
534         zone->min_ttl= 0xbadbad01;
535         zone->acl = NULL;
536         zone->axfr_serial = 0xbadbad00;
537 #endif
538 
539         log_debug("zone: %{dnsname}: releasing name and structure (nolock)", zone->origin);
540 
541         dnsname_zfree(zone->origin);
542 
543 #if DEBUG
544         zone->origin = NULL;
545 #endif
546 
547         ZFREE_ARRAY(zone, zone_footprint);
548     }
549 #if DEBUG
550     else
551     {
552         log_debug("zone: NULL: zdb_zone_destroy_nolock called on NULL (nolock)");
553     }
554 #endif
555 }
556 
557 /**
558  * @brief Destroys a zone and all its content
559  *
560  * Destroys a zone and all its content
561  *
562  * @param[in] zone a pointer to the zone
563  */
564 
565 void
zdb_zone_destroy(zdb_zone * zone)566 zdb_zone_destroy(zdb_zone *zone)
567 {
568     if(zone != NULL)
569     {
570         log_debug("zone: %{dnsname}: releasing memory", zone->origin);
571 
572         zdb_zone_lock(zone, ZDB_ZONE_MUTEX_DESTROY);
573         int rc = zone->rc;
574         zdb_zone_unlock(zone, ZDB_ZONE_MUTEX_DESTROY);
575 
576         if(rc != 0)
577         {
578             log_debug("zone: %{dnsname}: rc=%i != 0", zone->origin, rc);
579 
580             logger_flush();
581             abort();
582         }
583 
584 #if HAS_TRACK_ZONES_DEBUG_SUPPORT
585         mutex_lock(&g_zone_instanciated_count.mutex);
586         bool known_zone = (ptr_set_find(&g_zone_instanciated_set, zone) != NULL);
587         yassert(known_zone);
588         ptr_set_delete(&g_zone_instanciated_set, zone);
589 
590         mutex_unlock(&g_zone_instanciated_count.mutex);
591         smp_int_dec(&g_zone_instanciated_count);
592         yassert(smp_int_get(&g_zone_instanciated_count) >= 0);
593 #endif
594 
595         zdb_zone_lock(zone, ZDB_ZONE_MUTEX_DESTROY);
596         if(zone->alarm_handle != ALARM_HANDLE_INVALID)
597         {
598             log_debug("zone: %{dnsname}: removing alarm events (%p)", zone->origin, zone->alarm_handle);
599 
600             alarm_close(zone->alarm_handle);
601             zone->alarm_handle = ALARM_HANDLE_INVALID;
602         }
603 #if ZDB_ZONE_HAS_JNL_REFERENCE
604         if(zone->journal != NULL)
605         {
606             journal *jh = zone->journal; // pointed for closing/releasing
607             zdb_zone_unlock(zone, ZDB_ZONE_MUTEX_DESTROY);
608             journal_close(jh); // only authorised usage of this call
609             zone->journal = NULL;
610         }
611         else
612 #endif
613         {
614             zdb_zone_unlock(zone, ZDB_ZONE_MUTEX_DESTROY);
615         }
616 
617 
618 #if !DEBUG
619         // do not bother clearing the memory if it's for a shutdown (faster)
620         if(!dnscore_shuttingdown())
621 #endif
622         {
623             if(zone->apex != NULL)
624             {
625 #if ZDB_HAS_NSEC_SUPPORT
626                 log_debug("zone: %{dnsname}: deleting NSEC chain (if any)", zone->origin);
627 
628                 nsec_destroy_zone(zone);
629 #endif
630 
631 #if ZDB_HAS_NSEC3_SUPPORT
632                 log_debug("zone: %{dnsname}: deleting NSEC3 chain (if any)", zone->origin);
633 
634                 nsec3_destroy_zone(zone);
635 #endif
636                 log_debug("zone: %{dnsname}: deleting records", zone->origin);
637 
638                 zdb_rr_label_destroy(zone, &zone->apex);
639                 zone->apex = NULL;
640             }
641             else
642             {
643                 log_debug("zone: %{dnsname}: apex is empty", zone->origin);
644             }
645         }
646 #if !DEBUG
647         else
648         {
649             log_debug("zone: %{dnsname}: will not spend time carefully releasing the memory used by the records and the chains (shutting down)", zone->origin);
650         }
651 #endif
652 
653         u32 zone_footprint = zdb_zone_get_struct_size(zone->origin);
654 
655 #if HAS_DNSSEC_SUPPORT
656         if(zone->progressive_signature_update.current_fqdn != NULL)
657         {
658             log_debug("zone: %{dnsname}: releasing progressive signature update", zone->origin);
659 
660             dnsname_zfree(zone->progressive_signature_update.current_fqdn);
661             zone->progressive_signature_update.current_fqdn = NULL;
662         }
663 #endif
664 
665 #if DEBUG
666         zone->min_ttl= 0xbadbad01;
667         zone->acl = NULL;
668         zone->axfr_serial = 0xbadbad00;
669 #endif
670         cond_finalize(&zone->lock_cond);
671         mutex_destroy(&zone->lock_mutex);
672 
673         log_debug("zone: %{dnsname}: releasing name and structure", zone->origin);
674 
675         dnsname_zfree(zone->origin);
676 
677 #if DEBUG
678         zone->origin = NULL;
679 #endif
680 
681         ZFREE_ARRAY(zone, zone_footprint);
682     }
683 #if DEBUG
684     else
685     {
686         log_debug("zone: NULL: zdb_zone_destroy called on NULL");
687     }
688 #endif
689 }
690 
691 /**
692  * @brief Copies the soa of a zone to an soa_rdata structure.
693  *
694  * Copies the soa of a zone to an soa_rdata structure.
695  * No memory is allocated for the soa_rdata.  If the zone is destroyed,
696  * the soa_rdata becomes invalid.
697  *
698  * @param[in] zone a pointer to the zone
699  * @param[out] soa_out a pointer to an soa_rdata structure
700  */
701 
702 ya_result
zdb_zone_getsoa(const zdb_zone * zone,soa_rdata * soa_out)703 zdb_zone_getsoa(const zdb_zone *zone, soa_rdata* soa_out)
704 {
705 #if DEBUG
706     if(zone->lock_owner == ZDB_ZONE_MUTEX_NOBODY)
707     {
708         log_err("zdb_zone_getsoa called on an unlocked zone: %{dnsname}", zone->origin);
709         debug_log_stacktrace(MODULE_MSG_HANDLE, LOG_ERR, "zdb_zone_getsoa");
710         //logger_flush();
711     }
712     else
713     {
714         log_debug("zdb_zone_getsoa called on a zone locked by %02hhx (%{dnsname})", zone->lock_owner, zone->origin);
715     }
716 #endif
717 
718     const zdb_rr_label *apex = zone->apex;
719     const zdb_packed_ttlrdata *soa = zdb_record_find(&apex->resource_record_set, TYPE_SOA); // zone is locked
720     ya_result return_code;
721 
722     if(soa != NULL)
723     {
724         return_code = zdb_record_getsoa(soa, soa_out);
725     }
726     else
727     {
728         return_code = ZDB_ERROR_NOSOAATAPEX;
729     }
730 
731     return return_code;
732 }
733 
734 ya_result
zdb_zone_getsoa_ttl_rdata(const zdb_zone * zone,u32 * ttl,u16 * rdata_size,const u8 ** rdata)735 zdb_zone_getsoa_ttl_rdata(const zdb_zone *zone, u32 *ttl, u16 *rdata_size, const u8 **rdata)
736 {
737 #if DEBUG
738     if(zone->lock_owner == ZDB_ZONE_MUTEX_NOBODY)
739     {
740         log_err("zdb_zone_getsoa_ttl_rdata called on an unlocked zone: %{dnsname}", zone->origin);
741         debug_log_stacktrace(MODULE_MSG_HANDLE, LOG_ERR, "zdb_zone_getsoa_ttl_rdata");
742         logger_flush();
743     }
744     else
745     {
746         log_debug("zdb_zone_getsoa_ttl_rdata called on a zone locked by %02hhx (%{dnsname})", zone->lock_owner, zone->origin);
747     }
748 #endif
749 
750     const zdb_rr_label *apex = zone->apex;
751     const zdb_packed_ttlrdata *soa = zdb_record_find(&apex->resource_record_set, TYPE_SOA); // zone is locked
752 
753     if(soa == NULL)
754     {
755         return ZDB_ERROR_NOSOAATAPEX;
756     }
757 
758     if(ttl != NULL)
759     {
760         *ttl = soa->ttl;
761     }
762 
763     if(rdata_size != NULL && rdata != NULL)
764     {
765         *rdata_size = soa->rdata_size;
766         *rdata = &soa->rdata_start[0];
767     }
768 
769     return SUCCESS;
770 }
771 
772 /**
773  * @brief Retrieve the serial of a zone
774  *
775  * Retrieve the serial of a zone
776  *
777  * @param[in] zone a pointer to the zone
778  * @param[out] soa_out a pointer to an soa_rdata structure
779  */
780 
781 ya_result
zdb_zone_getserial(const zdb_zone * zone,u32 * serial)782 zdb_zone_getserial(const zdb_zone *zone, u32 *serial)
783 {
784 #if DEBUG
785     if(zone->lock_owner == ZDB_ZONE_MUTEX_NOBODY)
786     {
787         log_err("zdb_zone_getserial called on an unlocked zone (%{dnsname})", zone->origin);
788         debug_log_stacktrace(MODULE_MSG_HANDLE, LOG_ERR, "zdb_zone_getserial");
789         logger_flush();
790     }
791     else
792     {
793         log_debug1("zdb_zone_getserial called on a zone locked by %02hhx (%{dnsname})", zone->lock_owner, zone->origin);
794     }
795 #endif
796 
797     yassert(serial != NULL);
798 
799     zdb_rr_label *apex = zone->apex;
800     zdb_packed_ttlrdata *soa = zdb_record_find(&apex->resource_record_set, TYPE_SOA); // zone is locked
801 
802     if(soa != NULL)
803     {
804         return rr_soa_get_serial(soa->rdata_start, soa->rdata_size, serial);
805     }
806 
807     return ZDB_ERROR_NOSOAATAPEX;
808 }
809 
810 const zdb_packed_ttlrdata*
zdb_zone_get_dnskey_rrset(zdb_zone * zone)811 zdb_zone_get_dnskey_rrset(zdb_zone *zone)
812 {
813     return zdb_record_find(&zone->apex->resource_record_set, TYPE_DNSKEY); // zone is locked
814 }
815 
816 bool
zdb_zone_isinvalid(zdb_zone * zone)817 zdb_zone_isinvalid(zdb_zone *zone)
818 {
819     bool invalid = TRUE;
820 
821     if((zone != NULL) && (zone->apex != NULL))
822     {
823         invalid = zdb_zone_is_invalid(zone);
824     }
825 
826     return invalid;
827 }
828 
829 #if HAS_DNSSEC_SUPPORT
830 
831 /**
832  *
833  * Returns TRUE iff the key is present as a record in the zone
834  *
835  * @param zone
836  * @param key
837  * @return
838  */
839 
840 bool
zdb_zone_contains_dnskey_record_for_key(zdb_zone * zone,const dnssec_key * key)841 zdb_zone_contains_dnskey_record_for_key(zdb_zone *zone, const dnssec_key *key)
842 {
843     yassert(zdb_zone_islocked(zone));
844 
845     const zdb_packed_ttlrdata *dnskey_rrset = zdb_record_find(&zone->apex->resource_record_set, TYPE_DNSKEY); // zone is locked
846 
847     const zdb_packed_ttlrdata *dnskey_record = dnskey_rrset;
848 
849     while(dnskey_record != NULL)
850     {
851         if(dnskey_matches_rdata(key, ZDB_PACKEDRECORD_PTR_RDATAPTR(dnskey_record), ZDB_PACKEDRECORD_PTR_RDATASIZE(dnskey_record)))
852         {
853             return TRUE;
854         }
855 
856         dnskey_record = dnskey_record->next;
857     }
858 
859     return FALSE;
860 }
861 
862 /**
863  * Returns TRUE iff there is at least one RRSIG record with the tag and algorithm of the key
864  *
865  * @param zone
866  * @param key
867  * @return
868  */
869 
870 bool
zdb_zone_apex_contains_rrsig_record_by_key(zdb_zone * zone,const dnssec_key * key)871 zdb_zone_apex_contains_rrsig_record_by_key(zdb_zone *zone, const dnssec_key *key)
872 {
873     yassert(zdb_zone_islocked(zone));
874 
875     const zdb_packed_ttlrdata *rrsig_rrset = zdb_record_find(&zone->apex->resource_record_set, TYPE_RRSIG); // zone is locked
876 
877     if(rrsig_rrset != NULL)
878     {
879         const zdb_packed_ttlrdata *rrsig_record = rrsig_rrset;
880         u16 tag = dnskey_get_tag_const(key);
881         u8 algorithm = dnskey_get_algorithm(key);
882 
883         while(rrsig_record != NULL)
884         {
885             if((RRSIG_ALGORITHM(rrsig_record) == algorithm) && (RRSIG_KEY_TAG(rrsig_record) == tag))
886             {
887                 return TRUE;
888             }
889 
890             rrsig_record = rrsig_record->next;
891         }
892     }
893 
894     return FALSE;
895 }
896 
897 #if HAS_MASTER_SUPPORT
898 
899 /**
900  * Adds a DNSKEY record in a zone from the dnssec_key object.
901  *
902  * @param key
903  * @return TRUE iff the record has been added
904  */
905 
906 bool
zdb_zone_add_dnskey_from_key(zdb_zone * zone,const dnssec_key * key)907 zdb_zone_add_dnskey_from_key(zdb_zone *zone, const dnssec_key *key)
908 {
909     yassert(zdb_zone_islocked(zone));
910 
911     zdb_packed_ttlrdata *dnskey_record;
912     u32 rdata_size = key->vtbl->dnssec_key_rdatasize(key);
913     ZDB_RECORD_ZALLOC_EMPTY(dnskey_record, 86400, rdata_size);
914     key->vtbl->dnssec_key_writerdata(key, ZDB_PACKEDRECORD_PTR_RDATAPTR(dnskey_record), rdata_size);
915 
916     // store the record
917 
918     if(zdb_record_insert_checked(&zone->apex->resource_record_set, TYPE_DNSKEY, dnskey_record)) /* FB done */
919     {
920         return TRUE;
921     }
922     else
923     {
924         ZDB_RECORD_ZFREE(dnskey_record);
925 
926         return FALSE;
927     }
928 }
929 
930 /**
931  * Removes a DNSKEY record in a zone from the dnssec_key object.
932  *
933  * @param key
934  * @return TRUE iff the record has been found and removed
935  */
936 
937 
938 bool
zdb_zone_remove_dnskey_from_key(zdb_zone * zone,const dnssec_key * key)939 zdb_zone_remove_dnskey_from_key(zdb_zone *zone, const dnssec_key *key)
940 {
941     yassert(zdb_zone_islocked(zone));
942 
943     zdb_packed_ttlrdata *dnskey_record;
944     u32 rdata_size = key->vtbl->dnssec_key_rdatasize(key);
945     ZDB_RECORD_ZALLOC_EMPTY(dnskey_record, 86400, rdata_size);
946     key->vtbl->dnssec_key_writerdata(key, ZDB_PACKEDRECORD_PTR_RDATAPTR(dnskey_record), rdata_size);
947 
948     zdb_ttlrdata unpacked_dnskey_record;
949     unpacked_dnskey_record.rdata_pointer = ZDB_PACKEDRECORD_PTR_RDATAPTR(dnskey_record);
950     unpacked_dnskey_record.rdata_size = ZDB_PACKEDRECORD_PTR_RDATASIZE(dnskey_record);
951     unpacked_dnskey_record.ttl = dnskey_record->ttl;
952 
953     // remove the record
954 
955     if(zdb_record_delete_self_exact(&zone->apex->resource_record_set, TYPE_DNSKEY, &unpacked_dnskey_record) >= 0)
956     {
957         // remove all RRSIG on DNSKEY
958         rrsig_delete(zone, zone->origin,zone->apex, TYPE_DNSKEY);
959 
960         rrsig_delete_by_tag(zone, dnskey_get_tag_const(key));
961 
962         // zdb_listener_notify_remove_type(zone, zone->origin, &zone->apex->resource_record_set, TYPE_RRSIG);
963 
964         ZDB_RECORD_ZFREE(dnskey_record);
965 
966         return TRUE;
967     }
968     else
969     {
970         ZDB_RECORD_ZFREE(dnskey_record);
971         return FALSE;
972     }
973 }
974 
975 static ya_result
zdb_zone_update_zone_remove_add_dnskeys(zdb_zone * zone,ptr_vector * removed_keys,ptr_vector * added_keys,u8 secondary_lock)976 zdb_zone_update_zone_remove_add_dnskeys(zdb_zone *zone, ptr_vector *removed_keys, ptr_vector *added_keys, u8 secondary_lock)
977 {
978     dynupdate_message dmsg;
979     packet_unpack_reader_data reader;
980     const u8 *fqdn = NULL;
981 
982     if(!ptr_vector_isempty(removed_keys))
983     {
984         dnssec_key *key = (dnssec_key*)ptr_vector_get(removed_keys, 0);
985         yassert(key != NULL);
986         fqdn = dnskey_get_domain(key);
987     }
988     else if(!ptr_vector_isempty(added_keys))
989     {
990         dnssec_key *key = (dnssec_key*)ptr_vector_get(added_keys, 0);
991         yassert(key != NULL);
992         fqdn = dnskey_get_domain(key);
993     }
994     else
995     {
996         return 0;   // EMPTY
997     }
998 
999     ya_result ret;
1000     int add_index = 0;
1001     int del_index = 0;
1002     bool work_to_do = FALSE;
1003 
1004     do
1005     {
1006         dynupdate_message_init(&dmsg, fqdn, CLASS_IN);
1007 
1008         for(; add_index <= ptr_vector_last_index(added_keys); ++add_index)
1009         {
1010             dnssec_key *key = (dnssec_key*)ptr_vector_get(added_keys, add_index);
1011 
1012             if(FAIL(ret = dynupdate_message_add_dnskey(&dmsg, zone->min_ttl, key)))
1013             {
1014                 log_debug("dnskey: %{dnsname}: +%03d+%05d/%d key cannot be sent with this update, postponing",dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key), ntohs(dnskey_get_flags(key)));
1015                 work_to_do = TRUE;
1016                 break;
1017             }
1018 
1019             log_info("dnskey: %{dnsname}: +%03d+%05d/%d key will be added", dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key), ntohs(dnskey_get_flags(key)));
1020         }
1021 
1022         if(!work_to_do)
1023         {
1024             for(; del_index <= ptr_vector_last_index(removed_keys); ++del_index)
1025             {
1026                 dnssec_key *key = (dnssec_key*)ptr_vector_get(removed_keys, del_index);
1027                 if(FAIL(ret = dynupdate_message_del_dnskey(&dmsg, key)))
1028                 {
1029                     log_debug("dnskey: %{dnsname}: +%03d+%05d/%d key cannot be sent with this update, postponing", dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key), ntohs(dnskey_get_flags(key)));
1030                     work_to_do = TRUE;
1031                     break;
1032                 }
1033 
1034                 log_info("dnskey: %{dnsname}: +%03d+%05d/%d key will be removed", dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key), ntohs(dnskey_get_flags(key)));
1035             }
1036         }
1037 
1038         dynupdate_message_set_reader(&dmsg, &reader);
1039         u16 count = dynupdate_message_get_count(&dmsg);
1040 
1041         packet_reader_skip(&reader, DNS_HEADER_LENGTH); // checked below
1042         packet_reader_skip_fqdn(&reader);               // checked below
1043         packet_reader_skip(&reader, 4);             // checked below
1044 
1045         if(!packet_reader_eof(&reader))
1046         {
1047             // the update is ready : push it
1048 
1049             if(ISOK(ret = dynupdate_diff(zone, &reader, count, secondary_lock, DYNUPDATE_DIFF_RUN)))
1050             {
1051                 // done
1052                 log_info("dnskey: %{dnsname}: keys update successful", fqdn);
1053             }
1054 
1055             if(FAIL(ret))
1056             {
1057                 if(ret == ZDB_JOURNAL_MUST_SAFEGUARD_CONTINUITY)
1058                 {
1059                     // trigger a background store of the zone
1060 
1061                     zdb_zone_info_background_store_zone(fqdn);
1062                 }
1063 
1064                 dynupdate_message_finalize(&dmsg);
1065 
1066                 log_err("dnskey: %{dnsname}: keys update failed", fqdn);
1067                 break;
1068             }
1069         }
1070         else
1071         {
1072             log_err("dnskey: %{dnsname}: keys update failed: FORMERR", fqdn);
1073         }
1074 
1075         dynupdate_message_finalize(&dmsg);
1076     }
1077     while(work_to_do);
1078 
1079     return ret;
1080 }
1081 
1082 /**
1083  * From the keystore (files/pkcs12) for that zone
1084  *
1085  * Remove the keys that should not be in the zone anymore.
1086  * Add the keys that should be in the zone.
1087  *
1088  * @param zone
1089  */
1090 
1091 void
zdb_zone_update_keystore_keys_from_zone(zdb_zone * zone,u8 secondary_lock)1092 zdb_zone_update_keystore_keys_from_zone(zdb_zone *zone, u8 secondary_lock)
1093 {
1094     // keystore keys with a publish time that did not expire yet have to be added
1095     // keystore keys with an unpublish time that passed have to be removed
1096     //
1097     // after (and only after) the signature is done, set alarms at all the (relevant) timings of the keys (publish, activate, inactivate, unpublish)
1098 
1099     yassert(zdb_zone_islocked(zone));
1100 
1101     ptr_vector dnskey_add = PTR_VECTOR_EMPTY;
1102     ptr_vector dnskey_del = PTR_VECTOR_EMPTY;
1103 
1104     bool rrsig_push_allowed = zdb_zone_get_rrsig_push_allowed(zone);
1105 
1106     for(int i = 0; ; ++i)
1107     {
1108         dnssec_key *key = dnssec_keystore_acquire_key_from_fqdn_at_index(zone->origin, i);
1109 
1110         if(key == NULL)
1111         {
1112             break;
1113         }
1114 
1115         if(dnskey_is_private(key))
1116         {
1117             time_t now = time(NULL);
1118 
1119             if(dnskey_is_published(key, now) || !dnskey_has_explicit_publish(key))  // if published or no publication set
1120             {
1121                 if(!dnskey_is_expired_now(key))                                         // if the key hasn't expired (includes a test for activation)
1122                 {
1123                     if(!zdb_zone_contains_dnskey_record_for_key(zone, key))         // if the key is not in the zone
1124                     {
1125                         if(!rrsig_push_allowed)
1126                         {
1127                             dnskey_acquire(key);                                        // then add the key in the zone
1128                             ptr_vector_append(&dnskey_add, key);
1129                         }
1130                     }
1131                 } // else there is no point publishing it if it was not already
1132             }
1133         }
1134 
1135         dnskey_release(key);
1136     }
1137 
1138     zdb_packed_ttlrdata *dnskey_rrset = zdb_record_find(&zone->apex->resource_record_set, TYPE_DNSKEY); // zone is locked
1139     zdb_packed_ttlrdata *dnskey_record = dnskey_rrset;
1140     while(dnskey_record != NULL)
1141     {
1142         dnssec_key *key;
1143 
1144         ya_result ret;
1145 
1146         if(ISOK(ret = dnssec_keystore_load_private_key_from_rdata(
1147                 ZDB_PACKEDRECORD_PTR_RDATAPTR(dnskey_record),
1148                 ZDB_PACKEDRECORD_PTR_RDATASIZE(dnskey_record),
1149                 zone->origin,
1150                 &key))) // key properly released
1151         {
1152             if(dnskey_has_explicit_delete(key) && dnskey_is_unpublished(key, time(NULL)))
1153             {
1154                 if(!rrsig_push_allowed)
1155                 {
1156                     // need to unpublish
1157                     dnskey_acquire(key);
1158                     ptr_vector_append(&dnskey_del, key);
1159                 }
1160             }
1161             // note: the key alarms are not set
1162 
1163             dnskey_release(key);
1164         }
1165 
1166         dnskey_record = dnskey_record->next;
1167     }
1168 
1169     if(ptr_vector_size(&dnskey_add) + ptr_vector_size(&dnskey_del) > 0)
1170     {
1171         if(ISOK(zdb_zone_update_zone_remove_add_dnskeys(zone, &dnskey_del, &dnskey_add, secondary_lock)))
1172         {
1173             log_info("zone: %{dnsname}: keys added: %i, keys deleted: %i", zone->origin, ptr_vector_size(&dnskey_add), ptr_vector_size(&dnskey_del));
1174         }
1175         else
1176         {
1177             log_warn("zone: %{dnsname}: failed to update keys (keys meant to be added: %i, keys meant to be deleted: %i)", zone->origin, ptr_vector_size(&dnskey_add), ptr_vector_size(&dnskey_del));
1178         }
1179 
1180         for(s32 i = 0; i <= ptr_vector_last_index(&dnskey_add); ++i)
1181         {
1182             dnskey_release(ptr_vector_get(&dnskey_add, i));
1183         }
1184         ptr_vector_destroy(&dnskey_add);
1185 
1186         for(s32 i = 0; i <= ptr_vector_last_index(&dnskey_del); ++i)
1187         {
1188             dnskey_release(ptr_vector_get(&dnskey_del, i));
1189         }
1190         ptr_vector_destroy(&dnskey_del);
1191     }
1192 }
1193 
1194 #endif // HAS_MASTER_SUPPORT
1195 
1196 #endif
1197 
1198 #if DEBUG
1199 
1200 /**
1201  * DEBUG
1202  */
1203 
1204 void
zdb_zone_print_indented(zdb_zone * zone,output_stream * os,int indent)1205 zdb_zone_print_indented(zdb_zone *zone, output_stream *os, int indent)
1206 {
1207     if(zone == NULL)
1208     {
1209         osformatln(os, "%tz: NULL", indent);
1210         return;
1211     }
1212 
1213     u16 zclass = zdb_zone_getclass(zone);
1214 
1215     osformatln(os, "%tzone@%p(CLASS=%{dnsclass},ORIGIN='%{dnsname}'", indent, (void*)zone, &zclass, zone->origin);
1216     zdb_rr_label_print_indented(zone->apex, os, indent + 1);
1217     osformatln(os, "%t+:", indent);
1218 }
1219 
1220 void
zdb_zone_print(zdb_zone * zone,output_stream * os)1221 zdb_zone_print(zdb_zone *zone, output_stream *os)
1222 {
1223     zdb_zone_print_indented(zone, os, 0);
1224 }
1225 
1226 #endif
1227 
1228 u32
zdb_zone_get_status(zdb_zone * zone)1229 zdb_zone_get_status(zdb_zone *zone)
1230 {
1231     u32 ret = atomic_load(&zone->_status);
1232     return ret;
1233 }
1234 
1235 u32
zdb_zone_set_status(zdb_zone * zone,u32 status)1236 zdb_zone_set_status(zdb_zone *zone, u32 status)
1237 {
1238 #if DEBUG
1239     log_debug4("zdb_zone_set_status(%{dnsname},%08x)", zone->origin, status);
1240 #endif
1241 
1242     for(;;)
1243     {
1244         uint_fast32_t expected = atomic_load(&zone->_status);
1245         if((expected & status) != status)
1246         {
1247             uint_fast32_t desired = expected | status;
1248             if(atomic_compare_exchange_strong(&zone->_status, &expected, desired))
1249             {
1250                 return expected;    // old value
1251             }
1252 
1253             // the update failed, try a gain
1254         }
1255         else
1256         {
1257             return expected;    // no work to do
1258         }
1259     }
1260 }
1261 
1262 u32
zdb_zone_clear_status(zdb_zone * zone,u32 status)1263 zdb_zone_clear_status(zdb_zone *zone, u32 status)
1264 {
1265 #if DEBUG
1266     log_debug4("zdb_zone_clear_status(%{dnsname},%08x)", zone->origin, status);
1267 #endif
1268 
1269     uint_fast32_t not_status = ~status;
1270 
1271     for(;;)
1272     {
1273         uint_fast32_t expected = atomic_load(&zone->_status);
1274         if((expected & status) != 0)
1275         {
1276             uint_fast32_t desired = expected & not_status;
1277             if(atomic_compare_exchange_strong(&zone->_status, &expected, desired))
1278             {
1279                 return expected;    // old value
1280             }
1281 
1282             // the update failed, try a gain
1283         }
1284         else
1285         {
1286             return expected;    // no work to do
1287         }
1288     }
1289 }
1290 
1291 bool
zdb_zone_error_status_getnot_set(zdb_zone * zone,u8 error_status)1292 zdb_zone_error_status_getnot_set(zdb_zone *zone, u8 error_status)
1293 {
1294     bool ret = (zone->_error_status & error_status) == 0;
1295     zone->_error_status &= ~error_status;
1296     return ret;
1297 }
1298 
1299 void
zdb_zone_error_status_clear(zdb_zone * zone,u8 error_status)1300 zdb_zone_error_status_clear(zdb_zone *zone, u8 error_status)
1301 {
1302     zone->_error_status &= ~error_status;
1303 }
1304 
1305 /** @} */
1306