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