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 dnsdbupdate Dynamic update functions
36  *  @ingroup dnsdb
37  *  @brief
38  *
39  * @{
40  */
41 /*------------------------------------------------------------------------------
42  *
43  * USE INCLUDES */
44 #include "dnsdb/dnsdb-config.h"
45 #include <stdio.h>
46 #include <stdlib.h>
47 
48 #include <dnscore/rfc.h>
49 #include <dnscore/logger.h>
50 #include <dnscore/dnsname.h>
51 #include <dnscore/digest.h>
52 
53 #include <dnscore/dnskey-signature.h>
54 
55 #include "dnsdb/zdb_types.h"
56 #include "dnsdb/nsec.h"
57 #include "dnsdb/nsec3.h"
58 
59 #include <dnscore/base32hex.h>
60 #include <dnscore/format.h>
61 #include <dnscore/bytearray_output_stream.h>
62 #include <dnscore/bytearray_input_stream.h>
63 #include <dnsdb/zdb-zone-maintenance.h>
64 
65 #include "dnsdb/dnssec.h"
66 #include "dnsdb/zdb_zone.h"
67 #include "dnsdb/dnssec-keystore.h"
68 #include "dnsdb/zdb_utils.h"
69 #include "dnsdb/dynupdate-diff.h"
70 #include "dnsdb/zdb-zone-path-provider.h"
71 #include "dnsdb/zdb_icmtl.h"
72 #if HAS_NSEC3_SUPPORT
73 #include "dnsdb/nsec3.h"
74 #endif
75 
76 #define ZDB_JOURNAL_CODE 1
77 #include "dnsdb/journal.h"
78 
79 #define MODULE_MSG_HANDLE g_database_logger
80 extern logger_handle *g_database_logger;
81 
82 // Disable detailed diff log even in debug builds
83 
84 #define DYNUPDATE_DIFF_DETAILED_LOG 0
85 
86 #ifndef DYNUPDATE_DIFF_DETAILED_LOG
87 #if DEBUG
88 #define DYNUPDATE_DIFF_DETAILED_LOG 1
89 #else
90 #define DYNUPDATE_DIFF_DETAILED_LOG 0
91 #endif
92 #endif
93 
94 #if DYNUPDATE_DIFF_DETAILED_LOG
95 #pragma message("WARNING: DYNUPDATE_DIFF_DETAILED_LOG is not set to 0")
96 #endif
97 
98 #define DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG 0
99 
100 #ifndef DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
101 #if DEBUG
102 #define DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG 1
103 #else
104 #define DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG 0
105 #endif
106 #endif
107 
108 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
109 #pragma message("WARNING: DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG is not set to 0")
110 #endif
111 
112 ///////////////////////////////////////////////////////////////////////////////
113 
114 static char zone_diff_record_state_format_letters[6] = {'+','-','O','V','E','A'};
115 
116 void
zone_diff_record_state_format(const void * data,output_stream * os,s32 a,char b,bool c,void * reserved_for_method_parameters)117 zone_diff_record_state_format(const void* data, output_stream* os, s32 a, char b , bool c, void* reserved_for_method_parameters)
118 {
119     (void)a;
120     (void)b;
121     (void)c;
122     (void)reserved_for_method_parameters;
123 
124     u8 state = *((u8*)data);
125     for(u32 i = 0; i < sizeof(zone_diff_record_state_format_letters); ++i)
126     {
127         char c = ((state & (1 << i)) != 0)?zone_diff_record_state_format_letters[i]:'_';
128         output_stream_write(os, &c, 1);
129     }
130 }
131 
132 #if DEBUG
133 static char zone_diff_chain_state_format_letters[8] = {'+','-',' ','r','E','{','}','!'};
134 
135 static void
zone_diff_chain_state_format(const void * data,output_stream * os,s32 a,char b,bool c,void * reserved_for_method_parameters)136 zone_diff_chain_state_format(const void* data, output_stream* os, s32 a, char b , bool c, void* reserved_for_method_parameters)
137 {
138     (void)a;
139     (void)b;
140     (void)c;
141     (void)reserved_for_method_parameters;
142 
143     u8 state = *((u8*)data);
144     for(u32 i = 0; i < sizeof(zone_diff_chain_state_format_letters); ++i)
145     {
146         char c = ((state & (1 << i)) != 0)?zone_diff_chain_state_format_letters[i]:'_';
147         output_stream_write(os, &c, 1);
148     }
149 }
150 #endif
151 
152 static void
zone_diff_fqdn_changes_format(const void * data,output_stream * os,s32 a,char b,bool c,void * reserved_for_method_parameters)153 zone_diff_fqdn_changes_format(const void* data, output_stream* os, s32 a, char b , bool c, void* reserved_for_method_parameters)
154 {
155     (void)a;
156     (void)b;
157     (void)c;
158     (void)reserved_for_method_parameters;
159 
160     zone_diff_fqdn *diff = (zone_diff_fqdn*)data;
161 
162     if(diff->type_map_changed) output_stream_write(os, "MAP ", 4);
163     if(diff->all_rrset_added) output_stream_write(os, "+ALL ", 5);
164     if(diff->all_rrset_removed) output_stream_write(os, "-ALL ", 5);
165     if(diff->is_apex) output_stream_write(os, "APEX ", 5);
166 
167     output_stream_write(os, "AT(", 3);
168     output_stream_write_u8(os, diff->was_at_delegation?'1':'0');
169     output_stream_write(os, "->", 2);
170     output_stream_write_u8(os, diff->at_delegation?'1':'0');
171     output_stream_write(os, ") ", 2);
172 
173     output_stream_write(os, "UNDER(", 6);
174     output_stream_write_u8(os, diff->was_under_delegation?'1':'0');
175     output_stream_write(os, "->", 2);
176     output_stream_write_u8(os, diff->under_delegation?'1':'0');
177     output_stream_write(os, ") ", 2);
178 
179     output_stream_write(os, "DS(", 3);
180     output_stream_write_u8(os, diff->had_ds?'1':'0');
181     output_stream_write(os, "->", 2);
182     output_stream_write_u8(os, diff->will_have_ds?'1':'0');
183     output_stream_write(os, ") ", 2);
184 
185     output_stream_write(os, "CHILDREN(", 9);
186     output_stream_write_u8(os, diff->had_children?'1':'0');
187     output_stream_write(os, "->", 2);
188     output_stream_write_u8(os, diff->will_have_children?'1':'0');
189     output_stream_write(os, ") ", 2);
190 
191     output_stream_write(os, "RECORDS(", 8);
192     output_stream_write_u8(os, diff->was_non_empty?'1':'0');
193     output_stream_write(os, "->", 2);
194     output_stream_write_u8(os, diff->will_be_non_empty?'1':'0');
195     output_stream_write(os, ") ", 2);
196 }
197 
198 static const u8 *
zone_diff_label_rr_rrv_get_fqdn(void * data,const void * p)199 zone_diff_label_rr_rrv_get_fqdn(void *data, const void* p)
200 {
201     (void)data;
202     zone_diff_label_rr *rr = (zone_diff_label_rr*)p;
203     return rr->fqdn;
204 }
205 
206 static u16
zone_diff_label_rr_rrv_get_type(void * data,const void * p)207 zone_diff_label_rr_rrv_get_type(void *data, const void* p)
208 {
209     /*
210     zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)data;
211     (void)p;
212     return rrset->rtype;
213     */
214     (void)data;
215     zone_diff_label_rr *rr = (zone_diff_label_rr*)p;
216     return rr->rtype;
217 }
218 
219 static u16
zone_diff_label_rr_rrv_get_class(void * data,const void * p)220 zone_diff_label_rr_rrv_get_class(void *data, const void* p)
221 {
222     /*
223     zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)data;
224     (void)p;
225     return rrset->rclass;
226     */
227     (void)data;
228     zone_diff_label_rr *rr = (zone_diff_label_rr*)p;
229     return rr->rclass;
230 }
231 
232 static s32
zone_diff_label_rr_rrv_get_ttl(void * data,const void * p)233 zone_diff_label_rr_rrv_get_ttl(void *data, const void* p)
234 {
235     /*
236     zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)data;
237     (void)p;
238     return rrset->new_ttl;
239     */
240     (void)data;
241     zone_diff_label_rr *rr = (zone_diff_label_rr*)p;
242     return rr->ttl;
243 }
244 
245 static u16
zone_diff_label_rr_rrv_get_rdata_size(void * data,const void * p)246 zone_diff_label_rr_rrv_get_rdata_size(void *data, const void* p)
247 {
248     (void)data;
249     zone_diff_label_rr *rr = (zone_diff_label_rr*)p;
250     return rr->rdata_size;
251 }
252 
253 static const u8 *
zone_diff_label_rr_rrv_get_rdata(void * data,const void * p)254 zone_diff_label_rr_rrv_get_rdata(void *data, const void* p)
255 {
256     (void)data;
257     zone_diff_label_rr *rr = (zone_diff_label_rr*)p;
258     return (const u8*)rr->rdata;
259 }
260 
261 static void *
zone_diff_label_rr_rrv_new_instance(void * data,const u8 * fqdn,u16 rtype,u16 rclass,s32 ttl,u16 rdata_size,const u8 * rdata)262 zone_diff_label_rr_rrv_new_instance(void *data, const u8 *fqdn, u16 rtype, u16 rclass, s32 ttl, u16 rdata_size, const u8 *rdata)
263 {
264     (void)data;
265     zone_diff_label_rr *rr = zone_diff_label_rr_new(fqdn, rtype, rclass, ttl, (void*)rdata, rdata_size, TRUE);
266     return rr;
267 }
268 
269 static const struct resource_record_view_vtbl zone_diff_label_rr_rrv_vtbl =
270 {
271     zone_diff_label_rr_rrv_get_fqdn,
272     zone_diff_label_rr_rrv_get_type,
273     zone_diff_label_rr_rrv_get_class,
274     zone_diff_label_rr_rrv_get_ttl,
275     zone_diff_label_rr_rrv_get_rdata_size,
276     zone_diff_label_rr_rrv_get_rdata,
277     zone_diff_label_rr_rrv_new_instance
278 };
279 
280 ///////////////////////////////////////////////////////////////////////////////
281 
282 /**
283  * Initialises a dnssec chain (editor).
284  * NSEC and NSEC3 chains cannot be mixed.
285  * The actual chain must be set using dnssec_chain_add_chain
286  *
287  * @param dc
288  * @param chain_functions
289  */
290 
dnssec_chain_init(dnssec_chain * dc,const dnssec_chain_node_vtbl * chain_functions,zone_diff * diff)291 void dnssec_chain_init(dnssec_chain *dc, const dnssec_chain_node_vtbl *chain_functions, zone_diff *diff)
292 {
293     dc->diff = diff;
294     ptr_set_init(&dc->chain_diff);
295     dc->chain_diff.compare = chain_functions->compare;
296     dc->chain = chain_functions;
297     dc->chains_count = 0;
298 }
299 
300 /**
301  * Adds a chain to the chain editor.
302  *
303  * NSEC3: every nsec3_zone* of the zone (one at a time).
304  * NSEC: the nsec_zone of the zone.
305  *
306  * @param dc
307  * @param chain
308  */
309 
dnssec_chain_add_chain(dnssec_chain * dc,dnssec_chain_head_t chain,bool being_deleted)310 void dnssec_chain_add_chain(dnssec_chain *dc, dnssec_chain_head_t chain, bool being_deleted)
311 {
312     if(dc->chains_count < DNSSEC_CHAIN_SUPPORTED_MAX)
313     {
314         dc->chains[dc->chains_count] = chain;
315         dc->chain_being_deleted[dc->chains_count] = being_deleted;
316         ++dc->chains_count;
317     }
318 }
319 
dnssec_chain_add_node(dnssec_chain * dc,const u8 * fqdn,u16 rtype,u8 asked_or_mask)320 static void dnssec_chain_add_node(dnssec_chain *dc, const u8 *fqdn, u16 rtype, u8 asked_or_mask)
321 {
322     // compute the hash
323     // find the prev & next in the current set
324     // store a node with "prev new next"
325     // store a node with "prev" marked as begin (if !E)
326     // store a node with "next" marked as end (if !E)
327 
328     (void)rtype;
329 
330     for(int chain_index = 0; chain_index < dc->chains_count; ++chain_index)
331     {
332         void *chain = dc->chains[chain_index];
333 
334         // need to know if it's under delegation
335 
336         //
337 #if DEBUG
338         log_debug("NEW NODE %{dnsname} (0)", fqdn);
339 #endif
340         void *chain_node = dc->chain->node_new(fqdn, chain);
341 
342         ptr_node *node = ptr_set_insert(&dc->chain_diff, chain_node);
343 
344         // if chain is not empty, edit it, else create it with one node
345 
346         if(!dc->chain->isempty(chain))
347         {
348             u8 or_mask = (!dc->chain_being_deleted[chain_index])?asked_or_mask:DNSSEC_CHAIN_DELETE;
349 
350             if(node->value == NULL)
351             {
352                 node->value = chain_node;
353 
354                 // create a node for the prev & next
355 
356                 void *chain_begin = dc->chain->node_prev(chain_node);
357 
358                 // zone_diff_add_fqdn(dc->diff, node->fqdn, rr_label);
359 
360                 yassert(chain_begin != NULL);
361                 ptr_node *node_prev = ptr_set_insert(&dc->chain_diff, chain_begin);
362                 if(node_prev->value == NULL)
363                 {
364                     node_prev->value = chain_begin;
365                 }
366                 else
367                 {
368                     dc->chain->node_merge(node_prev->value, chain_begin);
369                 }
370 
371                 void *chain_end = dc->chain->node_next(chain_node);
372                 yassert(chain_end != NULL);
373                 ptr_node *node_next = ptr_set_insert(&dc->chain_diff, chain_end);
374                 if(node_next->value == NULL)
375                 {
376                     node_next->value = chain_end;
377                 }
378                 else
379                 {
380                     dc->chain->node_merge(node_next->value, chain_end);
381                 }
382             }
383             else
384             {
385                 // node exists already ...
386 
387                 dc->chain->state_set(node->value, dc->chain->state_get(node->value) & ~(DNSSEC_CHAIN_BEGIN|DNSSEC_CHAIN_END));
388 
389                 dc->chain->node_delete(chain_node);
390             }
391 
392             if(or_mask != 0)
393             {
394                 dc->chain->state_set(node->value, dc->chain->state_get(node->value) | or_mask);
395             }
396         }
397         else
398         {
399             // instead of the doing diff computations the chain will be fully created
400             node->value = chain_node;
401         }
402     }
403 }
404 
dnssec_chain_add_node_neighbours(dnssec_chain * dc,const zone_diff_fqdn * diff_fqdn,void * chain_node,int chain_index)405 static void dnssec_chain_add_node_neighbours(dnssec_chain *dc, const zone_diff_fqdn *diff_fqdn, void *chain_node, int chain_index)
406 {
407 
408 
409     (void)diff_fqdn;
410     (void)chain_index;
411 
412     void *chain_begin = dc->chain->node_prev(chain_node);
413     yassert(chain_begin != NULL);
414 #if DEBUG
415     format_writer chain_node_prev_fw;
416     dc->chain->format_writer_init(chain_begin, &chain_node_prev_fw);
417 #endif
418     ptr_node *node_prev = ptr_set_insert(&dc->chain_diff, chain_begin);
419     if(node_prev->value == NULL)
420     {
421         node_prev->value = chain_begin;
422 #if DEBUG
423         log_debug2("dnssec-chain: %{dnsname}: chain[%i]: previous node is %w", diff_fqdn->fqdn, chain_index, &chain_node_prev_fw);
424 #endif
425     }
426     else
427     {
428 #if DEBUG
429         log_debug2("dnssec-chain: %{dnsname}: chain[%i]: previous node %w already in chain, merging", diff_fqdn->fqdn, chain_index, &chain_node_prev_fw);
430 #endif
431         dc->chain->node_merge(node_prev->value, chain_begin);
432 #if DEBUG
433         dc->chain->format_writer_init(node_prev->value, &chain_node_prev_fw);
434         log_debug2("dnssec-chain: %{dnsname}: chain[%i]: previous node %w merged", diff_fqdn->fqdn, chain_index, &chain_node_prev_fw);
435 #endif
436     }
437 
438     void *chain_end = dc->chain->node_next(chain_node);
439     yassert(chain_end != NULL);
440 #if DEBUG
441     format_writer chain_node_next_fw;
442     dc->chain->format_writer_init(chain_end, &chain_node_next_fw);
443 #endif
444     ptr_node *node_next = ptr_set_insert(&dc->chain_diff, chain_end);
445     if(node_next->value == NULL)
446     {
447 #if DEBUG
448         log_debug2("dnssec-chain: %{dnsname}: chain[%i]: next node is %w", diff_fqdn->fqdn, chain_index, &chain_node_next_fw);
449 #endif
450         node_next->value = chain_end;
451     }
452     else
453     {
454 #if DEBUG
455         log_debug2("dnssec-chain: %{dnsname}: chain[%i]: next node %w already in chain, merging", diff_fqdn->fqdn, chain_index, &chain_node_next_fw);
456 #endif
457         dc->chain->node_merge(node_next->value, chain_end);
458 #if DEBUG
459         dc->chain->format_writer_init(node_next->value, &chain_node_next_fw);
460         log_debug2("dnssec-chain: %{dnsname}: chain[%i]: next node %w merged", diff_fqdn->fqdn, chain_index, &chain_node_next_fw);
461 #endif
462     }
463 }
464 
465 static int
dnssec_chain_add_node_from_diff_fqdn(dnssec_chain * dc,const zone_diff_fqdn * diff_fqdn,u16 rtype,u8 asked_or_mask)466 dnssec_chain_add_node_from_diff_fqdn(dnssec_chain *dc, const zone_diff_fqdn *diff_fqdn, u16 rtype, u8 asked_or_mask)
467 {
468     int ret = 0;
469     // compute the hash
470     // find the prev & next in the current set
471     // store a node with "prev new next"
472     // store a node with "prev" marked as begin (if !E)
473     // store a node with "next" marked as end (if !E)
474 
475     (void)rtype;
476 
477     for(int chain_index = 0; chain_index < dc->chains_count; ++chain_index)
478     {
479         void *chain = dc->chains[chain_index];
480 
481         // need to know if it's under delegation
482 
483         if(asked_or_mask & DNSSEC_CHAIN_DELETE)
484         {
485             // IT HAD TO EXIST FIRST!
486             if(!dc->chain->fqdn_was_covered(diff_fqdn))
487             {
488 #if DEBUG
489                 log_debug2("dnssec-chain: %{dnsname}: chain[%i]: did not cover", diff_fqdn->fqdn, chain_index);
490 #endif
491                 continue;
492             }
493 
494         }
495         else
496         {
497             if(!dc->chain->fqdn_is_covered(diff_fqdn))
498             {
499 #if DEBUG
500                 log_debug2("dnssec-chain: %{dnsname}: chain[%i]: does not covers", diff_fqdn->fqdn, chain_index);
501 #endif
502                 continue;
503             }
504         }
505 
506 #if DEBUG
507         log_debug2("dnssec-chain: %{dnsname}: chain[%i]: covers", diff_fqdn->fqdn, chain_index);
508 #endif
509 
510         //
511 
512 #if DEBUG
513         log_debug3("NEW NODE %{dnsname} (1)", diff_fqdn->fqdn);
514 #endif
515 
516         void *chain_node = dc->chain->node_new(diff_fqdn->fqdn, chain);
517 
518 #if DEBUG
519         format_writer chain_node_fw;
520         dc->chain->format_writer_init(chain_node, &chain_node_fw);
521         log_debug2("dnssec-chain: %{dnsname}: chain[%i]: node is %w", diff_fqdn->fqdn, chain_index, &chain_node_fw);
522 #endif
523 
524         ptr_node *node = ptr_set_insert(&dc->chain_diff, chain_node);
525 
526         if(!dc->chain->isempty(chain))
527         {
528             u8 or_mask = (!dc->chain_being_deleted[chain_index])?asked_or_mask:DNSSEC_CHAIN_DELETE;
529 
530             if(node->value == NULL)
531             {
532 #if DEBUG
533                 log_debug2("dnssec-chain: %{dnsname}: chain[%i]: node %w is new, getting both neighbours", diff_fqdn->fqdn, chain_index, &chain_node_fw);
534 #endif
535                 node->value = chain_node;
536 
537                 // create a node for the prev & next
538 
539                 dnssec_chain_add_node_neighbours(dc, diff_fqdn, chain_node, chain_index);
540             }
541             else
542             {
543 #if DEBUG
544                 log_debug2("dnssec-chain: %{dnsname}: chain[%i]: node %w already exists", diff_fqdn->fqdn, chain_index, &chain_node_fw);
545 #endif
546                 // node exists already ...
547                 dnssec_chain_add_node_neighbours(dc, diff_fqdn, chain_node, chain_index);
548                 dc->chain->node_merge(node->value, chain_node);
549                 dc->chain->state_set(node->value, dc->chain->state_get(node->value) & ~(DNSSEC_CHAIN_BEGIN|DNSSEC_CHAIN_END));
550             }
551 
552             // check if any of the RRSET of the label have been added or removed
553 
554             //
555             u8 prev_state = dc->chain->state_get(node->value);
556 
557             if(prev_state & DNSSEC_CHAIN_EXISTS)
558             {
559                 bool type_map_changed = zone_diff_fqdn_type_map_changed(diff_fqdn);
560 
561                 if(type_map_changed)
562                 {
563                     or_mask |= DNSSEC_CHAIN_REMAP;
564                 }
565             }
566 
567             if(or_mask != 0)
568             {
569                 dc->chain->state_set(node->value, prev_state | or_mask);
570             }
571             if(((prev_state & DNSSEC_CHAIN_EXISTS) == 0) || ((or_mask & (DNSSEC_CHAIN_DELETE|DNSSEC_CHAIN_REMAP)) != 0))
572             {
573                 ++ret;
574             }
575         }
576         else
577         {
578 #if DEBUG
579             log_debug("dnssec-chain: %{dnsname}: chain[%i] was empty", diff_fqdn->fqdn, chain_index);
580 #endif
581             // instead of the doing diff computations the chain will be fully created
582 
583             if(node->value != NULL)
584             {
585 #if DEBUG
586                 log_debug("dnssec-chain: %{dnsname}: chain[%i]: node %w already exists", diff_fqdn->fqdn, chain_index, &chain_node_fw);
587 #endif
588                 // node exists already ...
589                 assert(dc->chain->compare(node->value, chain_node) == 0);
590 
591                 dc->chain->node_merge(node->value, chain_node);
592                 dc->chain->state_set(node->value, dc->chain->state_get(node->value) & ~(DNSSEC_CHAIN_BEGIN|DNSSEC_CHAIN_END));
593             }
594             else
595             {
596                 node->value = chain_node;
597             }
598 
599             ++ret;
600         }
601     }
602 
603     return ret;
604 }
605 
606 /**
607  * Adds a node to the chain.
608  *
609  * @param dc
610  * @param fqdn
611  * @param rtype
612  */
613 
dnssec_chain_add(dnssec_chain * dc,const u8 * fqdn,u16 rtype)614 void dnssec_chain_add(dnssec_chain *dc, const u8 *fqdn, u16 rtype)
615 {
616     dnssec_chain_add_node(dc, fqdn, rtype, DNSSEC_CHAIN_ADD);
617     // It used to be :
618     // dnssec_chain_add_node(dc, fqdn, rtype, 0);
619 }
620 
dnssec_chain_add_from_diff_fqdn(dnssec_chain * dc,const zone_diff_fqdn * diff_fqdn,u16 rtype)621 int dnssec_chain_add_from_diff_fqdn(dnssec_chain *dc, const zone_diff_fqdn* diff_fqdn, u16 rtype)
622 {
623     int ret = dnssec_chain_add_node_from_diff_fqdn(dc, diff_fqdn, rtype, DNSSEC_CHAIN_ADD);
624     return ret;
625 }
626 
627 /**
628  * Removes a node from the chain.
629  *
630  * @param dc
631  * @param fqdn
632  * @param rtype
633  */
634 
dnssec_chain_del(dnssec_chain * dc,const u8 * fqdn,u16 rtype)635 void dnssec_chain_del(dnssec_chain *dc, const u8 *fqdn, u16 rtype)
636 {
637     dnssec_chain_add_node(dc, fqdn, rtype, DNSSEC_CHAIN_DELETE);
638 }
639 
dnssec_chain_del_from_diff_fqdn(dnssec_chain * dc,const zone_diff_fqdn * diff_fqdn,u16 rtype)640 int dnssec_chain_del_from_diff_fqdn(dnssec_chain *dc, const zone_diff_fqdn* diff_fqdn, u16 rtype)
641 {
642     int ret = dnssec_chain_add_node_from_diff_fqdn(dc, diff_fqdn, rtype, DNSSEC_CHAIN_DELETE);
643     return ret;
644 }
645 
dnssec_chain_store_diff_publish_chain_node(dnssec_chain * dc,zone_diff * diff,ptr_vector * keys,void * chain,void * prev,void * prev_next,ptr_vector * add)646 static void dnssec_chain_store_diff_publish_chain_node(dnssec_chain *dc, zone_diff *diff, ptr_vector *keys,
647         void *chain, void *prev, void *prev_next, ptr_vector *add)
648 {
649     ya_result ret;
650     s32 from_offset = ptr_vector_size(add);
651 
652     dc->chain->publish_add(chain, prev, prev_next, diff, add);
653 
654     // and its signature(s)
655 
656     s32 to_offset = ptr_vector_size(add);
657     // make a ptr_vector that's a view of the last added records
658     ptr_vector rrset = {&add->data[from_offset], 0, to_offset - from_offset};
659 
660     struct resource_record_view rrv = {NULL, &zone_diff_label_rr_rrv_vtbl};
661     u16 rrset_type = TYPE_NONE;
662     for(int i = 0; i <= ptr_vector_last_index(&rrset); ++i)
663     {
664         void* data = ptr_vector_get(&rrset, i);
665         const void *fqdn = rrv.vtbl->get_fqdn(rrv.data, data);
666         u16 rtype = rrv.vtbl->get_type(rrv.data, data);
667         u16 rclass = rrv.vtbl->get_class(rrv.data, data);
668         s32 ttl = rrv.vtbl->get_ttl(rrv.data, data);
669         u16 rdata_size = rrv.vtbl->get_rdata_size(rrv.data, data);
670         const void *rdata = rrv.vtbl->get_rdata(rrv.data, data);
671 
672         rrset_type = rtype;
673 
674         rdata_desc rdt = {rtype, rdata_size, rdata};
675         log_debug("update: %{dnsname}: will sign chain record #%i: %{dnsname} %i %{dnsclass} %{typerdatadesc}",
676                 diff->origin, i, fqdn, ttl, &rclass, &rdt);
677     }
678 
679     bool canonize = TRUE;
680 
681     for(int j = 0; j <= ptr_vector_last_index(keys); ++j)
682     {
683         const dnssec_key *key = (dnssec_key*)ptr_vector_get(keys, j);
684 
685         zone_diff_label_rr *rrsig_rr = NULL;
686 
687         s32 maxinterval = diff_generate_signature_interval(diff);
688 
689         // rrset_to_sign;
690         if(ISOK(ret = dnskey_sign_rrset_with_maxinterval(key, &rrset, canonize, &rrv, maxinterval, (void **) &rrsig_rr)))
691         {
692             canonize = FALSE;
693 
694             // add the key to the add set
695 
696             rdata_desc rdt = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
697             log_debug("update: %{dnsname}: signed chain rrset %{dnstype} with key %03d %05d: %{dnsname} %i %{dnsclass} %{typerdatadesc}",
698                     diff->origin, &rrset_type, dnskey_get_algorithm(key), dnskey_get_tag_const(key),
699                     rrsig_rr->fqdn, rrsig_rr->ttl, &rrsig_rr->rclass, &rdt
700                     );
701 
702             rrsig_rr->state |= ZONE_DIFF_RR_VOLATILE;
703             ptr_vector_append(add, rrsig_rr);
704 
705             // since we are mapping inside the array and the array could have been replaced by a bigger one ...
706             rrset.data = &add->data[from_offset];
707         }
708 #if DEBUG
709         else
710         {
711             log_debug("update: %{dnsname}: did not sign rrset %{dnstype} with key %03d %05d: %r",
712                 diff->origin, &rrset_type, dnskey_get_algorithm(key), dnskey_get_tag_const(key), ret);
713         }
714 #endif
715     }
716 }
717 
718 /**
719  * Computes the changes of the chain into a del and an add records vector.
720  *
721  * @param diff
722  * @param origin
723  * @param nttl
724  */
725 
dnssec_chain_store_diff(dnssec_chain * dc,zone_diff * diff,ptr_vector * keys,ptr_vector * del,ptr_vector * add)726 void dnssec_chain_store_diff(dnssec_chain *dc, zone_diff *diff, ptr_vector *keys, ptr_vector *del, ptr_vector *add)
727 {
728     // simplify then apply the changes
729 
730     // put all the nodes in an array
731 
732     ptr_vector nodes;
733 
734     ptr_vector_init(&nodes);
735 
736     // for every chain
737 
738     for(int chain_index = 0; chain_index < dc->chains_count; ++chain_index)
739     {
740         void *chain = dc->chains[chain_index];
741 
742         ptr_vector_clear(&nodes);
743 
744         // gather all the nodes in the chain in an array
745         // they are inserted in sorted order (ptr_set_iterator does this)
746 
747         ptr_set_iterator iter;
748         ptr_set_iterator_init(&dc->chain_diff, &iter);
749         while(ptr_set_iterator_hasnext(&iter))
750         {
751             ptr_node *node = ptr_set_iterator_next_node(&iter);
752             yassert(node->value != NULL);
753             ptr_vector_append(&nodes, node->value);
754         }
755 
756         // look in a circular pattern for all the nodes that have the "delete" status
757 
758         log_debug("update: %{dnsname}: %i nodes in dnssec chain #%i", diff->origin, ptr_vector_size(&nodes), chain_index);
759 
760         if(ptr_vector_size(&nodes) == 0)
761         {
762             continue;
763         }
764 
765 #if DEBUG
766         for(int i = 0; i <= ptr_vector_last_index(&nodes); ++i)
767         {
768             void *node = ptr_vector_get_mod(&nodes, i);
769             void *next = (i < ptr_vector_last_index(&nodes))?ptr_vector_get_mod(&nodes, i + 1) : NULL;
770             u8 state = dc->chain->state_get(node);
771 
772             format_writer temp_fw_0 = {zone_diff_chain_state_format, &state};
773             log_debug1("update: %{dnsname}: %3i: %02x %w", diff->origin, i, state, &temp_fw_0);
774             dc->chain->publish_log(node, next);
775         }
776 #endif
777 
778         int first_begin = -1; // the first chain node at the begin of a change
779         int last_end;
780 
781         bool whole_chain = FALSE; // does the operation covers the whole chain
782 
783         if(!dc->chain->isempty(chain))
784         {
785             // chain is not empty but may be too small (1 item)
786 
787             if(ptr_vector_last_index(&nodes) > 0) // if true, then it has more than one item
788             {
789                 int exists = 0;
790                 int begin = 0;
791                 int end = 0;
792                 int both = 0;
793 
794                 bool prev_does_not_alter_the_chain = FALSE;
795 
796                 {
797                     void *node = ptr_vector_last(&nodes);
798                     u8 state = dc->chain->state_get(node);
799 
800                     if(state & DNSSEC_CHAIN_EXISTS)
801                     {
802                         ++exists;
803                         // if the node exists and is not deleted
804                         prev_does_not_alter_the_chain = ((state & (DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_DELETE)) != DNSSEC_CHAIN_DELETE);
805                     }
806                     else // the node did not exist (and thus will be added, as there is no other reason being here)
807                     {
808                         prev_does_not_alter_the_chain = FALSE;
809                     }
810                 }
811 
812                 // this loop marks nodes with the next field changed
813 
814                 for(int i = 0; i <= ptr_vector_last_index(&nodes); ++i)
815                 {
816                     void *node = ptr_vector_get(&nodes, i);
817                     u8 state = dc->chain->state_get(node);
818 
819                     if(state & DNSSEC_CHAIN_BEGIN) // the node exists already in the chain and is the start of an update
820                     {
821                         first_begin = i;
822                         ++begin;
823                     }
824 
825                     if(state & DNSSEC_CHAIN_END) // the node exists already in the chain and is the end of an update
826                     {
827                         ++end;
828                         if(state & DNSSEC_CHAIN_BEGIN) // if it's also the start of an update, some merging will happen
829                         {
830                             ++both;
831                         }
832                     }
833 
834                     bool does_not_alter_the_chain; // as in : the label is not new and is not deleted
835 
836                     if(state & DNSSEC_CHAIN_EXISTS)
837                     {
838                         ++exists;
839                         // if the node exists and is not deleted
840                         does_not_alter_the_chain = ((state & (DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_DELETE)) != DNSSEC_CHAIN_DELETE);
841                     }
842                     else // the node did not exist (and thus will be added, as there is no other reason being here)
843                     {
844                         does_not_alter_the_chain = FALSE;
845                     }
846 
847                     if(!does_not_alter_the_chain && prev_does_not_alter_the_chain) // since this one is added and not the previous one, the previous one has to be
848                     {                                          // updated
849                         void *prev_node = ptr_vector_get_mod(&nodes, i - 1);
850                         u8 prev_state = dc->chain->state_get(prev_node);
851                         dc->chain->state_set(prev_node, prev_state | (DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_DELETE));
852                     }
853 
854                     prev_does_not_alter_the_chain = does_not_alter_the_chain;
855                 }
856 
857                 int chain_loops = 0;
858 
859                 if(begin + end == 0)
860                 {
861                     // the chain is looping on itself, take the first exist and mark it as begin & end
862 
863                     for(int i = 0; i <= ptr_vector_last_index(&nodes); ++i)
864                     {
865                         void *node = ptr_vector_get(&nodes, i);
866                         u8 state = dc->chain->state_get(node);
867                         u8 masked_state = state & (DNSSEC_CHAIN_EXISTS|DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_DELETE);
868                         if((masked_state == DNSSEC_CHAIN_EXISTS) ||
869                            (masked_state == (DNSSEC_CHAIN_EXISTS | DNSSEC_CHAIN_ADD)) ||
870                            (masked_state == (DNSSEC_CHAIN_EXISTS | DNSSEC_CHAIN_ADD | DNSSEC_CHAIN_DELETE)))
871                         {
872                             dc->chain->state_set(node, state | (DNSSEC_CHAIN_BEGIN|DNSSEC_CHAIN_END));
873                             first_begin = i;
874                             chain_loops = 1;
875                             break;
876                         }
877                     }
878                 }
879                 else if((begin == 1) && (end == 1) && (both == 1))
880                 {
881                     whole_chain = TRUE;
882                 }
883 
884                 yassert(first_begin >= 0);
885 
886                 last_end = first_begin + ptr_vector_last_index(&nodes) + chain_loops;
887             }
888             else // there is only one item in the chain update
889             {
890                 log_debug("update: %{dnsname}: chain #%i update has only one item", diff->origin, chain_index);
891 
892                 first_begin = 0;
893                 last_end = ptr_vector_last_index(&nodes);
894             }
895         }
896         else // chain is empty, we add everything
897         {
898             log_debug("update: %{dnsname}: chain #%i is empty", diff->origin, chain_index);
899 
900             first_begin = 0;
901             last_end = ptr_vector_last_index(&nodes);
902         }
903 
904         //yassert(dc->chain->isempty(chain) || (first_begin >= 0) || ((first_begin == 0) && (last_end == 0)));
905 
906 #if DEBUG
907         for(int i = first_begin; i <= last_end; ++i)
908         {
909             void *node = ptr_vector_get_mod(&nodes, i);
910             u8 state = dc->chain->state_get(node);
911             void *next = ((state & (DNSSEC_CHAIN_BEGIN|DNSSEC_CHAIN_END)) != DNSSEC_CHAIN_END) ? ptr_vector_get_mod(&nodes, i + 1) : NULL;
912 
913             format_writer temp_fw_0 = {zone_diff_chain_state_format, &state};
914             log_debug1("update: %{dnsname}: %3i: %02x %w: %p -> %p", diff->origin, i, state, &temp_fw_0, node, next);
915             dc->chain->publish_log(node, next);
916         }
917 #endif
918 
919         if(dc->chain->isempty(chain) || whole_chain || ((first_begin == 0) && (last_end == 0)))
920         {
921             // we are processing a new/whole chain, or the chain chain is made of one record
922 
923             for(int i = first_begin; i <= last_end; ++i)
924             {
925                 int j = i + 1;
926                 void *node = ptr_vector_get_mod(&nodes, i);
927                 void *node_next = ptr_vector_get_mod(&nodes, j);
928                 u8 state = dc->chain->state_get(node);
929 
930                 if(state & DNSSEC_CHAIN_EXISTS)
931                 {
932                     if((state & DNSSEC_CHAIN_REMAP) || ((state & (DNSSEC_CHAIN_DELETE|DNSSEC_CHAIN_ADD)) == (DNSSEC_CHAIN_DELETE|DNSSEC_CHAIN_ADD)))
933                     {
934 #if DEBUG
935                         log_debug3("update: %{dnsname}: chain %i state (%02x) del/add", diff->origin, chain_index, state);
936 #endif
937                         dc->chain->publish_delete(chain, node, node_next, diff, del);
938                         dnssec_chain_store_diff_publish_chain_node(dc, diff, keys, chain, node, node_next, add);
939                     }
940                     else if(state & DNSSEC_CHAIN_DELETE)
941                     {
942 #if DEBUG
943                         log_debug3("update: %{dnsname}: chain %i state (%02x) del", diff->origin, chain_index, state);
944 #endif
945                         dc->chain->publish_delete(chain, node, node_next, diff, del);
946                     }
947                 }
948                 else
949                 {
950                     if((state & DNSSEC_CHAIN_EXISTS) == 0)
951                     {
952                         state &= ~DNSSEC_CHAIN_DELETE;      // cannot delete what does not exists
953                         if(state & DNSSEC_CHAIN_REMAP)
954                         {
955                             state &= ~DNSSEC_CHAIN_REMAP;   // do not remap, create
956                             state |= DNSSEC_CHAIN_ADD;
957                         }
958 
959                         dc->chain->state_set(node, state);
960                     }
961 
962                     if(state & DNSSEC_CHAIN_ADD)
963                     {
964 #if DEBUG
965                         log_debug3("update: %{dnsname}: chain %i state (%02x) add", diff->origin, chain_index, state);
966 #endif
967                         dnssec_chain_store_diff_publish_chain_node(dc, diff, keys, chain, node, node_next, add);
968                     }
969                 }
970             }
971 
972             continue;
973         }
974 
975         yassert(first_begin != last_end);
976 
977         void *next_did_exist_node = NULL;
978         void *next_will_exist_node = NULL;
979         int next_did_exist_index = -1;
980         int next_will_exist_index = -1;
981 
982         for(int i = first_begin; i < last_end; ++i)
983         {
984             void *node = ptr_vector_get_mod(&nodes, i);
985             u8 state = dc->chain->state_get(node);
986 
987             if((state & DNSSEC_CHAIN_EXISTS) == 0)
988             {
989                 state &= ~DNSSEC_CHAIN_DELETE;      // cannot delete what does not exists
990                 if(state & DNSSEC_CHAIN_REMAP)
991                 {
992                     state &= ~DNSSEC_CHAIN_REMAP;   // do not remap, create
993                     state |= DNSSEC_CHAIN_ADD;
994                 }
995 
996                 dc->chain->state_set(node, state);
997             }
998 
999             if(state & (DNSSEC_CHAIN_DELETE|DNSSEC_CHAIN_REMAP))
1000             {
1001 #if DEBUG
1002                 if((state & DNSSEC_CHAIN_EXISTS) == 0)
1003                 {
1004                     format_writer chain_node_fw;
1005                     dc->chain->format_writer_init(node, &chain_node_fw);
1006                     format_writer temp_fw_0 = {zone_diff_chain_state_format, &state};
1007                     log_err("dnssec-chain: %{dnsname}: chain %i node %w with state %w should be remapped or deleted but does not exist ?",
1008                         diff->origin, chain_index, &chain_node_fw, &temp_fw_0);
1009                     logger_flush();
1010                 }
1011 #endif
1012                 yassert(state & DNSSEC_CHAIN_EXISTS); // trips on an empty terminal : the node to delete does not exists.
1013 
1014                 if(next_did_exist_index <= i)
1015                 {
1016                     for(int j = i + 1; j <= last_end; ++j)
1017                     {
1018                         void *next_node = ptr_vector_get_mod(&nodes, j);
1019                         u8 next_state = dc->chain->state_get(next_node);
1020                         if(next_state & DNSSEC_CHAIN_EXISTS)
1021                         {
1022                             next_did_exist_node = next_node;
1023                             next_did_exist_index = j;
1024                             break;
1025                         }
1026                     }
1027                 }
1028 
1029                 yassert(next_did_exist_index > i);
1030 
1031                 dc->chain->publish_delete(chain, node, next_did_exist_node, diff, del);
1032             }
1033 
1034             switch(state & (DNSSEC_CHAIN_DELETE|DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_EXISTS|DNSSEC_CHAIN_REMAP))
1035             {
1036                 case DNSSEC_CHAIN_ADD:
1037                 case DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_REMAP:
1038                 case DNSSEC_CHAIN_DELETE|DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_EXISTS:
1039                 case DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_EXISTS|DNSSEC_CHAIN_REMAP:
1040                 case DNSSEC_CHAIN_DELETE|DNSSEC_CHAIN_ADD|DNSSEC_CHAIN_EXISTS|DNSSEC_CHAIN_REMAP:
1041                 {
1042                     if(next_will_exist_index <= i)
1043                     {
1044                         for(int j = i + 1; j <= last_end; ++j)
1045                         {
1046                             void *next_node = ptr_vector_get_mod(&nodes, j);
1047                             u8 next_state = dc->chain->state_get(next_node);
1048                             if((next_state & DNSSEC_CHAIN_ADD) || ((next_state & (DNSSEC_CHAIN_DELETE | DNSSEC_CHAIN_EXISTS)) == DNSSEC_CHAIN_EXISTS))
1049                             {
1050                                 next_will_exist_node = next_node;
1051                                 next_will_exist_index = j;
1052                                 break;
1053                             }
1054                         }
1055                     }
1056 
1057                     yassert(next_will_exist_index > i);
1058 
1059 #if DEBUG
1060                     log_debug3("update: %{dnsname}: chain %i state (%02x) publish chain node", diff->origin, chain_index, state);
1061 #endif
1062 
1063                     dnssec_chain_store_diff_publish_chain_node(dc, diff, keys, chain, node, next_will_exist_node, add);
1064 
1065                     break;
1066                 }
1067                 default:
1068                 {
1069                     break;
1070                 }
1071             }
1072         } // for all items in [begin;end[
1073     }
1074 
1075     ptr_vector_destroy(&nodes);
1076 }
1077 
1078 /**
1079  * Releases the memory used by a chain
1080  */
1081 
dnssec_chain_finalize(dnssec_chain * dc)1082 void dnssec_chain_finalize(dnssec_chain *dc)
1083 {
1084     ptr_set_callback_and_destroy(&dc->chain_diff, dc->chain->ptr_set_node_delete_callback);
1085 }
1086 
zone_diff_label_rr_compare(const void * node_a,const void * node_b)1087 static int zone_diff_label_rr_compare(const void *node_a, const void *node_b)
1088 {
1089     const zone_diff_label_rr *a = (const zone_diff_label_rr*)node_a;
1090     const zone_diff_label_rr *b = (const zone_diff_label_rr*)node_b;
1091 
1092     int d;
1093 
1094     d = a->rclass;
1095     d -= b->rclass;
1096 
1097     if(d == 0)
1098     {
1099         d = a->rtype;
1100         d -= b->rtype;
1101 
1102         if(d == 0)
1103         {
1104             d = dnsname_getdepth(a->fqdn);
1105             d -= dnsname_getdepth(b->fqdn);
1106 
1107             if(d == 0)
1108             {
1109                 d = dnsname_compare(a->fqdn, b->fqdn);
1110 
1111                 if(d == 0)
1112                 {
1113                     u16 len = MIN(a->rdata_size, b->rdata_size);
1114                     d = memcmp(a->rdata, b->rdata, len);
1115 
1116                     if(d == 0)
1117                     {
1118                         d = a->rdata_size;
1119                         d -= b->rdata_size;
1120                     }
1121                 }
1122             }
1123         }
1124         else
1125         {
1126             // SOA have to be first
1127 
1128             if(a->rtype == TYPE_SOA)
1129             {
1130                 d = -1;
1131             }
1132             else
1133             {
1134                 d = 1;
1135             }
1136         }
1137     }
1138 
1139     return d;
1140 }
1141 
1142 zone_diff_label_rr *
zone_diff_label_rr_new(const u8 * fqdn,u16 rtype,u16 rclass,s32 ttl,void * rdata,u16 rdata_size,bool copy)1143 zone_diff_label_rr_new(const u8 *fqdn, u16 rtype, u16 rclass, s32 ttl, void *rdata, u16 rdata_size, bool copy)
1144 {
1145     zone_diff_label_rr *rr;
1146     ZALLOC_OBJECT_OR_DIE(rr, zone_diff_label_rr, ZDFFLABL_TAG);
1147     rr->fqdn = dnsname_zdup(fqdn);
1148     rr->ttl = ttl;
1149     rr->rtype = rtype;
1150     rr->rclass = rclass;
1151     rr->rdata_size = rdata_size;
1152     if(copy)
1153     {
1154         ZALLOC_ARRAY_OR_DIE(u8*, rr->rdata, rdata_size, ZDFFLBRR_TAG);
1155         memcpy(rr->rdata, rdata, rdata_size);
1156         rr->state = ZONE_DIFF_RR_RDATA_OWNED;
1157     }
1158     else
1159     {
1160         rr->rdata = rdata;
1161         rr->state = 0;
1162     }
1163     return rr;
1164 }
1165 
1166 void
zone_diff_label_rr_init_tmp(zone_diff_label_rr * rr,const u8 * fqdn,u16 rtype,u16 rclass,s32 ttl,void * rdata,u16 rdata_size)1167 zone_diff_label_rr_init_tmp(zone_diff_label_rr *rr, const u8 *fqdn, u16 rtype, u16 rclass, s32 ttl, void *rdata, u16 rdata_size)
1168 {
1169     rr->fqdn = (u8*)fqdn;
1170     rr->ttl = ttl;
1171     rr->rtype = rtype;
1172     rr->rclass = rclass;
1173     rr->rdata_size = rdata_size;
1174     rr->rdata = rdata;
1175     rr->state = 0;
1176 }
1177 
1178 zone_diff_label_rr *
zone_diff_label_rr_new_nordata(const u8 * fqdn,u16 rtype,u16 rclass,s32 ttl,u16 rdata_size)1179 zone_diff_label_rr_new_nordata(const u8 *fqdn, u16 rtype, u16 rclass, s32 ttl, u16 rdata_size)
1180 {
1181     zone_diff_label_rr *rr;
1182     ZALLOC_OBJECT_OR_DIE(rr, zone_diff_label_rr, ZDFFLABL_TAG);
1183     rr->fqdn = dnsname_zdup(fqdn);
1184     rr->ttl = ttl;
1185     rr->rtype = rtype;
1186     rr->rclass = rclass;
1187     rr->rdata_size = rdata_size;
1188     ZALLOC_ARRAY_OR_DIE(u8*, rr->rdata, rdata_size, ZDFFLBRR_TAG);
1189     rr->state = ZONE_DIFF_RR_RDATA_OWNED;
1190 
1191     return rr;
1192 }
1193 
zone_diff_label_rr_delete(zone_diff_label_rr * rr)1194 static void zone_diff_label_rr_delete(zone_diff_label_rr *rr)
1195 {
1196     dnsname_zfree(rr->fqdn);
1197 
1198     if(rr->state & ZONE_DIFF_RR_RDATA_OWNED)
1199     {
1200 #if DEBUG
1201         memset(rr->rdata, 0xff, rr->rdata_size);
1202 #endif
1203         ZFREE_ARRAY(rr->rdata, rr->rdata_size);
1204     }
1205 #if DEBUG
1206     memset(rr, 0xff, sizeof(zone_diff_label_rr));
1207 #endif
1208     ZFREE_OBJECT(rr);
1209 }
1210 
zone_diff_label_rr_vector_clear(ptr_vector * records)1211 static void zone_diff_label_rr_vector_clear(ptr_vector *records)
1212 {
1213     for(int i = 0; i <= ptr_vector_last_index(records); ++i)
1214     {
1215         zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(records, i);
1216         if((rr->state & ZONE_DIFF_RR_VOLATILE) != 0)
1217         {
1218             zone_diff_label_rr_delete(rr);
1219         }
1220     }
1221     ptr_vector_clear(records);
1222 }
1223 
zone_diff_fqdn_rr_set_new(u16 rtype)1224 zone_diff_fqdn_rr_set *zone_diff_fqdn_rr_set_new(u16 rtype)
1225 {
1226     zone_diff_fqdn_rr_set *rr_set;
1227     ZALLOC_OBJECT_OR_DIE(rr_set, zone_diff_fqdn_rr_set, ZDFFRRST_TAG);
1228     ptr_set_init(&rr_set->rr);
1229     rr_set->rr.compare = zone_diff_label_rr_compare;
1230     rr_set->key_mask = 0;
1231     rr_set->org_ttl = -1;
1232     rr_set->new_ttl = -1;
1233     rr_set->rtype = rtype;
1234     rr_set->rclass = CLASS_IN;
1235     return rr_set;
1236 }
1237 
zone_diff_fqdn_rr_set_delete_cb(ptr_node * node)1238 static void zone_diff_fqdn_rr_set_delete_cb(ptr_node *node)
1239 {
1240     zone_diff_label_rr *rr = (zone_diff_label_rr*)node->value;
1241 #if DEBUG
1242     log_debug7("update: %{dnsname}: deleting %{dnstype} structure", rr->fqdn, &rr->rtype);
1243 #endif
1244     zone_diff_label_rr_delete(rr);
1245 }
1246 
zone_diff_fqdn_rr_set_delete(zone_diff_fqdn_rr_set * rr_set)1247 static void zone_diff_fqdn_rr_set_delete(zone_diff_fqdn_rr_set *rr_set)
1248 {
1249     if(rr_set != NULL)
1250     {
1251         ptr_set_callback_and_destroy(&rr_set->rr, zone_diff_fqdn_rr_set_delete_cb);
1252         ZFREE_OBJECT(rr_set);
1253     }
1254 }
1255 
zone_diff_fqdn_rr_set_rr_add_replace(zone_diff_fqdn_rr_set * rr_set,zone_diff_label_rr * rr)1256 void zone_diff_fqdn_rr_set_rr_add_replace(zone_diff_fqdn_rr_set *rr_set, zone_diff_label_rr *rr)
1257 {
1258     ptr_node *node = ptr_set_insert(&rr_set->rr, rr);
1259 
1260     if(node->value == NULL)
1261     {
1262         node->value = rr;
1263     }
1264     else
1265     {
1266         zone_diff_label_rr_delete((zone_diff_label_rr*)node->value);
1267         node->key = rr;
1268         node->value = rr;
1269     }
1270 }
1271 
1272 zone_diff_label_rr*
zone_diff_fqdn_rr_set_rr_add_get(zone_diff_fqdn_rr_set * rr_set,zone_diff_label_rr * rr)1273 zone_diff_fqdn_rr_set_rr_add_get(zone_diff_fqdn_rr_set *rr_set, zone_diff_label_rr *rr)
1274 {
1275     ptr_node *node = ptr_set_insert(&rr_set->rr, rr);
1276 
1277     if(node->value == NULL)
1278     {
1279         node->value = rr;
1280     }
1281     else
1282     {
1283         zone_diff_label_rr_delete(rr);
1284         rr = (zone_diff_label_rr*)node->value;
1285     }
1286     return rr;
1287 }
1288 
1289 static zone_diff_label_rr *
zone_diff_fqdn_rr_set_get_existing_rr(zone_diff_fqdn_rr_set * rr_set,const zone_diff_label_rr * rr)1290 zone_diff_fqdn_rr_set_get_existing_rr(zone_diff_fqdn_rr_set *rr_set, const zone_diff_label_rr *rr)
1291 {
1292     ptr_node *node = ptr_set_find(&rr_set->rr, rr);
1293 
1294     if(node != NULL)
1295     {
1296         return (zone_diff_label_rr*)node->value;
1297     }
1298 
1299     return NULL;
1300 }
1301 
1302 //
1303 
zone_diff_fqdn_new(const u8 * fqdn)1304 static zone_diff_fqdn *zone_diff_fqdn_new(const u8 *fqdn)
1305 {
1306     zone_diff_fqdn *diff_fqdn;
1307     ZALLOC_OBJECT_OR_DIE(diff_fqdn, zone_diff_fqdn, ZDFFFQDN_TAG);
1308     memset(diff_fqdn, 0, sizeof(zone_diff_fqdn));
1309     u32_set_init(&diff_fqdn->rrset);
1310     diff_fqdn->fqdn = dnsname_zdup(fqdn);
1311     //diff_fqdn->type_map_changed = FALSE;
1312     return diff_fqdn;
1313 }
1314 
zone_diff_fqdn_delete_cb(u32_node * node)1315 static void zone_diff_fqdn_delete_cb(u32_node *node)
1316 {
1317     zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)node->value;
1318 #if DEBUG
1319     if(rrset == NULL)
1320     {
1321         u16 rtype = (u16)node->key;
1322         log_debug1("zone_diff_fqdn_delete_cb empty set for type %{dnstype}", &rtype);
1323     }
1324 #endif
1325     zone_diff_fqdn_rr_set_delete(rrset);
1326 }
1327 
zone_diff_fqdn_delete(zone_diff_fqdn * diff_fqdn)1328 static void zone_diff_fqdn_delete(zone_diff_fqdn *diff_fqdn)
1329 {
1330     u32_set_callback_and_destroy(&diff_fqdn->rrset, zone_diff_fqdn_delete_cb);
1331 
1332 #if DEBUG
1333     log_debug1("update: %{dnsname}: deleting diff fqdn", diff_fqdn->fqdn);
1334 #endif
1335     dnsname_zfree(diff_fqdn->fqdn);
1336     ZFREE_OBJECT(diff_fqdn);
1337 }
1338 
1339 zone_diff_fqdn_rr_set*
zone_diff_fqdn_rr_set_add(zone_diff_fqdn * diff_fqdn,u16 rtype)1340 zone_diff_fqdn_rr_set_add(zone_diff_fqdn *diff_fqdn, u16 rtype)
1341 {
1342     u32_node *node = u32_set_insert(&diff_fqdn->rrset, rtype);
1343     if(node->value == NULL)
1344     {
1345         node->value = zone_diff_fqdn_rr_set_new(rtype);
1346     }
1347     return (zone_diff_fqdn_rr_set*)node->value;
1348 }
1349 
1350 /**
1351  * Returns the local copy of the specified RRSET
1352  * Creates an emtpy set if it does not exist.
1353  *
1354  * @param diff_fqdn
1355  * @param rtype
1356  * @return
1357  */
1358 
1359 zone_diff_fqdn_rr_set *
zone_diff_fqdn_rr_set_get(const zone_diff_fqdn * diff_fqdn,u16 rtype)1360 zone_diff_fqdn_rr_set_get(const zone_diff_fqdn *diff_fqdn, u16 rtype)
1361 {
1362 #if 0 /* fix */
1363 #else
1364     u32_node *node = u32_set_find(&diff_fqdn->rrset, rtype);
1365     if(node != NULL)
1366     {
1367         return (zone_diff_fqdn_rr_set*)node->value;
1368     }
1369     return NULL;
1370 #endif
1371 }
1372 
1373 /**
1374  * Returns the local copy of the specified RRSET
1375  *
1376  * @param diff_fqdn
1377  * @param rtype
1378  * @return
1379  */
1380 
zone_diff_fqdn_rr_get_const(const zone_diff_fqdn * diff_fqdn,u16 rtype)1381 const zone_diff_fqdn_rr_set *zone_diff_fqdn_rr_get_const(const zone_diff_fqdn *diff_fqdn, u16 rtype)
1382 {
1383     u32_node *node = u32_set_find(&diff_fqdn->rrset, rtype);
1384 
1385     if(node != NULL)
1386     {
1387         return (zone_diff_fqdn_rr_set*)node->value;
1388     }
1389 
1390     return NULL;
1391 }
1392 
1393 s32
zone_diff_fqdn_rr_set_get_ttl(zone_diff_fqdn_rr_set * rrset)1394 zone_diff_fqdn_rr_set_get_ttl(zone_diff_fqdn_rr_set *rrset)
1395 {
1396     // @note 20170228 edf -- issue detection
1397     // If this aborts, it's likely somebody called zone_diff_fqdn_rr_get without
1398     // the intent of putting records in it.
1399     // Find it and call zone_diff_will_have_rrset_type instead.
1400     yassert(rrset != NULL);
1401 
1402     ptr_set_iterator rr_iter;
1403     ptr_set_iterator_init(&rrset->rr, &rr_iter);
1404     while(ptr_set_iterator_hasnext(&rr_iter))
1405     {
1406         ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
1407         zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
1408 
1409         if((rr->state & ZONE_DIFF_RR_REMOVE) == 0)
1410         {
1411             // this record was present or is being added
1412             return rr->ttl;
1413         }
1414     }
1415 
1416     return -1;
1417 }
1418 
1419 s32
zone_diff_fqdn_rr_get_ttl(const zone_diff_fqdn * diff_fqdn,u16 rtype)1420 zone_diff_fqdn_rr_get_ttl(const zone_diff_fqdn *diff_fqdn, u16 rtype)
1421 {
1422     s32 ttl = -1;
1423     u32_node *rrset_node = u32_set_find(&diff_fqdn->rrset, rtype);
1424     if(rrset_node != NULL)
1425     {
1426         zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)rrset_node->value;
1427         ttl = zone_diff_fqdn_rr_set_get_ttl(rrset);
1428     }
1429     return ttl;  // TTL is signed, 32 bits and >= 0
1430 }
1431 
1432 /**
1433  * Deletes an RRSET if it's empty.
1434  *
1435  * @param diff_fqdn
1436  * @param rtype
1437  */
1438 
1439 void
zone_diff_fqdn_rr_clear(zone_diff_fqdn * diff_fqdn,u16 rtype)1440 zone_diff_fqdn_rr_clear(zone_diff_fqdn *diff_fqdn, u16 rtype)
1441 {
1442     u32_node *node = u32_set_insert(&diff_fqdn->rrset, rtype);
1443     if(node != NULL)
1444     {
1445         if(node->value == NULL)
1446         {
1447             u32_set_delete(&diff_fqdn->rrset, rtype);
1448         }
1449     }
1450 }
1451 
1452 /**
1453  * Returns TRUE iff an rrset as been added or removed from the label.
1454  * Stressing out this concerns RRSET as a whole.
1455  *
1456  * @param diff_fqdn
1457  * @return
1458  */
1459 
zone_diff_fqdn_type_map_changed(const zone_diff_fqdn * diff_fqdn)1460 bool zone_diff_fqdn_type_map_changed(const zone_diff_fqdn *diff_fqdn)
1461 {
1462     if(diff_fqdn->rrsig_kept == 0)
1463     {
1464         if(diff_fqdn->rrsig_added || diff_fqdn->rrsig_removed)
1465         {
1466             return TRUE;        // RRSIG type bitmap has changed;
1467         }
1468     }
1469 
1470     u32_set_iterator iter;
1471     ptr_set_iterator rr_iter;
1472 
1473     u32_set_iterator_init(&diff_fqdn->rrset, &iter);
1474     while(u32_set_iterator_hasnext(&iter))
1475     {
1476         u32_node *node = u32_set_iterator_next_node(&iter);
1477         zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)node->value;
1478         if(rrset != NULL)
1479         {
1480             ptr_set_iterator_init(&rrset->rr, &rr_iter);
1481             u8 rr_state = 0;
1482             while(ptr_set_iterator_hasnext(&rr_iter))
1483             {
1484                 ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
1485                 zone_diff_label_rr *rr = (zone_diff_label_rr *)rr_node->key;
1486 
1487                 if(rr->state == 0)
1488                 {
1489                     // previously existing record : no change on this set
1490                     rr_state = 0;
1491                     break;
1492                 }
1493 
1494                 rr_state |= rr->state & (ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADD);
1495             }
1496 
1497             if((rr_state != 0) && ((rr_state & (ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADD)) != (ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADD)))
1498             {
1499                 // this set is completely added or completely removed
1500 
1501                 if(rrset->rtype != TYPE_RRSIG) // exceptional test
1502                 {
1503                     return TRUE;
1504                 }
1505                 else
1506                 {
1507                     if(!diff_fqdn->is_apex)
1508                     {
1509                         if(diff_fqdn->has_active_zsk)
1510                         {
1511                             rr_state |= ZONE_DIFF_RR_ADD;
1512 
1513                             if((rr_state != 0) && ((rr_state & (ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADD)) != (ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADD)))
1514                             {
1515                                 return TRUE;
1516                             }
1517                         }
1518                     }
1519                     else
1520                     {
1521                         if(diff_fqdn->has_active_zsk||diff_fqdn->has_active_ksk)
1522                         {
1523                             rr_state |= ZONE_DIFF_RR_ADD;
1524 
1525                             if((rr_state != 0) && ((rr_state & (ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADD)) != (ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADD)))
1526                             {
1527                                 return TRUE;
1528                             }
1529                         }
1530                     }
1531                 }
1532             }
1533         }
1534     }
1535 
1536     return FALSE;
1537 }
1538 
1539 /**
1540  * Initialises a zone diff
1541  *
1542  * @param diff
1543  * @param origin
1544  * @param nttl
1545  */
1546 
zone_diff_init(zone_diff * diff,zdb_zone * zone,bool rrsig_update_allowed)1547 void zone_diff_init(zone_diff *diff, zdb_zone *zone, bool rrsig_update_allowed)
1548 {
1549     log_debug1("update: %{dnsname}: initialising diff @%p", zone->origin, diff);
1550 
1551     ptr_set_init(&diff->fqdn);
1552     ptr_set_init(&diff->root.sub);
1553     diff->root.sub.compare = ptr_set_dnslabel_node_compare;
1554     diff->fqdn.compare = ptr_set_fqdn_node_compare;
1555     diff->origin = zone->origin;
1556 
1557     diff->rrsig_validity_interval = MAX(zone->sig_validity_interval_seconds, 0);
1558     diff->rrsig_validity_regeneration = MAX(zone->sig_validity_regeneration_seconds, 0);
1559     diff->rrsig_validity_jitter = MAX( zone->sig_validity_jitter_seconds, 0);
1560     diff->nttl = zone->min_ttl;
1561     diff->rrsig_update_allowed = rrsig_update_allowed;
1562     diff->has_active_zsk = FALSE;
1563     diff->has_active_ksk = FALSE;
1564 
1565     u8 maintain_mode = zone_get_maintain_mode(zone);
1566 
1567     switch(maintain_mode)
1568     {
1569         case ZDB_ZONE_MAINTAIN_NSEC3:
1570         case ZDB_ZONE_MAINTAIN_NSEC3_OPTOUT:
1571         {
1572             diff->maintain_nsec = FALSE;
1573             diff->maintain_nsec3 = TRUE;
1574             break;
1575         }
1576         case ZDB_ZONE_MAINTAIN_NSEC:
1577         {
1578             diff->maintain_nsec = TRUE;
1579             diff->maintain_nsec3 = FALSE;
1580             break;
1581         }
1582         default:
1583         {
1584             diff->maintain_nsec = FALSE;
1585             diff->maintain_nsec3 = FALSE;
1586             break;
1587         }
1588     }
1589 
1590     // NOTE: set the apex at the end of the function
1591 
1592     diff->apex = zone_diff_fqdn_add(diff, zone->origin, zone->apex);
1593 }
1594 
1595 static zone_diff_label_tree*
zone_diff_label_tree_add_fqdn(zone_diff * diff,const u8 * fqdn)1596 zone_diff_label_tree_add_fqdn(zone_diff *diff, const u8 *fqdn)
1597 {
1598 #if DEBUG
1599     log_debug2("zone-diff: %{dnsname}: label tree add %{dnsname}", diff->origin, fqdn);
1600 #endif
1601 
1602     if(fqdn[0] != 0)
1603     {
1604         zone_diff_label_tree *label_node;
1605         ptr_node *label_tree_node;
1606         const u8 *parent_fqdn = fqdn + fqdn[0] + 1;
1607         zone_diff_label_tree *parent = zone_diff_label_tree_add_fqdn(diff, parent_fqdn);
1608 
1609         label_tree_node = ptr_set_insert(&parent->sub, (u8*)fqdn);
1610 
1611         if(label_tree_node->value != NULL)
1612         {
1613             label_node = (zone_diff_label_tree*)label_tree_node->value;
1614         }
1615         else
1616         {
1617             ZALLOC_OBJECT_OR_DIE(label_node, zone_diff_label_tree, ZDLABELT_TAG);
1618             label_node->label = fqdn;
1619             label_node->diff_fqdn = zone_diff_fqdn_get(diff, fqdn);
1620             ptr_set_init(&label_node->sub);
1621             label_node->sub.compare = ptr_set_dnslabel_node_compare;
1622             label_tree_node->value = label_node;
1623         }
1624 
1625         return label_node;
1626     }
1627     else
1628     {
1629         return &diff->root;
1630     }
1631 }
1632 
zone_diff_label_tree_destroy_cb(ptr_node * node)1633 static void zone_diff_label_tree_destroy_cb(ptr_node* node)
1634 {
1635     zone_diff_label_tree* dlt = (zone_diff_label_tree*)node->value;
1636     if(dlt != NULL)
1637     {
1638         if(!ptr_set_isempty(&dlt->sub))
1639         {
1640             ptr_set_callback_and_destroy(&dlt->sub, zone_diff_label_tree_destroy_cb);
1641         }
1642         ZFREE_OBJECT(dlt);
1643     }
1644 }
1645 
zone_diff_label_tree_destroy(zone_diff * diff)1646 static void zone_diff_label_tree_destroy(zone_diff *diff)
1647 {
1648     ptr_set_callback_and_destroy(&diff->root.sub, zone_diff_label_tree_destroy_cb);
1649 }
1650 
1651 static zone_diff_label_tree*
zone_diff_fqdn_label_find(zone_diff_label_tree * parent,const u8 * fqdn)1652 zone_diff_fqdn_label_find(zone_diff_label_tree* parent, const u8 *fqdn)
1653 {
1654     if(fqdn[0] != 0)
1655     {
1656         parent = zone_diff_fqdn_label_find(parent, fqdn + fqdn[0] + 1);
1657         if(parent != NULL)
1658         {
1659             ptr_node *node = ptr_set_find(&parent->sub, fqdn);
1660             parent = (zone_diff_label_tree*)node->value;
1661         }
1662     }
1663     return parent;
1664 }
1665 
1666 bool
zone_diff_fqdn_has_children(zone_diff * diff,const u8 * fqdn)1667 zone_diff_fqdn_has_children(zone_diff *diff, const u8 *fqdn)
1668 {
1669     zone_diff_label_tree* parent = &diff->root;
1670     parent = zone_diff_fqdn_label_find(parent, fqdn);
1671     return parent != NULL;
1672 }
1673 
1674 //#define ZONE_DIFF_FQDN_LABEL_STATE_RECORDS_EXISTED 1
1675 //#define ZONE_DIFF_FQDN_LABEL_STATE_RECORDS_ADDED   2
1676 //#define ZONE_DIFF_FQDN_LABEL_STATE_RECORDS_EXISTS  3
1677 #define ZONE_DIFF_FQDN_LABEL_STATE_NONEMPTY 2
1678 #define ZONE_DIFF_FQDN_LABEL_STATE_CHILDREN 1
1679 
1680 static u8
zone_diff_fqdn_children_state_find(zone_diff_label_tree * parent)1681 zone_diff_fqdn_children_state_find(zone_diff_label_tree* parent)
1682 {
1683     u8 ret;
1684 
1685     if(parent->diff_fqdn != NULL)
1686     {
1687         ret = parent->diff_fqdn->is_apex;
1688 
1689         if(parent->diff_fqdn->children_flags_set)
1690         {
1691             ret |= parent->diff_fqdn->will_be_non_empty | parent->diff_fqdn->will_have_children;
1692 #if DYNUPDATE_DIFF_DETAILED_LOG
1693             log_debug3("zone_diff_fqdn_children_state_find(%{dnsname}) = %x (already known)", parent->diff_fqdn->fqdn, ret);
1694 #endif
1695             return ret;
1696         }
1697     }
1698     else
1699     {
1700         ret = 0;
1701     }
1702 
1703     ptr_set_iterator iter;
1704     ptr_set_iterator_init(&parent->sub, &iter);
1705     while(ptr_set_iterator_hasnext(&iter))
1706     {
1707         ptr_node* node = ptr_set_iterator_next_node(&iter);
1708 
1709         zone_diff_label_tree* fqdn_node = (zone_diff_label_tree*)node->value;
1710 
1711         if(fqdn_node->diff_fqdn != NULL)
1712         {
1713             if(!fqdn_node->diff_fqdn->children_flags_set)
1714             {
1715                 if(!ptr_set_isempty(&fqdn_node->sub))
1716                 {
1717                     if(zone_diff_fqdn_children_state_find(fqdn_node) != 0)
1718                     {
1719                         // ret |= ZONE_DIFF_FQDN_LABEL_STATE_CHILDREN;
1720 
1721                         fqdn_node->diff_fqdn->will_have_children = 1;
1722                     }
1723                 }
1724 
1725                 fqdn_node->diff_fqdn->children_flags_set = 1;
1726             }
1727 
1728             ret |= fqdn_node->diff_fqdn->will_be_non_empty | fqdn_node->diff_fqdn->will_have_children;
1729         }
1730         else
1731         {
1732             if(!ptr_set_isempty(&fqdn_node->sub))
1733             {
1734                 if(zone_diff_fqdn_children_state_find(fqdn_node) != 0)
1735                 {
1736                     ret |= ZONE_DIFF_FQDN_LABEL_STATE_CHILDREN;
1737                 }
1738             }
1739         }
1740     }
1741 
1742 #if DYNUPDATE_DIFF_DETAILED_LOG
1743     log_debug3("zone_diff_fqdn_children_state_find(%{dnsname}) = %x", parent->diff_fqdn->fqdn, ret);
1744 #endif
1745 
1746     return ret;
1747 }
1748 
1749 u8
zone_diff_fqdn_children_state(zone_diff * diff,const u8 * fqdn)1750 zone_diff_fqdn_children_state(zone_diff *diff, const u8 *fqdn)
1751 {
1752     zone_diff_label_tree* fqdn_node = zone_diff_fqdn_label_find(&diff->root, fqdn);
1753 
1754     if(fqdn_node != NULL)
1755     {
1756 /*
1757         zone_diff_fqdn_children_state_parm parms;
1758         int fqdn_len = dnsname_len(fqdn);
1759         parms.fqdn = &parms.fqdn_storage[256 - fqdn_len];
1760         memcpy(parms.fqdn, fqdn, fqdn_len);
1761 */
1762         // if node has sub, set it
1763         // for all sub
1764         //      if sub has all records removed, set it
1765         //      if sub has records added, set it
1766         //      if +- are both set, stop seeking (all needed answers are ready)
1767         //      if sub has sub, go deeper
1768 
1769         zone_diff_fqdn_children_state_find(fqdn_node);
1770     }
1771 
1772     return 0;
1773 }
1774 
1775 /**
1776  * Finalises a zone diff
1777  *
1778  * @param diff
1779  */
1780 
zone_diff_finalize_cb(ptr_node * node)1781 static void zone_diff_finalize_cb(ptr_node *node)
1782 {
1783     zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)node->value;
1784     zone_diff_fqdn_delete(diff_fqdn);
1785 }
1786 
zone_diff_finalize(zone_diff * diff)1787 void zone_diff_finalize(zone_diff *diff)
1788 {
1789     log_debug1("update: %{dnsname}: deleting diff @%p", diff->origin, diff);
1790     zone_diff_label_tree_destroy(diff);
1791     ptr_set_callback_and_destroy(&diff->fqdn, zone_diff_finalize_cb);
1792 }
1793 
1794 zone_diff_fqdn*
zone_diff_fqdn_add_empty(zone_diff * diff,const u8 * fqdn)1795 zone_diff_fqdn_add_empty(zone_diff *diff, const u8 *fqdn)
1796 {
1797     ptr_node *node = ptr_set_insert(&diff->fqdn, (u8*)fqdn);
1798 
1799     if(node->value == NULL)
1800     {
1801 #if DEBUG
1802         log_debug2("update: %{dnsname} ...", fqdn);
1803 #endif
1804 
1805         zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_new(fqdn);
1806         node->key = diff_fqdn->fqdn; // to guarantee the const
1807         node->value = diff_fqdn;
1808     }
1809 
1810     return (zone_diff_fqdn*)node->value;
1811 }
1812 
1813 /**
1814  * label will be replaced ...
1815  *
1816  * @param diff
1817  * @param fqdn
1818  * @param label
1819  * @return
1820  */
1821 
1822 zone_diff_fqdn*
zone_diff_fqdn_add(zone_diff * diff,const u8 * fqdn,zdb_rr_label * label)1823 zone_diff_fqdn_add(zone_diff *diff, const u8 *fqdn, zdb_rr_label *label)
1824 {
1825     ptr_node *node = ptr_set_insert(&diff->fqdn, (u8*)fqdn);
1826 
1827     if(node->value == NULL)
1828     {
1829 #if DEBUG
1830         log_debug2("update: %{dnsname} (%p) ...", fqdn, label);
1831 #endif
1832 
1833         zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_new(fqdn);
1834         node->key = diff_fqdn->fqdn; // to guarantee the const
1835         node->value = diff_fqdn;
1836 
1837         zone_diff_label_tree *diff_fqdn_label = zone_diff_label_tree_add_fqdn(diff, diff_fqdn->fqdn);
1838         diff_fqdn_label->diff_fqdn = diff_fqdn;
1839 
1840         // copy all records
1841         if(label != NULL)
1842         {
1843             diff_fqdn->is_apex = zdb_rr_label_is_apex(label);
1844             diff_fqdn->at_delegation = ZDB_LABEL_ATDELEGATION(label);
1845             diff_fqdn->under_delegation = ZDB_LABEL_UNDERDELEGATION(label);
1846             diff_fqdn->had_ds = zdb_rr_label_has_rrset(label, TYPE_DS);
1847             diff_fqdn->was_at_delegation = diff_fqdn->at_delegation;
1848             diff_fqdn->was_under_delegation = diff_fqdn->under_delegation;
1849             diff_fqdn->was_non_empty = btree_notempty(label->resource_record_set);
1850             diff_fqdn->had_children = dictionary_notempty(&label->sub);
1851             //diff_fqdn->will_be_non_empty = diff_fqdn->was_non_empty;
1852             diff_fqdn->will_have_children = diff_fqdn->is_apex;
1853             diff_fqdn->will_have_ds = diff_fqdn->had_ds;
1854             diff_fqdn->children_added = 0;
1855 
1856             diff_fqdn->has_active_zsk = diff->has_active_zsk;
1857             diff_fqdn->has_active_ksk = diff->has_active_ksk;
1858 
1859             diff_fqdn->is_in_database = 1;
1860 
1861             btree_iterator iter;
1862             btree_iterator_init(label->resource_record_set, &iter);
1863 
1864             while(btree_iterator_hasnext(&iter))
1865             {
1866                 btree_node *rr_node = btree_iterator_next_node(&iter);
1867                 u16 type = (u16)rr_node->hash;
1868 
1869 #if DEBUG
1870                 log_debug2("update: %{dnsname} (%p) copying %{dnstype} RRSET", fqdn, label, &type);
1871 #endif
1872 
1873                 zone_diff_fqdn_rr_set *rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, type);
1874 
1875                 zdb_packed_ttlrdata *rr_sll = (zdb_packed_ttlrdata*)rr_node->data;
1876                 yassert(rr_sll != NULL);
1877 
1878                 if(rr_set->org_ttl == -1)
1879                 {
1880                     rr_set->org_ttl = rr_sll->ttl;
1881                 }
1882 
1883                 rr_set->new_ttl = rr_sll->ttl;
1884 
1885                 do
1886                 {
1887                     zone_diff_label_rr *rr = zone_diff_label_rr_new(fqdn, type, CLASS_IN, rr_sll->ttl, ZDB_PACKEDRECORD_PTR_RDATAPTR(rr_sll), ZDB_PACKEDRECORD_PTR_RDATASIZE(rr_sll), FALSE);
1888                     rr->state |= ZONE_DIFF_RR_IN_ZONE;
1889                     /** rr = */ zone_diff_fqdn_rr_set_rr_add_replace(rr_set, rr); /// NOTE: there should not be any collision here
1890                     rr_sll = rr_sll->next;
1891                 }
1892                 while(rr_sll != NULL);
1893             }
1894         }
1895         else
1896         {
1897 #if DEBUG
1898             log_debug2("update: %{dnsname} (%p) label is not in the zone", fqdn, label);
1899 #endif
1900             /*
1901             diff_fqdn->is_apex = FALSE;
1902             diff_fqdn->at_delegation = FALSE;
1903             diff_fqdn->under_delegation = FALSE;
1904             diff_fqdn->will_have_ds = FALSE;
1905             diff_fqdn->was_at_delegation = FALSE;
1906             diff_fqdn->was_under_delegation = FALSE;
1907             diff_fqdn->had_ds = FALSE;
1908             diff_fqdn->was_non_empty = FALSE;
1909             */
1910         }
1911     }
1912 #if DEBUG
1913     else
1914     {
1915         log_debug2("update: %{dnsname} (%p) already known (add)", fqdn, label);
1916     }
1917 #endif
1918 
1919     return (zone_diff_fqdn*)node->value;
1920 }
1921 
1922 #if ZDB_HAS_NSEC3_SUPPORT
1923 zone_diff_fqdn*
zone_diff_add_nsec3(zone_diff * diff,const nsec3_zone * n3,const nsec3_node * item,s32 ttl,zone_diff_fqdn_rr_set ** out_nsec3_rrset)1924 zone_diff_add_nsec3(zone_diff *diff, const nsec3_zone* n3, const nsec3_node *item, s32 ttl, zone_diff_fqdn_rr_set **out_nsec3_rrset)
1925 {
1926     u8 digest_len = NSEC3_NODE_DIGEST_SIZE(item);
1927     u8 fqdn[MAX_DOMAIN_LENGTH];
1928 
1929     fqdn[0] = base32hex_encode(NSEC3_NODE_DIGEST_PTR(item), digest_len, (char*)&fqdn[1]);
1930     dnsname_copy(&fqdn[fqdn[0] + 1], diff->origin);
1931 
1932     ptr_node *node = ptr_set_insert(&diff->fqdn, fqdn);
1933 
1934     if(node->value == NULL)
1935     {
1936 #if DEBUG
1937         log_debug2("update: %{dnsname} (%p) ...", fqdn, item);
1938 #endif
1939 
1940         zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_new(fqdn);
1941         node->key = diff_fqdn->fqdn; // to guarantee the const
1942         node->value = diff_fqdn;
1943 
1944         zone_diff_label_tree *diff_fqdn_label = zone_diff_label_tree_add_fqdn(diff, diff_fqdn->fqdn);
1945         diff_fqdn_label->diff_fqdn = diff_fqdn;
1946 
1947         // copy all records
1948         //diff_fqdn->is_apex = 0;
1949         //diff_fqdn->at_delegation = 0;
1950         //diff_fqdn->under_delegation = 0;
1951         //diff_fqdn->will_have_ds = 0;
1952         //diff_fqdn->was_at_delegation = 0;
1953         //diff_fqdn->was_under_delegation = 0;
1954         //diff_fqdn->had_ds = 0;
1955         diff_fqdn->was_non_empty = 1;
1956         //diff_fqdn->had_children = 0;
1957         //diff_fqdn->will_have_children = 0;
1958         //diff_fqdn->children_added = 0;
1959         diff_fqdn->is_nsec3 = 1;
1960 
1961         diff_fqdn->has_active_zsk = diff->has_active_zsk;
1962         diff_fqdn->has_active_ksk = diff->has_active_ksk;
1963 
1964 #if DEBUG
1965         log_debug2("update: %{dnsname} (%p) copying NSEC3 record", fqdn, item);
1966 #endif
1967         u32 param_rdata_size = NSEC3_ZONE_RDATA_SIZE(n3);
1968         u8 hash_len = NSEC3_NODE_DIGEST_SIZE(item);
1969         u32 type_bit_maps_size = item->type_bit_maps_size;
1970 
1971         /* Whatever the editor says: rdata_size is used. */
1972         u32 rdata_size = param_rdata_size + 1 + hash_len + type_bit_maps_size;
1973 
1974         zone_diff_fqdn_rr_set *nsec3_rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, TYPE_NSEC3);
1975         zone_diff_label_rr *rr = zone_diff_label_rr_new_nordata(fqdn, TYPE_NSEC3, CLASS_IN, ttl, rdata_size);
1976         nsec3_zone_item_to_rdata(n3, item, rr->rdata, rdata_size);
1977 
1978         rr->state |= ZONE_DIFF_RR_IN_ZONE;
1979         zone_diff_fqdn_rr_set_rr_add_replace(nsec3_rr_set, rr); /// NOTE: there should not be any collision here
1980         if(out_nsec3_rrset != NULL)
1981         {
1982             *out_nsec3_rrset = nsec3_rr_set;
1983         }
1984 
1985         zdb_packed_ttlrdata *nsec3_rrsig_rr_sll = (zdb_packed_ttlrdata*)item->rrsig;
1986 
1987         if(nsec3_rrsig_rr_sll != NULL)
1988         {
1989             zone_diff_fqdn_rr_set *nsec3_rrsig_rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, TYPE_RRSIG);
1990 
1991             nsec3_rrsig_rr_set->org_ttl = ttl;
1992             nsec3_rrsig_rr_set->new_ttl = ttl;
1993 
1994             while(nsec3_rrsig_rr_sll != NULL)
1995             {
1996                 zone_diff_label_rr *new_rr = zone_diff_label_rr_new(fqdn, TYPE_RRSIG, CLASS_IN, ttl, ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec3_rrsig_rr_sll), ZDB_PACKEDRECORD_PTR_RDATASIZE(nsec3_rrsig_rr_sll), FALSE);
1997                 new_rr->state |= ZONE_DIFF_RR_IN_ZONE;
1998                 /** rr = */ zone_diff_fqdn_rr_set_rr_add_replace(nsec3_rrsig_rr_set, new_rr); /// NOTE: there should not be any collision here
1999                 nsec3_rrsig_rr_sll = nsec3_rrsig_rr_sll->next;
2000             }
2001         }
2002     }
2003 #if DEBUG
2004     else
2005     {
2006         log_debug2("update: %{dnsname} (%p) already known (add nsec3)", fqdn, item);
2007     }
2008 #endif
2009 
2010     return (zone_diff_fqdn*)node->value;
2011 }
2012 
2013 zone_diff_fqdn*
zone_diff_add_nsec3_ex(zone_diff * diff,const ptr_vector * zsk_keys,const nsec3_zone * n3,const nsec3_node * item,s32 ttl,zone_diff_fqdn_rr_set ** out_nsec3_rrset,s32 now,s32 regeneration)2014 zone_diff_add_nsec3_ex(zone_diff *diff, const ptr_vector *zsk_keys, const nsec3_zone* n3, const nsec3_node *item, s32 ttl, zone_diff_fqdn_rr_set **out_nsec3_rrset, s32 now, s32 regeneration)
2015 {
2016     u8 digest_len = NSEC3_NODE_DIGEST_SIZE(item);
2017     u8 fqdn[MAX_DOMAIN_LENGTH];
2018 
2019     fqdn[0] = base32hex_encode(NSEC3_NODE_DIGEST_PTR(item), digest_len, (char*)&fqdn[1]);
2020     dnsname_copy(&fqdn[fqdn[0] + 1], diff->origin);
2021 
2022     ptr_node *node = ptr_set_insert(&diff->fqdn, fqdn);
2023 
2024     if(node->value == NULL)
2025     {
2026 #if DEBUG
2027         log_debug2("update: %{dnsname} (%p) ...", fqdn, item);
2028 #endif
2029 
2030         zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_new(fqdn);
2031         node->key = diff_fqdn->fqdn; // to guarantee the const
2032         node->value = diff_fqdn;
2033 
2034         zone_diff_label_tree *diff_fqdn_label = zone_diff_label_tree_add_fqdn(diff, diff_fqdn->fqdn);
2035         diff_fqdn_label->diff_fqdn = diff_fqdn;
2036 
2037         // copy all records
2038         //diff_fqdn->is_apex = 0;
2039         //diff_fqdn->at_delegation = 0;
2040         //diff_fqdn->under_delegation = 0;
2041         //diff_fqdn->will_have_ds = 0;
2042         //diff_fqdn->was_at_delegation = 0;
2043         //diff_fqdn->was_under_delegation = 0;
2044         //diff_fqdn->had_ds = 0;
2045         diff_fqdn->was_non_empty = 1;
2046         //diff_fqdn->had_children = 0;
2047         //diff_fqdn->will_have_children = 0;
2048         //diff_fqdn->children_added = 0;
2049         diff_fqdn->is_nsec3 = 1;
2050 
2051         diff_fqdn->has_active_zsk = diff->has_active_zsk;
2052         diff_fqdn->has_active_ksk = diff->has_active_ksk;
2053 
2054 #if DEBUG
2055         log_debug2("update: %{dnsname} (%p) copying NSEC3 record", fqdn, item);
2056 #endif
2057         u32 param_rdata_size = NSEC3_ZONE_RDATA_SIZE(n3);
2058         u8 hash_len = NSEC3_NODE_DIGEST_SIZE(item);
2059         u32 type_bit_maps_size = item->type_bit_maps_size;
2060 
2061         /* Whatever the editor says: rdata_size is used. */
2062         u32 rdata_size = param_rdata_size + 1 + hash_len + type_bit_maps_size;
2063 
2064         zone_diff_fqdn_rr_set *nsec3_rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, TYPE_NSEC3);
2065         zone_diff_label_rr *rr = zone_diff_label_rr_new_nordata(fqdn, TYPE_NSEC3, CLASS_IN, ttl, rdata_size);
2066         nsec3_zone_item_to_rdata(n3, item, rr->rdata, rdata_size);
2067 
2068         rr->state |= ZONE_DIFF_RR_IN_ZONE;
2069         zone_diff_fqdn_rr_set_rr_add_replace(nsec3_rr_set, rr); /// NOTE: there should not be any collision here
2070         if(out_nsec3_rrset != NULL)
2071         {
2072             *out_nsec3_rrset = nsec3_rr_set;
2073         }
2074 
2075         zdb_packed_ttlrdata *nsec3_rrsig_rr_sll = (zdb_packed_ttlrdata*)item->rrsig;
2076 
2077         if(nsec3_rrsig_rr_sll != NULL)
2078         {
2079             zone_diff_fqdn_rr_set *nsec3_rrsig_rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, TYPE_RRSIG);
2080 
2081             nsec3_rrsig_rr_set->org_ttl = ttl;
2082             nsec3_rrsig_rr_set->new_ttl = ttl;
2083 
2084             while(nsec3_rrsig_rr_sll != NULL)
2085             {
2086                 zone_diff_label_rr *new_rr = zone_diff_label_rr_new(fqdn, TYPE_RRSIG, CLASS_IN, ttl, ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec3_rrsig_rr_sll), ZDB_PACKEDRECORD_PTR_RDATASIZE(nsec3_rrsig_rr_sll), FALSE);
2087                 new_rr->state |= ZONE_DIFF_RR_IN_ZONE;
2088                 s32 matching_key_index = -2;
2089                 if(rrsig_should_remove_signature_from_rdata(
2090                         ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec3_rrsig_rr_sll), ZDB_PACKEDRECORD_PTR_RDATASIZE(nsec3_rrsig_rr_sll),
2091                         zsk_keys, now, regeneration, &matching_key_index) /* unnecessary: || (matching_key_index == -1)*/)
2092                 {
2093                     new_rr->state |= ZONE_DIFF_RR_REMOVE;
2094                 }
2095 
2096                 /** rr = */ zone_diff_fqdn_rr_set_rr_add_replace(nsec3_rrsig_rr_set, new_rr); /// NOTE: there should not be any collision here
2097                 nsec3_rrsig_rr_sll = nsec3_rrsig_rr_sll->next;
2098             }
2099         }
2100     }
2101 #if DEBUG
2102     else
2103     {
2104         log_debug2("update: %{dnsname} (%p) already known (add nsec3 ex)", fqdn, item);
2105     }
2106 #endif
2107 
2108     return (zone_diff_fqdn*)node->value;
2109 }
2110 
2111 #endif // HAS_NSEC3_SUPPORT
2112 
2113 zone_diff_fqdn*
zone_diff_add_static_fqdn(zone_diff * diff,const u8 * fqdn,zdb_rr_label * label)2114 zone_diff_add_static_fqdn(zone_diff *diff, const u8 *fqdn, zdb_rr_label *label)
2115 {
2116     zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_add(diff, fqdn, label);
2117     diff_fqdn->will_be_non_empty = diff_fqdn->was_non_empty;
2118     diff_fqdn->will_have_children = diff_fqdn->had_children;
2119     diff_fqdn->will_have_ds = diff_fqdn->had_ds && diff_fqdn->at_delegation;
2120     if(diff_fqdn->will_have_ds != diff_fqdn->had_ds)
2121     {
2122         // may be looking at a broken zone
2123         // it it only contains DS records (and RRSIG records) then it should be marked empty
2124 
2125         btree_iterator iter;
2126         btree_iterator_init(label->resource_record_set, &iter);
2127 
2128         while(btree_iterator_hasnext(&iter))
2129         {
2130             btree_node *rr_node = btree_iterator_next_node(&iter);
2131             u16 type = (u16)rr_node->hash;
2132             if((type != TYPE_RRSIG) && (type != TYPE_DS))
2133             {
2134                 return diff_fqdn;
2135             }
2136         }
2137 
2138         // the label will be emptied by validation later, the the NSEC3 chain doesn't know that yet.
2139 
2140         log_warn("update: %{dnsname}: %{dnsname} label only contained DS and RRSIG resource record sets: they will be removed", diff->origin, fqdn);
2141 
2142         diff_fqdn->will_be_non_empty = 0;
2143     }
2144     return diff_fqdn;
2145 }
2146 
2147 void
zone_diff_add_fqdn_children(zone_diff * diff,const u8 * fqdn,zdb_rr_label * label)2148 zone_diff_add_fqdn_children(zone_diff *diff, const u8 *fqdn, zdb_rr_label *label)
2149 {
2150     dictionary_iterator iter;
2151     u8 sub_fqdn[MAX_DOMAIN_LENGTH];
2152     dictionary_iterator_init(&label->sub, &iter);
2153 
2154     while(dictionary_iterator_hasnext(&iter))
2155     {
2156         zdb_rr_label *sub_label =  *(zdb_rr_label**)dictionary_iterator_next(&iter);
2157         dnsname_copy(&sub_fqdn[dnslabel_copy(sub_fqdn, sub_label->name)], fqdn);
2158         zone_diff_fqdn *parent = zone_diff_fqdn_add(diff, sub_fqdn, sub_label);
2159         parent->children_added = 1;
2160 
2161         if(dictionary_notempty(&sub_label->sub))
2162         {
2163             zone_diff_add_fqdn_children(diff, sub_fqdn, sub_label);
2164         }
2165     }
2166 }
2167 
2168 void
zone_diff_add_fqdn_parents_up_to_below_apex(zone_diff * diff,const u8 * fqdn,zdb_zone * zone)2169 zone_diff_add_fqdn_parents_up_to_below_apex(zone_diff *diff, const u8 *fqdn, zdb_zone *zone)
2170 {
2171     size_t origin_len = dnsname_len(diff->origin);
2172     fqdn += fqdn[0] + 1;
2173     while(dnsname_len(fqdn) > origin_len)
2174     {
2175         zdb_rr_label *fqdn_label = zdb_rr_label_find_from_name(zone, fqdn);
2176         zone_diff_fqdn *parent = zone_diff_fqdn_add(diff, fqdn, fqdn_label);
2177         parent->children_added = 1;
2178         fqdn += fqdn[0] + 1;
2179     }
2180 }
2181 
2182 /**
2183  * Enables the or_state flags in every record of the set.
2184  *
2185  * @param rrset
2186  * @param or_state
2187  */
2188 
2189 void
zone_diff_fqdn_rr_set_set_state(zone_diff_fqdn_rr_set * rrset,u8 or_state)2190 zone_diff_fqdn_rr_set_set_state(zone_diff_fqdn_rr_set *rrset, u8 or_state)
2191 {
2192     ptr_set_iterator rr_iter;
2193     ptr_set_iterator_init(&rrset->rr, &rr_iter);
2194     while(ptr_set_iterator_hasnext(&rr_iter))
2195     {
2196         ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2197         zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2198         rr->state |= or_state;
2199     }
2200 }
2201 
2202 /**
2203  * Returns true iff an rrset of the given type will be present after applying
2204  * the diff.
2205  *
2206  * @param diff_fqdn
2207  * @param rtype
2208  * @return
2209  */
2210 
2211 bool
zone_diff_will_have_rrset_type(const zone_diff_fqdn * diff_fqdn,u16 rtype)2212 zone_diff_will_have_rrset_type(const zone_diff_fqdn *diff_fqdn, u16 rtype)
2213 {
2214     u32_node *rrset_node = u32_set_find(&diff_fqdn->rrset, rtype);
2215     if(rrset_node != NULL)
2216     {
2217         zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)rrset_node->value;
2218 
2219         // @note 20170228 edf -- issue detection
2220         // If this aborts, it's likely somebody called zone_diff_fqdn_rr_get without
2221         // the intent of putting records in it.
2222         // Find it and call zone_diff_will_have_rrset_type instead.
2223         yassert(rrset != NULL);
2224 
2225         ptr_set_iterator rr_iter;
2226         ptr_set_iterator_init(&rrset->rr, &rr_iter);
2227         while(ptr_set_iterator_hasnext(&rr_iter))
2228         {
2229             ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2230             zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2231 
2232             if((rr->state & ZONE_DIFF_RR_REMOVE) == 0)
2233             {
2234                 // this record was present or is being added
2235                 return TRUE;
2236             }
2237         }
2238     }
2239     return FALSE;
2240 }
2241 
2242 bool
zone_diff_remove_rrsig_covering_type(zone_diff_fqdn * diff_fqdn,u16 rtype)2243 zone_diff_remove_rrsig_covering_type(zone_diff_fqdn *diff_fqdn, u16 rtype)
2244 {
2245     u32_node *rrsig_rrset_node = u32_set_find(&diff_fqdn->rrset, TYPE_RRSIG);
2246     if(rrsig_rrset_node != NULL)
2247     {
2248         zone_diff_fqdn_rr_set *rrsig_rrset = (zone_diff_fqdn_rr_set*)rrsig_rrset_node->value;
2249 
2250         // @note 20170228 edf -- issue detection
2251         // If this aborts, it's likely somebody called zone_diff_fqdn_rr_get without
2252         // the intent of putting records in it.
2253         // Find it and call zone_diff_will_have_rrset_type instead.
2254         yassert(rrsig_rrset != NULL);
2255 
2256         ptr_vector to_remove = PTR_VECTOR_EMPTY;
2257 
2258         ptr_set_iterator rr_iter;
2259         ptr_set_iterator_init(&rrsig_rrset->rr, &rr_iter);
2260         while(ptr_set_iterator_hasnext(&rr_iter))
2261         {
2262             ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2263             zone_diff_label_rr *rr = (zone_diff_label_rr*)node->key;
2264 
2265             if(rrsig_get_type_covered_from_rdata(rr->rdata, rr->rdata_size) == rtype)
2266             {
2267                 if((rr->state & ZONE_DIFF_RR_IN_ZONE) != 0) // did exist ?
2268                 {
2269                     // remove
2270                     rr->state |= ZONE_DIFF_RR_REMOVE;
2271 
2272                     log_debug2("update: %{dnsname} RRSIG covering %{dnstype} record will be removed from zone", rr->fqdn, &rtype);
2273                 }
2274                 else if((rr->state & ZONE_DIFF_RR_ADD) != 0) // was  being added ?
2275                 {
2276                     // remove the entry instead (postponed)
2277                     ptr_vector_append(&to_remove, rr);
2278 
2279                     log_debug2("update: %{dnsname} RRSIG covering %{dnstype} record will not be added to zone", rr->fqdn, &rtype);
2280                 }
2281                 else
2282                 {
2283                     //
2284 
2285                     log_warn("update: %{dnsname} RRSIG covering %{dnstype} record has state %02x, which is not expected", rr->fqdn, &rtype, rr->state);
2286                 }
2287             }
2288         }
2289 
2290         for(int i = 0; i <= ptr_vector_last_index(&to_remove); ++i)
2291         {
2292             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&to_remove, i);
2293             ptr_set_delete(&rrsig_rrset->rr, rr);
2294             zone_diff_label_rr_delete(rr);
2295         }
2296 
2297         if(ptr_set_isempty(&rrsig_rrset->rr))
2298         {
2299             u32_set_delete(&diff_fqdn->rrset, TYPE_RRSIG);
2300         }
2301 
2302         ptr_vector_destroy(&to_remove);
2303     }
2304     return FALSE;
2305 }
2306 
2307 /**
2308  *
2309  * Removes existing records as well as cancels additions of new ones.
2310  *
2311  * This is called by zone_diff_validate.
2312  * This means there is no rrset_to_sign collection yet.
2313  *
2314  */
2315 
2316 bool
zone_diff_remove_rrset_type(zone_diff_fqdn * diff_fqdn,u16 rtype)2317 zone_diff_remove_rrset_type(zone_diff_fqdn *diff_fqdn, u16 rtype)
2318 {
2319     u32_node *rrset_node = u32_set_find(&diff_fqdn->rrset, rtype);
2320     if(rrset_node != NULL)
2321     {
2322         zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)rrset_node->value;
2323 
2324         // @note 20170228 edf -- issue detection
2325         // If this aborts, it's likely somebody called zone_diff_fqdn_rr_get without
2326         // the intent of putting records in it.
2327         // Find it and call zone_diff_will_have_rrset_type instead.
2328         yassert(rrset != NULL);
2329 
2330         ptr_vector to_remove = PTR_VECTOR_EMPTY;
2331 
2332         ptr_set_iterator rr_iter;
2333         ptr_set_iterator_init(&rrset->rr, &rr_iter);
2334         while(ptr_set_iterator_hasnext(&rr_iter))
2335         {
2336             ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2337             zone_diff_label_rr *rr = (zone_diff_label_rr*)node->key;
2338 
2339             if((rr->state & ZONE_DIFF_RR_IN_ZONE) != 0) // did exist ?
2340             {
2341                 // remove
2342                 rr->state |= ZONE_DIFF_RR_REMOVE;
2343 
2344                 log_debug2("update: %{dnsname} %{dnstype} record will be removed from zone", rr->fqdn, &rtype);
2345             }
2346             else if((rr->state & ZONE_DIFF_RR_ADD) != 0) // was  being added ?
2347             {
2348                 // remove the entry instead (postponed)
2349                 ptr_vector_append(&to_remove, rr);
2350 
2351                 log_debug2("update: %{dnsname} %{dnstype} record will not be added to zone", rr->fqdn, &rtype);
2352             }
2353             else
2354             {
2355                 //
2356 
2357                 log_warn("update: %{dnsname} %{dnstype} record has state %02x, which is not expected", rr->fqdn, &rtype, rr->state);
2358             }
2359         }
2360 
2361         for(int i = 0; i <= ptr_vector_last_index(&to_remove); ++i)
2362         {
2363             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&to_remove, i);
2364             ptr_set_delete(&rrset->rr, rr);
2365             zone_diff_label_rr_delete(rr);
2366         }
2367 
2368         if(ptr_vector_last_index(&to_remove) >= 0)
2369         {
2370             if(ptr_set_isempty(&rrset->rr))
2371             {
2372                 u32_set_delete(&diff_fqdn->rrset, rtype);
2373             }
2374 
2375             zone_diff_remove_rrsig_covering_type(diff_fqdn, rtype);
2376         }
2377 
2378         ptr_vector_destroy(&to_remove);
2379     }
2380     return FALSE;
2381 }
2382 
2383 /**
2384  * Returns true iff a DNSKEY with these exact parameters will be present in the zone after the diff.
2385  *
2386  * @param diff_fqdn
2387  * @param algorithm
2388  * @param flags
2389  * @param tag
2390  * @return
2391  */
2392 
2393 bool
zone_diff_will_have_dnskey_with_algorithm_flags_tag(const zone_diff_fqdn * diff_fqdn,u8 algorithm,u16 flags,u16 tag)2394 zone_diff_will_have_dnskey_with_algorithm_flags_tag(const zone_diff_fqdn *diff_fqdn, u8 algorithm, u16 flags, u16 tag)
2395 {
2396     u32_node *rrset_node = u32_set_find(&diff_fqdn->rrset, TYPE_DNSKEY);
2397     if(rrset_node != NULL)
2398     {
2399         zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)rrset_node->value;
2400 
2401         // @note 20170228 edf -- issue detection
2402         // If this aborts, it's likely somebody called zone_diff_fqdn_rr_get without
2403         // the intent of putting records in it.
2404         // Find it and call zone_diff_will_have_rrset_type instead.
2405         yassert(rrset != NULL);
2406 
2407         ptr_set_iterator rr_iter;
2408         ptr_set_iterator_init(&rrset->rr, &rr_iter);
2409         while(ptr_set_iterator_hasnext(&rr_iter))
2410         {
2411             ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2412             zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2413 
2414             if((rr->state & ZONE_DIFF_RR_REMOVE) == 0)
2415             {
2416                 // this record was present or is being added
2417                 if(rr->rdata_size > 3)
2418                 {
2419                     if(dnskey_get_algorithm_from_rdata(rr->rdata) == algorithm)
2420                     {
2421                         if(dnskey_get_flags_from_rdata(rr->rdata) == flags)
2422                         {
2423                             if(dnskey_get_tag_from_rdata(rr->rdata, rr->rdata_size) == tag)
2424                             {
2425                                 return TRUE;
2426                             }
2427                         }
2428                     }
2429                 }
2430             }
2431         }
2432     }
2433     return FALSE;
2434 }
2435 
2436 /**
2437  * Returns true iff a DNSKEY with these exact parameters will be present in the zone after the diff.
2438  *
2439  * @param diff_fqdn
2440  * @param algorithm
2441  * @param flags
2442  * @param tag
2443  * @return
2444  */
2445 
2446 bool
zone_diff_will_have_dnskey_with_algorithm_tag(const zone_diff_fqdn * diff_fqdn,u8 algorithm,u16 tag)2447 zone_diff_will_have_dnskey_with_algorithm_tag(const zone_diff_fqdn *diff_fqdn, u8 algorithm, u16 tag)
2448 {
2449     u32_node *rrset_node = u32_set_find(&diff_fqdn->rrset, TYPE_DNSKEY);
2450     if(rrset_node != NULL)
2451     {
2452         zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)rrset_node->value;
2453 
2454         // @note 20170228 edf -- issue detection
2455         // If this aborts, it's likely somebody called zone_diff_fqdn_rr_get without
2456         // the intent of putting records in it.
2457         // Find it and call zone_diff_will_have_rrset_type instead.
2458         yassert(rrset != NULL);
2459 
2460         ptr_set_iterator rr_iter;
2461         ptr_set_iterator_init(&rrset->rr, &rr_iter);
2462         while(ptr_set_iterator_hasnext(&rr_iter))
2463         {
2464             ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2465             zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2466 
2467             if((rr->state & ZONE_DIFF_RR_REMOVE) == 0)
2468             {
2469                 // this record was present or is being added
2470                 if(rr->rdata_size > 3)
2471                 {
2472                     if(dnskey_get_algorithm_from_rdata(rr->rdata) == algorithm)
2473                     {
2474                         if(dnskey_get_tag_from_rdata(rr->rdata, rr->rdata_size) == tag)
2475                         {
2476                             return TRUE;
2477                         }
2478                     }
2479                 }
2480             }
2481         }
2482     }
2483     return FALSE;
2484 }
2485 
2486 /**
2487  * Releases keys that will not be in the apex after the diff is applied.
2488  *
2489  * @param diff
2490  * @param keys
2491  */
2492 
2493 void
zone_diff_filter_out_keys(const zone_diff * diff,ptr_vector * keys)2494 zone_diff_filter_out_keys(const zone_diff *diff, ptr_vector *keys)
2495 {
2496     const zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_get_const(diff, diff->origin);
2497     if(diff_fqdn != NULL)
2498     {
2499         for(int i = 0; i <= ptr_vector_last_index(keys); ++i)
2500         {
2501             dnssec_key *key = (dnssec_key*)ptr_vector_get(keys, i);
2502 
2503 #if !DEBUG
2504             if(!dnskey_is_private(key) || !zone_diff_will_have_dnskey_with_algorithm_flags_tag(diff_fqdn,
2505                                                                                                dnskey_get_algorithm(
2506                                                                                                    key),
2507                                                                                                dnskey_get_flags(key),
2508                                                                                                dnskey_get_tag(key)))
2509             {
2510                 ptr_vector_end_swap(keys, i);
2511                 ptr_vector_pop(keys);
2512                 dnskey_release(key);
2513             }
2514 #else
2515             /*if(!dnskey_is_private(key))
2516             {
2517                 log_debug3("zone_diff_filter_out_keys: 'K%{dnsname}+%03d+%05hd' is not private", diff->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
2518 
2519                 ptr_vector_end_swap(keys, i);
2520                 ptr_vector_pop(keys);
2521                 dnskey_release(key);
2522             }
2523             else*/ if(!zone_diff_will_have_dnskey_with_algorithm_flags_tag(diff_fqdn, dnskey_get_algorithm(key), dnskey_get_flags(key), dnskey_get_tag(key)))
2524             {
2525                 log_debug3("zone_diff_filter_out_keys: 'K%{dnsname}+%03d+%05hd' will not be in the zone", diff->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
2526 
2527                 ptr_vector_end_swap(keys, i);
2528                 ptr_vector_pop(keys);
2529                 dnskey_release(key);
2530             }
2531 #endif
2532         }
2533     }
2534 }
2535 
2536 /**
2537  * find label for fqdn ...
2538  *
2539  * @param diff
2540  * @param fqdn
2541  * @param label
2542  * @return
2543  */
2544 
2545 const zone_diff_fqdn*
zone_diff_fqdn_get_const(const zone_diff * diff,const u8 * fqdn)2546 zone_diff_fqdn_get_const(const zone_diff *diff, const u8 *fqdn)
2547 {
2548     zone_diff_fqdn *ret = NULL;
2549     ptr_node *node = ptr_set_find(&diff->fqdn, (u8*)fqdn);
2550     if(node != NULL)
2551     {
2552         ret = (zone_diff_fqdn*)node->value;
2553     }
2554     return ret;
2555 }
2556 
2557 zone_diff_fqdn*
zone_diff_fqdn_get(const zone_diff * diff,const u8 * fqdn)2558 zone_diff_fqdn_get(const zone_diff *diff, const u8 *fqdn)
2559 {
2560     zone_diff_fqdn *ret = NULL;
2561     ptr_node *node = ptr_set_find(&diff->fqdn, (u8*)fqdn);
2562     if(node != NULL)
2563     {
2564         ret = (zone_diff_fqdn*)node->value;
2565     }
2566     return ret;
2567 }
2568 
2569 /**
2570  * Generates a type bit map based on the diff including records matching:
2571  *
2572  * (status & mask) == masked
2573  *
2574  * mask,masked
2575  *      all pre records : ZONE_DIFF_REMOVE|ZONE_DIFF_ADD == 0
2576  *      all post records: ZONE_DIFF_REMOVE = 0
2577  *
2578  * Note: it ignores A and AAAA records at or under a delegation
2579  *
2580  * @param diff
2581  * @param fqdn
2582  * @param bitmap
2583  * @param mask
2584  * @param masked
2585  * @return
2586  */
2587 
2588 u16
zone_diff_type_bit_map_generate(const zone_diff * diff,const u8 * fqdn,type_bit_maps_context * bitmap,u8 mask,u8 masked,const u8 * chain_node_fqdn,bool append_existing_signatures)2589 zone_diff_type_bit_map_generate(const zone_diff *diff, const u8 *fqdn, type_bit_maps_context *bitmap, u8 mask,
2590                                 u8 masked, const u8 *chain_node_fqdn, bool append_existing_signatures)
2591 {
2592     type_bit_maps_init(bitmap);
2593 
2594     const zone_diff_fqdn* zdf = zone_diff_fqdn_get_const(diff, fqdn);
2595 
2596     if(zdf != NULL)
2597     {
2598         if(zdf->at_delegation || zdf->under_delegation)
2599         {
2600             ptr_set_iterator rr_iter;
2601             u32_set_iterator iter;
2602             u32_set_iterator_init(&zdf->rrset, &iter);
2603             while(u32_set_iterator_hasnext(&iter))
2604             {
2605                 u32_node *node = u32_set_iterator_next_node(&iter);
2606                 u16 rtype = (u16)node->key;
2607 
2608                 if((rtype == TYPE_A) || (rtype == TYPE_AAAA))
2609                 {
2610                     continue;
2611                 }
2612 
2613                 zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)node->value;
2614 
2615                 ptr_set_iterator_init(&rrset->rr, &rr_iter);
2616                 while(ptr_set_iterator_hasnext(&rr_iter))
2617                 {
2618                     ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2619                     zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2620 
2621                     if((rr->state & mask) == masked)
2622                     {
2623                         log_debug1("update: %{dnsname}: %{dnsname}: %x: %{dnstype}", diff->origin, chain_node_fqdn, mask, &rtype);
2624 
2625                         type_bit_maps_set_type(bitmap, rtype);
2626                         break;
2627                     }
2628                 }
2629             }
2630         }
2631         else
2632         {
2633             ptr_set_iterator rr_iter;
2634             u32_set_iterator iter;
2635             u32_set_iterator_init(&zdf->rrset, &iter);
2636             while(u32_set_iterator_hasnext(&iter))
2637             {
2638                 u32_node *node = u32_set_iterator_next_node(&iter);
2639                 u16 rtype = (u16)node->key;
2640 
2641                 zone_diff_fqdn_rr_set *rrset = (zone_diff_fqdn_rr_set*)node->value;
2642 
2643                 ptr_set_iterator_init(&rrset->rr, &rr_iter);
2644                 while(ptr_set_iterator_hasnext(&rr_iter))
2645                 {
2646                     ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2647                     zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2648 
2649                     if((rr->state & mask) == masked)
2650                     {
2651                         log_debug1("update: %{dnsname}: %{dnsname}: %x: %{dnstype}", diff->origin, chain_node_fqdn, mask, &rtype);
2652 
2653                         type_bit_maps_set_type(bitmap, rtype);
2654                         break;
2655                     }
2656                 }
2657             }
2658         }
2659 
2660         if(append_existing_signatures)
2661         {
2662             if((zdf->rrsig_kept == 0) && zdf->rrsig_added)
2663             {
2664                 type_bit_maps_set_type(bitmap, TYPE_RRSIG);
2665             }
2666         }
2667     }
2668     else
2669     {
2670         log_debug1("update: %{dnsname}: %{dnsname}: %x: no matching fqdn in the diff", diff->origin, chain_node_fqdn, mask);
2671     }
2672 
2673     u16 bitmap_size = type_bit_maps_update_size(bitmap);
2674 
2675     return bitmap_size;
2676 }
2677 
2678 /**
2679  * Adds a record on a diff
2680  *
2681  *
2682  * @param diff
2683  * @param rr_label
2684  * @param fqdn
2685  * @param rtype
2686  * @param rttl
2687  * @param rdata_size
2688  * @param rdata
2689  */
2690 
2691 zone_diff_label_rr*
zone_diff_record_add(zone_diff * diff,zdb_rr_label * rr_label,const u8 * fqdn,u16 rtype,s32 rttl,u16 rdata_size,void * rdata)2692 zone_diff_record_add(zone_diff *diff, zdb_rr_label *rr_label, const u8 *fqdn, u16 rtype, s32 rttl, u16 rdata_size, void *rdata)
2693 {
2694     zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_add(diff, fqdn, rr_label);
2695     zone_diff_fqdn_rr_set *rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, rtype);
2696     zone_diff_label_rr *rr = zone_diff_label_rr_new(fqdn, rtype, CLASS_IN, rttl, rdata, rdata_size, TRUE);
2697     rr = zone_diff_fqdn_rr_set_rr_add_get(rr_set, rr);
2698 
2699 #if DEBUG
2700     rdata_desc rd = {rtype, rdata_size, rdata};
2701     log_debug2("update: %{dnsname}: will add [%02x] %{dnsname} %5i %{typerdatadesc}", diff->origin, rr->state, fqdn, rttl, &rd);
2702 #endif
2703 
2704     if( ((rr->state & ZONE_DIFF_RR_IN_ZONE) != 0) && ((rr->state & ZONE_DIFF_RR_REMOVE) != 0) )
2705     {
2706         //rr->state |= ZONE_DIFF_RR_ADD;
2707         rr->state &= ~ZONE_DIFF_RR_REMOVE;
2708 #if DEBUG
2709         log_debug2("update: %{dnsname}: will add [%02x] %{dnsname} %5i %{typerdatadesc} (no add needed, cleared del)", diff->origin, rr->state, fqdn, rttl, &rd);
2710 #endif
2711     }
2712     else if( ((rr->state & ZONE_DIFF_RR_IN_ZONE) == 0) || ((rr->state & ZONE_DIFF_RR_REMOVE) != 0) )
2713     {
2714         rr->state |= ZONE_DIFF_RR_ADD;
2715 #if DEBUG
2716         log_debug2("update: %{dnsname}: will add [%02x] %{dnsname} %5i %{typerdatadesc} (set  add)", diff->origin, rr->state, fqdn, rttl, &rd);
2717 #endif
2718     }
2719 
2720     return rr;
2721 }
2722 
2723 /**
2724  *
2725  * Adds the removal of a specific record on a diff
2726  *
2727  * @param diff
2728  * @param rr_label
2729  * @param fqdn
2730  * @param rtype
2731  * @param rttl
2732  * @param rdata_size
2733  * @param rdata
2734  */
2735 
2736 void
zone_diff_record_remove(zone_diff * diff,zdb_rr_label * rr_label,const u8 * fqdn,u16 rtype,s32 rttl,u16 rdata_size,void * rdata)2737 zone_diff_record_remove(zone_diff *diff, zdb_rr_label *rr_label, const u8 *fqdn, u16 rtype, s32 rttl, u16 rdata_size, void *rdata)
2738 {
2739 #if DEBUG
2740     rdata_desc rd = {rtype, rdata_size, rdata};
2741     log_debug2("update: %{dnsname}: will del %{dnsname} %5i %{typerdatadesc}", diff->origin, fqdn, rttl, &rd);
2742 #endif
2743     (void)rttl;
2744     zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_add(diff, fqdn, rr_label);
2745     zone_diff_fqdn_rr_set *rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, rtype);
2746     zone_diff_label_rr *rr = zone_diff_label_rr_new(fqdn, rtype, CLASS_IN, 0, rdata, rdata_size, TRUE);
2747     rr = zone_diff_fqdn_rr_set_rr_add_get(rr_set, rr);
2748     rr->state |= ZONE_DIFF_RR_REMOVE;
2749 }
2750 
2751 bool
zone_diff_record_remove_existing(zone_diff * diff,zdb_rr_label * rr_label,const u8 * fqdn,u16 rtype,s32 rttl,u16 rdata_size,void * rdata)2752 zone_diff_record_remove_existing(zone_diff *diff, zdb_rr_label *rr_label, const u8 *fqdn, u16 rtype, s32 rttl, u16 rdata_size, void *rdata)
2753 {
2754 #if DEBUG
2755     rdata_desc rd = {rtype, rdata_size, rdata};
2756     log_debug2("update: %{dnsname}: will del %{dnsname} %5i %{typerdatadesc}", diff->origin, fqdn, rttl, &rd);
2757 #endif
2758     (void)rttl;
2759     zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_add(diff, fqdn, rr_label);
2760     zone_diff_fqdn_rr_set *rr_set = zone_diff_fqdn_rr_set_get(diff_fqdn, rtype);
2761     if(rr_set != NULL)
2762     {
2763         zone_diff_label_rr tmp_rr;
2764         zone_diff_label_rr_init_tmp(&tmp_rr, fqdn, rtype, CLASS_IN, 0, rdata, rdata_size);
2765         zone_diff_label_rr *rr = zone_diff_fqdn_rr_set_get_existing_rr(rr_set, &tmp_rr);
2766         if(rr != NULL)
2767         {
2768             rr->state |= ZONE_DIFF_RR_REMOVE;
2769             return TRUE;
2770         }
2771     }
2772 
2773     return FALSE;
2774 }
2775 
2776 void
zone_diff_record_remove_automated(zone_diff * diff,zdb_rr_label * rr_label,const u8 * fqdn,u16 rtype,s32 rttl,u16 rdata_size,void * rdata)2777 zone_diff_record_remove_automated(zone_diff *diff, zdb_rr_label *rr_label, const u8 *fqdn, u16 rtype, s32 rttl, u16 rdata_size, void *rdata)
2778 {
2779 #if DEBUG
2780     rdata_desc rd = {rtype, rdata_size, rdata};
2781     log_debug2("update: %{dnsname}: will del %{dnsname} %5i %{typerdatadesc}", diff->origin, fqdn, rttl, &rd);
2782 #endif
2783     (void)rttl;
2784     zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_add(diff, fqdn, rr_label);
2785     zone_diff_fqdn_rr_set *rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, rtype);
2786     zone_diff_label_rr *rr = zone_diff_label_rr_new(fqdn, rtype, CLASS_IN, 0, rdata, rdata_size, TRUE);
2787     rr = zone_diff_fqdn_rr_set_rr_add_get(rr_set, rr);
2788     rr->state |= ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_AUTOMATED;
2789 }
2790 
2791 /**
2792  * Adds the removal of a record set on a diff
2793  *
2794  * @param diff
2795  * @param rr_label
2796  * @param fqdn
2797  * @param rtype
2798  */
2799 
zone_diff_record_remove_all(zone_diff * diff,zdb_rr_label * rr_label,const u8 * fqdn,u16 rtype)2800 void zone_diff_record_remove_all(zone_diff *diff, zdb_rr_label *rr_label, const u8 *fqdn, u16 rtype)
2801 {
2802     zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_add(diff, fqdn, rr_label);
2803     zone_diff_fqdn_rr_set *rr_set = zone_diff_fqdn_rr_set_add(diff_fqdn, rtype);
2804 
2805     ptr_set_iterator iter;
2806     ptr_set_iterator_init(&rr_set->rr, &iter);
2807     while(ptr_set_iterator_hasnext(&iter))
2808     {
2809         ptr_node *node = ptr_set_iterator_next_node(&iter);
2810         zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2811         rr->state |= ZONE_DIFF_RR_REMOVE;
2812     }
2813 }
2814 
2815 /**
2816  * Adds the removal all record sets on a diff
2817  *
2818  * @param diff
2819  * @param rr_label
2820  * @param fqdn
2821  * @param rtype
2822  */
2823 
2824 void
zone_diff_record_remove_all_sets(zone_diff * diff,zdb_rr_label * rr_label,const u8 * fqdn)2825 zone_diff_record_remove_all_sets(zone_diff *diff, zdb_rr_label *rr_label, const u8 *fqdn)
2826 {
2827     zone_diff_fqdn *diff_fqdn = zone_diff_fqdn_add(diff, fqdn, rr_label);
2828 
2829     u32_set_iterator typeiter;
2830     u32_set_iterator_init(&diff_fqdn->rrset, &typeiter);
2831     while(u32_set_iterator_hasnext(&typeiter))
2832     {
2833         u32_node* node = u32_set_iterator_next_node(&typeiter);
2834 
2835         yassert((node != NULL) && (node->value != NULL));
2836 
2837         zone_diff_fqdn_rr_set *rr_set = (zone_diff_fqdn_rr_set*)node->value;
2838 
2839         ptr_set_iterator iter;
2840         ptr_set_iterator_init(&rr_set->rr, &iter);
2841         while(ptr_set_iterator_hasnext(&iter))
2842         {
2843             ptr_node *node = ptr_set_iterator_next_node(&iter);
2844             zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2845             rr->state |= ZONE_DIFF_RR_REMOVE;
2846         }
2847     }
2848 }
2849 
2850 /**
2851  * Adds the SOA records for the incremental update.
2852  *
2853  * @param diff
2854  * @return
2855  */
2856 
2857 ya_result
zone_diff_set_soa(zone_diff * diff,zdb_rr_label * label)2858 zone_diff_set_soa(zone_diff *diff, zdb_rr_label *label)
2859 {
2860     /**************************************************************************
2861      * SOA HANDLING
2862      **************************************************************************/
2863 
2864     // check the SOA
2865     // expects 1 record, "removed", then add 1 added with incremented serial
2866     // else one and only one should be seen as "added" (and not removed), then do nothing
2867     // else still add 1 added incremented serial
2868 
2869     // if one (and only one, more being an error) SOA is marked as added, then do nothing
2870     // else add one with incremented serial based on the highest found serial
2871 
2872     zone_diff_fqdn *apex = zone_diff_fqdn_add(diff, diff->origin, label);
2873     zone_diff_fqdn_rr_set *soa_rrset = zone_diff_fqdn_rr_set_add(apex, TYPE_SOA);
2874 
2875     //ptr_set_iterator fqdn_iter;
2876     ptr_set_iterator rr_iter;
2877 
2878     zone_diff_label_rr *rr_soa_removed = NULL;
2879     zone_diff_label_rr *rr_soa_added = NULL;
2880     u32 soa_latest_serial;
2881     ya_result ret;
2882 
2883     ptr_set_iterator_init(&soa_rrset->rr, &rr_iter);
2884     while(ptr_set_iterator_hasnext(&rr_iter))
2885     {
2886         ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
2887         zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
2888 
2889 #if DEBUG
2890         rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
2891         log_debug1("update: %{dnsname}: SOA[%x] %{dnsname} %9i %{typerdatadesc}", diff->origin, rr->state, rr->fqdn, rr->ttl, &rd);
2892 #endif
2893 
2894         if(rr->state & ZONE_DIFF_RR_REMOVE)
2895         {
2896             u32 soa_serial;
2897 
2898             if(FAIL(ret = rr_soa_get_serial(rr->rdata, rr->rdata_size, &soa_serial)))
2899             {
2900                 // error
2901                 return ret;
2902             }
2903 
2904             if(rr_soa_removed == NULL)
2905             {
2906                 soa_latest_serial = soa_serial;
2907                 rr_soa_removed = rr;
2908             }
2909             else
2910             {
2911                 soa_latest_serial = serial_max(soa_latest_serial, soa_serial); // soa_latest_serial is initialized
2912                 if(serial_lt(soa_latest_serial, soa_serial))
2913                 {
2914                     rr_soa_removed = rr;
2915                 }
2916             }
2917         }
2918 
2919         if((rr->state & (ZONE_DIFF_RR_ADD | ZONE_DIFF_RR_REMOVE)) == ZONE_DIFF_RR_ADD) // VS false positive: rr is a key and can't be NULL
2920         {
2921             if(rr_soa_added != NULL)
2922             {
2923                 return INVALID_STATE_ERROR; // two SOA added ...
2924             }
2925 
2926             rr_soa_added = rr;
2927         }
2928     }
2929 
2930     if(rr_soa_removed == NULL)
2931     {
2932         return INVALID_STATE_ERROR;
2933     }
2934 
2935     if(rr_soa_added != NULL)
2936     {
2937         u32 soa_serial;
2938 
2939         if(FAIL(ret = rr_soa_get_serial(rr_soa_added->rdata, rr_soa_added->rdata_size, &soa_serial)))
2940         {
2941             // error
2942 
2943             return ret;
2944         }
2945 
2946         if(serial_le(soa_serial, soa_latest_serial)) // soa_latest_serial is initialized
2947         {
2948             // error
2949 
2950             return INVALID_STATE_ERROR;
2951         }
2952     }
2953     else
2954     {
2955         // add the SOA add record
2956 
2957 #if C11_VLA_AVAILABLE
2958         u8 tmp_rdata[rr_soa_removed->rdata_size];
2959 #else
2960         u8* const tmp_rdata = (u8* const)stack_alloc(rr_soa_removed->rdata_size);
2961 #endif
2962 
2963         memcpy(tmp_rdata, rr_soa_removed->rdata, rr_soa_removed->rdata_size);
2964         rr_soa_increase_serial(tmp_rdata, rr_soa_removed->rdata_size, 1);
2965         rr_soa_added = zone_diff_label_rr_new(rr_soa_removed->fqdn, TYPE_SOA, CLASS_IN, rr_soa_removed->ttl, tmp_rdata, rr_soa_removed->rdata_size, TRUE);
2966         rr_soa_added = zone_diff_fqdn_rr_set_rr_add_get(soa_rrset, rr_soa_added); // add_get
2967         rr_soa_added->state |= ZONE_DIFF_RR_ADD  | ZONE_DIFF_RR_AUTOMATED;
2968     }
2969 
2970     return SUCCESS;
2971 }
2972 
2973 /**
2974  * Updates status and validates a diff.
2975  *
2976  * @param diff
2977  * @return
2978  */
2979 
2980 ya_result
zone_diff_validate(zone_diff * diff)2981 zone_diff_validate(zone_diff *diff)
2982 {
2983     ptr_set_iterator fqdn_iter;
2984 
2985     ptr_vector diff_fqdn_to_remove = EMPTY_PTR_VECTOR;
2986 
2987     ptr_set_iterator_init(&diff->fqdn, &fqdn_iter);
2988     while(ptr_set_iterator_hasnext(&fqdn_iter))
2989     {
2990         ptr_node *diff_fqdn_node = ptr_set_iterator_next_node(&fqdn_iter);
2991 
2992         const u8 *diff_fqdn_name = (const u8*)diff_fqdn_node->key;
2993         zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)diff_fqdn_node->value;
2994 
2995         // update status flags
2996         // do validation tests
2997 
2998         log_debug2("update: %{dnsname}: validating %{dnsname}", diff->origin, diff_fqdn_name);
2999 
3000         if(diff_fqdn->is_apex)
3001         {
3002             // only check for CNAME
3003 
3004             if(zone_diff_will_have_rrset_type(diff_fqdn, TYPE_CNAME))
3005             {
3006                 log_err("update: %{dnsname}: update would add CNAME on apex", diff->origin);
3007 
3008                 //dnssec_chain_finalize(&dc);
3009 
3010                 return INVALID_STATE_ERROR;
3011             }
3012         }
3013         else
3014         {
3015             // check for CNAME
3016 
3017             // update under-delegation
3018             //
3019             //      for all labels above, look in the diff if they are present and if their delegation status will be changed
3020 
3021             bool under_delegation = FALSE;
3022 
3023             const u8 *above_fqdn = diff_fqdn->fqdn;
3024             bool is_right_above = TRUE;
3025             while(*above_fqdn != 0)
3026             {
3027                 above_fqdn += *above_fqdn + 1;
3028 
3029                 const zone_diff_fqdn *parent = zone_diff_fqdn_get_const(diff, above_fqdn);
3030 
3031                 if(parent != NULL)
3032                 {
3033                     if(parent->is_apex)
3034                     {
3035                         break;
3036                     }
3037 
3038                     if(is_right_above)
3039                     {
3040                         if((parent->was_at_delegation || parent->was_under_delegation) && !diff_fqdn->was_under_delegation)
3041                         {
3042                             // then we are at delegation
3043 #if DEBUG
3044                             if(diff_fqdn->is_in_database)
3045                             {
3046                                 log_warn("update: %{dnsname}: %{dnsname} expected to be marked as being under delegation in the database as %{dnsname} at=%i under=%i (fixing)", diff->origin,
3047                                           diff_fqdn->fqdn, parent->fqdn, parent->was_at_delegation, parent->was_under_delegation);
3048                             }
3049 #endif
3050                             diff_fqdn->was_under_delegation = TRUE;
3051                         }
3052                         else if(!((parent->was_at_delegation || parent->was_under_delegation)) && diff_fqdn->was_under_delegation)
3053                         {
3054                             // then we are at delegation
3055 #if DEBUG
3056                             if(diff_fqdn->is_in_database)
3057                             {
3058                                 log_warn("update: %{dnsname}: %{dnsname} not expected to be marked as being under delegation in the database as %{dnsname} at=%i under=%i (fixing)", diff->origin,
3059                                          diff_fqdn->fqdn, parent->fqdn, parent->was_at_delegation, parent->was_under_delegation);
3060                             }
3061 #endif
3062                             diff_fqdn->was_under_delegation = FALSE;
3063                         }
3064 
3065                         is_right_above = FALSE;
3066                     }
3067 
3068                     if(parent->under_delegation)
3069                     {
3070                         if(!diff_fqdn->under_delegation)
3071                         {
3072                             log_debug("update: %{dnsname}: %{dnsname} under under delegation %{dnsname}", diff->origin,
3073                                     diff_fqdn->fqdn, parent->fqdn);
3074                         }
3075                         under_delegation = TRUE;
3076                         break;
3077                     }
3078 
3079                     if(parent->at_delegation)
3080                     {
3081                         if(!diff_fqdn->under_delegation)
3082                         {
3083                             log_debug1("update: %{dnsname}: %{dnsname} under delegation %{dnsname}", diff->origin,
3084                                     diff_fqdn->fqdn, parent->fqdn);
3085                         }
3086                         under_delegation = TRUE;
3087                         break;
3088                     }
3089                 }
3090                 /*else
3091                 {
3092                     under_delegation = diff_fqdn->under_delegation;
3093                 }*/
3094             }
3095 
3096             if(diff_fqdn->under_delegation && !under_delegation)
3097             {
3098                 log_debug1("update: %{dnsname}: %{dnsname} not under delegation anymore", diff->origin, diff_fqdn->fqdn);
3099             }
3100 
3101             diff_fqdn->under_delegation = under_delegation;
3102 
3103             // update delegation
3104             //
3105             //
3106 
3107             if(zone_diff_will_have_rrset_type(diff_fqdn, TYPE_NS))
3108             {
3109                 diff_fqdn->at_delegation = TRUE;
3110 
3111                 // check there will be only glue records under this level
3112             }
3113             else
3114             {
3115                 diff_fqdn->at_delegation = FALSE;
3116             }
3117 
3118             diff_fqdn->will_have_ds = zone_diff_will_have_rrset_type(diff_fqdn, TYPE_DS);
3119 
3120             if(diff_fqdn->will_have_ds && !diff_fqdn->at_delegation)
3121             {
3122                 log_debug1("update: %{dnsname}: %{dnsname} will have a DS but no NS : removing all DS", diff->origin, diff_fqdn->fqdn);
3123 
3124                 zone_diff_remove_rrset_type(diff_fqdn, TYPE_DS);
3125                 diff_fqdn->will_have_ds = 0;
3126 
3127                 if(u32_set_isempty(&diff_fqdn->rrset))
3128                 {
3129                     ptr_vector_append(&diff_fqdn_to_remove, diff_fqdn_node);
3130                 }
3131 
3132                 // TODO: remove NSEC3 record
3133             }
3134         }
3135 
3136         log_debug2("update: %{dnsname}: validating %{dnsname}: apex=%i at=%i under=%i ds=%i was-at=%i was-under=%i had-ds=%i",
3137                 diff->origin, diff_fqdn_name,
3138                 diff_fqdn->is_apex, diff_fqdn->at_delegation, diff_fqdn->under_delegation, diff_fqdn->will_have_ds,
3139                 diff_fqdn->was_at_delegation, diff_fqdn->was_under_delegation, diff_fqdn->had_ds
3140                 );
3141     }
3142 
3143     for(int i = 0; i <= ptr_vector_last_index(&diff_fqdn_to_remove); ++i)
3144     {
3145         ptr_node *diff_fqdn_node = (ptr_node*)ptr_vector_get(&diff_fqdn_to_remove, i);
3146         //const u8 *diff_fqdn_name = (const u8*)diff_fqdn_node->key;
3147         zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)diff_fqdn_node->value;
3148         ptr_set_delete(&diff->fqdn, diff_fqdn->fqdn);   // remove the node
3149 
3150         // if diff_fqdn is not in the database
3151         //   from diff->root, remove the fqdn with attention to empty terminal not in the database
3152         //   and
3153 
3154         // zone_diff_fqdn_delete(diff_fqdn);               // delete the data
3155     }
3156     ptr_vector_destroy(&diff_fqdn_to_remove);
3157 
3158     return SUCCESS;
3159 }
3160 
3161 struct zone_diff_get_changes_update_rr_parm
3162 {
3163     u8 changes;
3164     bool rrset_removed;
3165     bool rrset_new;
3166     bool all_rrset_added;
3167     bool all_rrset_removed;
3168     bool non_empty;
3169 };
3170 
3171 static void
zone_diff_get_changes_update_rrsig_rr(zone_diff_fqdn_rr_set * rr_set,struct zone_diff_get_changes_update_rr_parm * parm,ptr_vector * remove,ptr_vector * add)3172 zone_diff_get_changes_update_rrsig_rr(zone_diff_fqdn_rr_set *rr_set, struct zone_diff_get_changes_update_rr_parm *parm, ptr_vector *remove, ptr_vector *add)
3173 {
3174     u8 changes = parm->changes;
3175     bool rrset_removed = parm->rrset_removed;
3176     bool all_rrset_added = parm->all_rrset_added;
3177     bool all_rrset_removed = parm->all_rrset_removed;
3178     bool rrset_new = TRUE;
3179 
3180     ptr_set_iterator rr_iter;
3181 
3182     // for all marked rr
3183 
3184     ptr_set_iterator_init(&rr_set->rr, &rr_iter);
3185     while(ptr_set_iterator_hasnext(&rr_iter))
3186     {
3187         ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
3188         zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
3189 
3190         yassert(rr->rtype == TYPE_RRSIG);
3191 
3192         if((rr->state & (ZONE_DIFF_RR_IN_ZONE|ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADDED)) == ZONE_DIFF_RR_ADD)
3193         {
3194             // add
3195 #if DEBUG
3196             rdata_desc rrsig_rr_rd = {rr->rtype, rr->rdata_size, rr->rdata};
3197             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
3198             log_debug1("update: add %w %{dnsname} %9i %{typerdatadesc} (zone_diff_get_changes_update_rrsig_rr %p)",
3199                     &temp_fw_0, rr->fqdn, rr->ttl, &rrsig_rr_rd, rr);
3200 #endif
3201 
3202             ptr_vector_append(add, rr);
3203             rr->state |= ZONE_DIFF_RR_ADDED;
3204 
3205             // proceed with the chain if needed
3206 
3207             changes |= ZONE_DIFF_CHANGES_ADD;
3208             rrset_removed = FALSE;
3209             all_rrset_removed = FALSE;
3210         }
3211         else if((rr->state & (ZONE_DIFF_RR_IN_ZONE|ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_REMOVED)) == (ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_IN_ZONE))
3212         {
3213             // remove
3214 
3215 #if DEBUG
3216             rdata_desc rrsig_rr_rd = {rr->rtype, rr->rdata_size, rr->rdata};
3217             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
3218             log_debug1("update: del %w %{dnsname} %9i %{typerdatadesc} (zone_diff_get_changes_update_rrsig_rr %p)",
3219                     &temp_fw_0, rr->fqdn, rr->ttl, &rrsig_rr_rd, rr);
3220 #endif
3221 
3222             ptr_vector_append(remove, rr);
3223             rr->state |= ZONE_DIFF_RR_REMOVED;
3224 
3225             // proceed with the chain if needed
3226 
3227             changes |= ZONE_DIFF_CHANGES_REMOVE;
3228             all_rrset_added = FALSE;
3229         }
3230         else if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE)) == 0)
3231         {
3232             // stays
3233 
3234 #if DEBUG
3235             rdata_desc rrsig_rr_rd = {rr->rtype, rr->rdata_size, rr->rdata};
3236             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
3237             log_debug1("update: nop %w %{dnsname} %9i %{typerdatadesc} (zone_diff_get_changes_update_rrsig_rr %p)",
3238                     &temp_fw_0, rr->fqdn, rr->ttl, &rrsig_rr_rd, rr);
3239 #endif
3240 
3241             changes |= ZONE_DIFF_CHANGES_KEPT;
3242             rrset_removed = FALSE;
3243             all_rrset_removed = FALSE;
3244             all_rrset_added = FALSE;
3245 
3246             rrset_new = TRUE;
3247         }
3248         else
3249         {
3250 #if DEBUG
3251             rdata_desc rrsig_rr_rd = {rr->rtype, rr->rdata_size, rr->rdata};
3252             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
3253             log_debug1("update: ign %w %{dnsname} %9i %{typerdatadesc} (zone_diff_get_changes_update_rrsig_rr %p)",
3254                     &temp_fw_0, rr->fqdn, rr->ttl, &rrsig_rr_rd, rr);
3255 #endif
3256         }
3257     }
3258 
3259     parm->changes = changes;
3260     parm->rrset_removed = rrset_removed;
3261     parm->rrset_new = rrset_new;
3262     parm->all_rrset_added = all_rrset_added;
3263     parm->all_rrset_removed = all_rrset_removed;
3264 }
3265 
3266 static void
zone_diff_get_changes_update_rr(zone_diff_fqdn_rr_set * rr_set,struct zone_diff_get_changes_update_rr_parm * parm,ptr_vector * remove,ptr_vector * add)3267 zone_diff_get_changes_update_rr(zone_diff_fqdn_rr_set *rr_set, struct zone_diff_get_changes_update_rr_parm *parm, ptr_vector *remove, ptr_vector *add)
3268 {
3269 
3270     u8 changes = parm->changes;
3271     bool rrset_removed = parm->rrset_removed;
3272     bool all_rrset_added = parm->all_rrset_added;
3273     bool all_rrset_removed = parm->all_rrset_removed;
3274     bool non_empty = parm->non_empty;
3275 
3276     ptr_set_iterator rr_iter;
3277 
3278     // for all marked rr
3279 
3280     ptr_set_iterator_init(&rr_set->rr, &rr_iter);
3281     while(ptr_set_iterator_hasnext(&rr_iter))
3282     {
3283         ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
3284         zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
3285 
3286         if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_ADDED)) == ZONE_DIFF_RR_ADD)
3287         {
3288             // add
3289 
3290 #if DEBUG
3291             rdata_desc rrsig_rr_rd = {rr->rtype, rr->rdata_size, rr->rdata};
3292             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
3293             log_debug1("update: add %w %{dnsname} %9i %{typerdatadesc} (zone_diff_get_changes_update_rr %p)",
3294                     &temp_fw_0, rr->fqdn, rr->ttl, &rrsig_rr_rd, rr);
3295 #endif
3296             ptr_vector_append(add, rr);
3297             rr->state |= ZONE_DIFF_RR_ADDED;
3298 
3299             if(rr->rtype == TYPE_SOA)
3300             {
3301                 ptr_vector_end_swap(add, 0);
3302             }
3303 
3304             // proceed with the chain if needed
3305 
3306             changes |= ZONE_DIFF_CHANGES_ADD;
3307             rrset_removed = FALSE;
3308             all_rrset_removed = FALSE;
3309             non_empty = TRUE;
3310         }
3311         else if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_REMOVED)) == ZONE_DIFF_RR_REMOVE)
3312         {
3313             // remove
3314 
3315 #if DEBUG
3316             rdata_desc rrsig_rr_rd = {rr->rtype, rr->rdata_size, rr->rdata};
3317             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
3318             log_debug1("update: del %w %{dnsname} %9i %{typerdatadesc} (zone_diff_get_changes_update_rr %p)",
3319                     &temp_fw_0, rr->fqdn, rr->ttl, &rrsig_rr_rd, rr);
3320 #endif
3321 
3322             ptr_vector_append(remove, rr);
3323             rr->state |= ZONE_DIFF_RR_REMOVED;
3324 
3325             if(rr->rtype == TYPE_SOA)
3326             {
3327                 ptr_vector_end_swap(remove, 0);
3328             }
3329 
3330             // proceed with the chain if needed
3331 
3332             changes |= ZONE_DIFF_CHANGES_REMOVE;
3333             all_rrset_added = FALSE;
3334         }
3335         else if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE)) == 0)
3336         {
3337 
3338 #if DEBUG
3339             rdata_desc rrsig_rr_rd = {rr->rtype, rr->rdata_size, rr->rdata};
3340             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
3341             log_debug1("update: nop %w %{dnsname} %9i %{typerdatadesc} (zone_diff_get_changes_update_rr %p)",
3342                     &temp_fw_0, rr->fqdn, rr->ttl, &rrsig_rr_rd, rr);
3343 #endif
3344             // stays
3345             changes |= ZONE_DIFF_CHANGES_KEPT;
3346             rrset_removed = FALSE;
3347             all_rrset_removed = FALSE;
3348             all_rrset_added = FALSE;
3349             non_empty = TRUE;
3350         }
3351     }
3352 
3353     parm->changes = changes;
3354     parm->rrset_removed = rrset_removed;
3355     parm->all_rrset_added = all_rrset_added;
3356     parm->all_rrset_removed = all_rrset_removed;
3357     parm->non_empty = non_empty;
3358 }
3359 
3360 u64
zone_diff_key_vector_get_mask(ptr_vector * keys,time_t now)3361 zone_diff_key_vector_get_mask(ptr_vector *keys, time_t now)
3362 {
3363     u64 mask = 0;
3364     for(int i = 0; i <= ptr_vector_last_index(keys); ++i)
3365     {
3366         dnssec_key *key = (dnssec_key*)ptr_vector_get(keys, i);
3367 
3368         bool is_private = dnskey_is_private(key);
3369 
3370         if((is_private && dnskey_is_activated(key, now)) || !is_private)
3371         {
3372             mask |= 1ULL << i;
3373         }
3374     }
3375 
3376     return mask;
3377 }
3378 
3379 /**
3380  * Stores changes of a diff into two vectors.
3381  * Optionally keep track of record sets that need to be signed.
3382  * Optionally notify a chain about changes.
3383  *
3384  * @param diff
3385  * @param dc can be NULL
3386  * @param rrset_to_sign_vector can be NULL
3387  * @param remove
3388  * @param add
3389  * @return TRUE iff there is a DNSKEY rrset in the diff
3390  */
3391 
3392 s32
zone_diff_get_changes(zone_diff * diff,ptr_vector * rrset_to_sign_vector,ptr_vector * ksks,ptr_vector * zsks,ptr_vector * remove,ptr_vector * add)3393 zone_diff_get_changes(zone_diff *diff, ptr_vector *rrset_to_sign_vector, ptr_vector *ksks, ptr_vector *zsks, ptr_vector *remove, ptr_vector *add)
3394 {
3395     s32 mandatory_changes = 0;
3396     ya_result err = SUCCESS;
3397 
3398     // first fill the arrays with the relevant keys
3399 
3400     zone_diff_store_diff_dnskey_get_keys(diff, ksks, zsks);
3401 
3402     ptr_set_iterator fqdn_iter;
3403     ptr_set_iterator rr_iter;
3404 
3405     time_t now = time(NULL);
3406 
3407     u64 ksks_mask = zone_diff_key_vector_get_mask(ksks, now);
3408     u64 zsks_mask = zone_diff_key_vector_get_mask(zsks, now);
3409 
3410     //bool may_have_empty_terminals = FALSE;
3411 
3412     // for all fqdn
3413 
3414     ptr_set_iterator_init(&diff->fqdn, &fqdn_iter);
3415     while(ptr_set_iterator_hasnext(&fqdn_iter))
3416     {
3417         ptr_node *diff_fqdn_node = ptr_set_iterator_next_node(&fqdn_iter);
3418 #if DYNUPDATE_DIFF_DETAILED_LOG
3419         const u8 *diff_fqdn_name = (const u8*)diff_fqdn_node->key;
3420 #endif
3421         zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)diff_fqdn_node->value;
3422 
3423         // for all rrset
3424 
3425         bool type_map_changed = FALSE;
3426         bool all_rrset_added = TRUE;
3427         bool all_rrset_removed = TRUE;
3428         bool non_empty = FALSE;
3429 
3430         zone_diff_fqdn_rr_set *rrsig_rr_set = NULL;
3431 
3432         u32_node *rrset_node = u32_set_find(&diff_fqdn->rrset, TYPE_RRSIG);
3433         if(rrset_node != NULL)
3434         {
3435             rrsig_rr_set = (zone_diff_fqdn_rr_set*)rrset_node->value;
3436         }
3437 
3438         type_map_changed = (rrsig_rr_set == NULL);
3439 
3440         // for all records
3441 
3442         if(!u32_set_isempty(&diff_fqdn->rrset))
3443         {
3444             u32_set_iterator rrset_iter;
3445             u32_set_iterator_init(&diff_fqdn->rrset, &rrset_iter);
3446             while(u32_set_iterator_hasnext(&rrset_iter))
3447             {
3448                 u32_node *rrset_node = u32_set_iterator_next_node(&rrset_iter);
3449 
3450                 zone_diff_fqdn_rr_set *rr_set = (zone_diff_fqdn_rr_set*)rrset_node->value;
3451 
3452                 if(rr_set == NULL)
3453                 {
3454                     continue;
3455                 }
3456 
3457 #if DYNUPDATE_DIFF_DETAILED_LOG
3458                 {
3459                     // enumerate records
3460 
3461                     ptr_set_iterator rr_iter;
3462                     ptr_set_iterator_init(&rr_set->rr, &rr_iter);
3463                     rdata_desc rdatadesc = {rr_set->rtype, 0, NULL};
3464                     while(ptr_set_iterator_hasnext(&rr_iter))
3465                     {
3466                         ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
3467                         zone_diff_label_rr *rr = (zone_diff_label_rr *)rr_node->key;
3468                         rdatadesc.len = rr->rdata_size;
3469                         rdatadesc.rdata = rr->rdata;
3470                         log_debug("update: %02x [%llx] %{dnsname} %i %{typerdatadesc}", rr->state, rr_set->key_mask, rr->fqdn, rr->ttl, &rdatadesc);
3471                     }
3472                 }
3473 #endif
3474                 if(rr_set->rtype == TYPE_RRSIG)
3475                 {
3476                     // if allowed ...
3477 
3478                     if(diff->rrsig_update_allowed)
3479                     {
3480                         ptr_set_iterator_init(&rr_set->rr, &rr_iter);
3481                         bool rrsig_added = FALSE;
3482                         bool rrsig_kept = FALSE;
3483                         bool rrsig_removed = FALSE;
3484                         bool key_will_be_present = FALSE;
3485                         bool key_will_be_present_DNSKEY = FALSE;
3486                         bool key_will_be_present_not_DNSKEY = FALSE;
3487 
3488                         while(ptr_set_iterator_hasnext(&rr_iter))
3489                         {
3490                             ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
3491                             zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
3492                             if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE)) == ZONE_DIFF_RR_ADD)
3493                             {
3494                                 rdata_desc rdt = {rr->rtype, rr->rdata_size, rr->rdata};
3495 
3496                                 log_debug("update: %{dnsname}: checking for signing key of RRSIG record %{dnsname} %i %{dnsclass} %{typerdatadesc}",
3497                                           diff->origin, rr->fqdn, rr->ttl, &rr->rclass, &rdt);
3498 
3499                                 u8 algorithm = rrsig_get_algorithm_from_rdata(rr->rdata, rr->rdata_size);
3500                                 u16 tag = rrsig_get_key_tag_from_rdata(rr->rdata, rr->rdata_size);
3501 
3502                                 rrsig_added = TRUE;
3503 
3504                                 if(zone_diff_will_have_dnskey_with_algorithm_tag(diff_fqdn, algorithm, tag))
3505                                 {
3506                                     key_will_be_present = TRUE;
3507                                     if(rrsig_get_type_covered_from_rdata(rr->rdata, rr->rdata_size) == TYPE_DNSKEY)
3508                                     {
3509                                         key_will_be_present_DNSKEY = TRUE;
3510                                     }
3511                                     else
3512                                     {
3513                                         key_will_be_present_not_DNSKEY = TRUE;
3514                                     }
3515                                     break;
3516                                 }
3517                             }
3518                             else if((rr->state & (ZONE_DIFF_RR_IN_ZONE|ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE)) == ZONE_DIFF_RR_REMOVE)
3519                             {
3520                                 rrsig_removed = TRUE;
3521                             }
3522                             else if((rr->state & (ZONE_DIFF_RR_IN_ZONE/*|ZONE_DIFF_RR_ADD*/|ZONE_DIFF_RR_REMOVE)) == ZONE_DIFF_RR_IN_ZONE)
3523                             {
3524                                 rrsig_kept = TRUE; // if it's added but already in zone, it does not count does it ...
3525                             }
3526                         }
3527 
3528                         diff_fqdn->rrsig_added = rrsig_added;
3529                         diff_fqdn->rrsig_kept = rrsig_kept;
3530                         diff_fqdn->rrsig_removed = rrsig_removed;
3531 
3532                         if(!rrsig_added || (rrsig_added && key_will_be_present))
3533                         {
3534                             u8 changes = ZONE_DIFF_CHANGES_NONE;
3535                             bool rrset_removed = TRUE;
3536 
3537                             struct zone_diff_get_changes_update_rr_parm parms = {changes, rrset_removed, FALSE, all_rrset_added, all_rrset_removed, non_empty};
3538                             zone_diff_get_changes_update_rrsig_rr(rr_set, &parms, remove, add);
3539 
3540                             diff_fqdn->rrsig_kept = !parms.rrset_new;
3541                         }
3542                         else
3543                         {
3544                             if(!key_will_be_present_DNSKEY)
3545                             {
3546                                 log_info("update: %{dnsname}: DNSKEY RRSIG without signing DNSKEY present (probably on purpose)", diff_fqdn->fqdn);
3547                             }
3548                             if(!key_will_be_present_not_DNSKEY)
3549                             {
3550                                 log_err("update: %{dnsname}: RRSIG without signing DNSKEY present (probably bad)", diff_fqdn->fqdn);
3551                             }
3552 
3553                             err = INVALID_STATE_ERROR;
3554                         }
3555                     }
3556 #if DEBUG
3557                     else
3558                     {
3559                         log_debug1("update: %{dnsname}: not updating RRSIG rr_set at this point (rrsig_update_allowed is false)", diff_fqdn->fqdn);
3560                         ptr_set_iterator_init(&rr_set->rr, &rr_iter);
3561                         while(ptr_set_iterator_hasnext(&rr_iter))
3562                         {
3563                             ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
3564                             zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
3565                             if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE)) == ZONE_DIFF_RR_ADD)
3566                             {
3567                                 rdata_desc rdt = {rr->rtype, rr->rdata_size, rr->rdata};
3568 
3569                                 log_debug("update: %{dnsname}: (ignoring) [%02x] %{dnsname} %i %{dnsclass} %{typerdatadesc}",
3570                                           diff->origin, rr->state, rr->fqdn, rr->ttl, &rr->rclass, &rdt);
3571                             }
3572                         }
3573                     }
3574 #endif
3575 
3576                     continue;
3577                 }
3578                 u8 changes = ZONE_DIFF_CHANGES_NONE;
3579                 bool rrset_removed = TRUE;
3580 
3581                 struct zone_diff_get_changes_update_rr_parm parms = {changes, FALSE, rrset_removed, all_rrset_added, all_rrset_removed, non_empty};
3582                 zone_diff_get_changes_update_rr(rr_set, &parms, remove, add);
3583 
3584                 changes = parms.changes;
3585                 rrset_removed = parms.rrset_removed;
3586                 if(rr_set->rtype != TYPE_NSEC)
3587                 {
3588                     all_rrset_added = parms.all_rrset_added;
3589                     all_rrset_removed = parms.all_rrset_removed;
3590                     non_empty = parms.non_empty;
3591                 }
3592 
3593                 /*
3594                  * If the status is 0, then all the added records that have been added have also been removed => no map change, and no signature change
3595                  * If the status is 1, then the rrset has completely been removed => map change and remove all signatures
3596                  * If the status is 2, then the rrset has completely been added => map change, and add (new) signatures
3597                  * If the status is 4, then the rrset existed and still exists => no map change, and no signature change
3598                  *
3599                  * Any other combination having 1 or 2 on will make no map change but update the signature
3600                  *
3601                  */
3602 
3603                 if((changes == ZONE_DIFF_CHANGES_ADD) || (changes == ZONE_DIFF_CHANGES_REMOVE))
3604                 {
3605                     type_map_changed = TRUE;
3606                 }
3607 
3608                 ptr_vector *keys = zsks;
3609                 u64 keys_mask = zsks_mask;
3610 
3611                 if(rr_set->rtype == TYPE_DNSKEY)
3612                 {
3613                     keys = ksks;
3614                     keys_mask = ksks_mask;
3615                 }
3616 
3617                 if(rrset_node->key == TYPE_RRSIG)
3618                 {
3619                     continue;
3620                 }
3621 
3622                 bool rrset_updated = (changes & (ZONE_DIFF_CHANGES_ADD|ZONE_DIFF_CHANGES_REMOVE)); // || type_map_changed ?
3623 
3624                 if((rr_set->rtype != TYPE_SOA) && rrset_updated)
3625                 {
3626                     ++mandatory_changes;
3627                 }
3628 
3629     #if 0 /* fix */
3630 #else
3631                 bool rrset_expected_to_be_covered =
3632                         !(diff_fqdn->at_delegation || diff_fqdn->under_delegation) ||
3633                         (!diff_fqdn->under_delegation &&
3634                             (diff_fqdn->at_delegation && ((rr_set->rtype == TYPE_DS) || (rr_set->rtype == TYPE_NSEC)))
3635                         );
3636 
3637                 bool rrset_rrsig_covered_with_chain_rules = (!rrset_removed && rrset_expected_to_be_covered);
3638     #endif
3639                 bool came_under_delegation = (!diff_fqdn->was_under_delegation && diff_fqdn->under_delegation);
3640                 //bool came_out_of_delegation = (diff_fqdn->was_under_delegation && !diff_fqdn->under_delegation);
3641 
3642                 // blanket bombing
3643 
3644                 if((rrsig_rr_set != NULL) &&
3645                    (rrset_updated ||
3646                    all_rrset_removed ||
3647                    came_under_delegation ||
3648     #if 0 /* fix */
3649 #else
3650                     !rrset_rrsig_covered_with_chain_rules
3651     #endif
3652                     )
3653                    )
3654                 {
3655                     ptr_set_iterator_init(&rrsig_rr_set->rr, &rr_iter);
3656                     while(ptr_set_iterator_hasnext(&rr_iter))
3657                     {
3658                         ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
3659                         zone_diff_label_rr *rrsig_rr = (zone_diff_label_rr *)rr_node->key;
3660 
3661                         if(rrsig_get_type_covered_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size) != rr_set->rtype)
3662                         {
3663                             continue;
3664                         }
3665 
3666                         if(rrsig_rr->state & ZONE_DIFF_RR_ADD)
3667                         {
3668                             // manually added
3669                             continue;
3670                         }
3671 
3672                         if((rrsig_rr->state & ZONE_DIFF_RR_REMOVED) == 0) // the signature is not marked for removal (e.g.: expired)
3673                         {
3674                             rrsig_rr->state |= ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_AUTOMATED;
3675     #if DEBUG
3676                             {
3677                                 rdata_desc rrsig_rr_rd = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
3678                                 format_writer temp_fw_0 = {zone_diff_record_state_format, &rrsig_rr->state};
3679                                 log_debug1("update: del %w %{dnsname} %9i %{typerdatadesc} (rrsig A zone_diff_get_changes %p)",
3680                                            &temp_fw_0, rrsig_rr->fqdn, rrsig_rr->ttl, &rrsig_rr_rd, rrsig_rr);
3681                             }
3682     #endif
3683                             ptr_vector_append(remove, rrsig_rr);
3684                             rrsig_rr->state |= ZONE_DIFF_RR_REMOVED;
3685                         }
3686                     }
3687                 }
3688 
3689                 // for all rrsig, enumerate properly covered types
3690 
3691                 //bool rrset_already_covered = FALSE;
3692 
3693                 if(!all_rrset_removed &&
3694     #if 0 /* fix */
3695 #else
3696                     rrset_rrsig_covered_with_chain_rules
3697     #endif
3698                     ) // else this would be pointless
3699                 {
3700                     if(rrsig_rr_set != NULL)
3701                     {
3702                         u64 coverage = 0;
3703 
3704                         ptr_set_iterator_init(&rrsig_rr_set->rr, &rr_iter);
3705                         while(ptr_set_iterator_hasnext(&rr_iter))
3706                         {
3707                             ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
3708                             zone_diff_label_rr *rrsig_rr = (zone_diff_label_rr *)rr_node->key;
3709 
3710                             if(rrsig_get_type_covered_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size) != rr_set->rtype)
3711                             {
3712                                 continue;
3713                             }
3714 
3715                             if((rrsig_rr->state & (ZONE_DIFF_RR_ADDED|ZONE_DIFF_RR_RDATA_OWNED)) == (ZONE_DIFF_RR_ADDED|ZONE_DIFF_RR_RDATA_OWNED))
3716                             {
3717                                 continue;
3718                             }
3719 
3720                             if((rrsig_rr->state & ZONE_DIFF_RR_REMOVED) != 0) // the signature is not marked for removal (e.g.: expired)
3721                             {
3722                                 continue;
3723                             }
3724 
3725                             // check if the signature is with a valid key and is in its validity period
3726                             // if it's not valid yet, keep it
3727                             // if its expired, remove it
3728                             // if no valid signatures are available, may mark the record for signing
3729 
3730                             s32 key_index = -2;
3731 
3732                             if(rrsig_should_remove_signature_from_rdata(
3733                                 rrsig_rr->rdata, rrsig_rr->rdata_size,
3734                                 keys, now, diff->rrsig_validity_regeneration, &key_index) || (key_index == -1))
3735                             {
3736                                 rrsig_rr->state |= ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_AUTOMATED;
3737 
3738 #if DEBUG
3739                                 {
3740                                     rdata_desc rrsig_rr_rd = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
3741                                     format_writer temp_fw_0 = {zone_diff_record_state_format, &rrsig_rr->state};
3742                                     log_debug1("update: del %w %{dnsname} %9i %{typerdatadesc} (rrsig B zone_diff_get_changes %p)",
3743                                                &temp_fw_0, rrsig_rr->fqdn, rrsig_rr->ttl, &rrsig_rr_rd, rrsig_rr);
3744                                 }
3745 #endif
3746 
3747                                 ptr_vector_append(remove, rrsig_rr);
3748                                 rrsig_rr->state |= ZONE_DIFF_RR_REMOVED;
3749                                 ++mandatory_changes;
3750                                 continue;
3751                             }
3752 
3753                             // the signature will be kept
3754 
3755                             coverage |= (1ULL << key_index);
3756                         }
3757 
3758                         rr_set->key_mask = keys_mask ^ coverage;
3759                     }
3760                     else
3761                     {
3762                         rr_set->key_mask = keys_mask;
3763                     }
3764                 }
3765 
3766                 // If the chain believes it has to handle the fqdn, add the rrset to the "to sign"
3767                 // This does not work with mixed chains (NSEC & NSEC3)
3768 
3769                 if((rr_set->key_mask != 0) && (rrset_to_sign_vector != NULL))
3770                 {
3771                     if(rr_set->rtype != TYPE_SOA)
3772                     {
3773                         ++mandatory_changes;
3774                     }
3775 
3776                     // will generate new signatures for the rrset (postponed)
3777 
3778                     // verify that signatures are not already present
3779 
3780 
3781 #if DYNUPDATE_DIFF_DETAILED_LOG
3782                         log_debug("update: %{dnsname}: dnssec: %{dnsname} %{dnstype} rrset @%p should be signed (%08llx/%08llx)", diff->origin,
3783                                 diff_fqdn_name, &rr_set->rtype, rr_set, rr_set->key_mask, keys_mask);
3784 #endif
3785                         ptr_vector_append(rrset_to_sign_vector, rr_set);
3786 
3787                 }
3788             }
3789         }
3790         else
3791         {
3792 #if DYNUPDATE_DIFF_DETAILED_LOG
3793             {
3794                 // empty
3795                 log_debug("update: ?? [?] %{dnsname} has no records", diff_fqdn->fqdn);
3796             }
3797 #endif
3798             type_map_changed = FALSE;
3799             all_rrset_added = FALSE;
3800             all_rrset_removed = FALSE;
3801             non_empty = FALSE;
3802 
3803             //may_have_empty_terminals = TRUE;
3804         }
3805 
3806         // if type_map_changes, the type map has to be updated and the signature too, obviously
3807 
3808         diff_fqdn->type_map_changed = type_map_changed || (!diff_fqdn->rrsig_kept && (diff_fqdn->rrsig_added != diff_fqdn->rrsig_removed));
3809         diff_fqdn->all_rrset_added = all_rrset_added;
3810         diff_fqdn->all_rrset_removed = all_rrset_removed;
3811         diff_fqdn->will_be_non_empty = non_empty;
3812 
3813         /**/
3814         diff_fqdn->type_map_changed &= non_empty;
3815         diff_fqdn->all_rrset_added &= non_empty;
3816 
3817         /**/
3818 
3819         diff_fqdn->records_flags_set = 1;
3820 
3821 #if DYNUPDATE_DIFF_DETAILED_LOG
3822         {
3823             // empty
3824             log_debug("update: -- --- %{dnsname} remap=%i +all=%i -all=%i !empty=%i", diff_fqdn->fqdn, type_map_changed, all_rrset_added, all_rrset_removed, non_empty);
3825         }
3826 #endif
3827     }
3828 
3829     if(ISOK(err))
3830     {
3831         return mandatory_changes;
3832     }
3833     else
3834     {
3835         return err;
3836     }
3837 }
3838 
3839 #if 0 && DYNUPDATE_DIFF_DETAILED_LOG
3840     if(may_have_empty_terminals)
3841     {
3842         ptr_set_iterator_init(&diff->fqdn, &fqdn_iter);
3843         while(ptr_set_iterator_hasnext(&fqdn_iter))
3844         {
3845             ptr_node *diff_fqdn_node = ptr_set_iterator_next_node(&fqdn_iter);
3846             const u8 *diff_fqdn_name = (const u8*)diff_fqdn_node->key;
3847 
3848             zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)diff_fqdn_node->value;
3849         }
3850     }
3851 #endif
3852 
3853 void
zone_diff_get_chain_changes(zone_diff * diff,dnssec_chain * dc)3854 zone_diff_get_chain_changes(zone_diff *diff, dnssec_chain* dc/*, ptr_vector *rrset_to_sign_vector, ptr_vector *ksks, ptr_vector *zsks, ptr_vector *remove, ptr_vector *add*/)
3855 {
3856     ptr_set_iterator fqdn_iter;
3857 
3858     if(dc != NULL)
3859     {
3860         ptr_set_iterator_init(&diff->fqdn, &fqdn_iter);
3861         while(ptr_set_iterator_hasnext(&fqdn_iter))
3862         {
3863             ptr_node *diff_fqdn_node = ptr_set_iterator_next_node(&fqdn_iter);
3864             const u8 *diff_fqdn_name = (const u8*)diff_fqdn_node->key;
3865 
3866             zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)diff_fqdn_node->value;
3867 
3868             zone_diff_fqdn_children_state(diff, diff_fqdn->fqdn);
3869 
3870             // calling dnssec_chain_del_from_diff_fqdn and dnssec_chain_add_from_diff_fqdn respectively
3871             // tell to remove or to add a chain node (NSEC/NSEC3) for the given fqdn in the zone.
3872 
3873             // Note the "was" or "is" covered means "IF the fqdn existed, was the past state covering it, is the new state covering it."
3874 
3875             // This table gives the del/add for a node given the various states
3876             //                          (+-R => sumrises to "has changed")
3877             // Was covered | Is covered | +ALL | -ALL | REMAP | NODE
3878             // -----------------------------------------------+------
3879             //      0            0          1      0      ?   |
3880             //      0            0          0      1      ?   |
3881             //      0            0          0      0      0   |
3882             //      0            0          0      0      1   |
3883             // -----------------------------------------------+------
3884             //      0            1          1      0      ?   |  +
3885             //      0            1          0      1      ?   |        There is nothing anymore (empty non-terminal ? => +)
3886             //      0            1          0      0      0   |  +
3887             //      0            1          0      0      1   |  +
3888             // -----------------------------------------------+------
3889             //      1            0          1      0      ?   |        There was nothing before
3890             //      1            0          0      1      ?   |  -
3891             //      1            0          0      0      0   |  -
3892             //      1            0          0      0      1   |  -
3893             // -----------------------------------------------+------
3894             //      1            1          1      0      ?   |  +     There was nothing before
3895             //      1            1          0      1      ?   |  -                              (empty non-terminal ? => -+)
3896             //      1            1          0      0      0   |        There is no changed of state on this regard
3897             //      1            1          0      0      1   | -+
3898             // -----------------------------------------------+------
3899 
3900 #define CHAIN_NODE_NOP 0
3901 #define CHAIN_NODE_DEL 1
3902 #define CHAIN_NODE_ADD 2
3903 
3904             bool is_covered = dc->chain->fqdn_is_covered(diff_fqdn);
3905             bool was_covered = dc->chain->fqdn_was_covered(diff_fqdn);
3906 
3907 #if DYNUPDATE_DIFF_DETAILED_LOG
3908             log_debug("update: %{dnsname}: dnssec: %{dnsname}: +ALL(%i) -ALL(%i) RECORDS(%i->%i) COVERED(%i->%i) CHILDREN(%i->%i) AT(%i->%i) UNDER(%i->%i) MAP(%i)",
3909                     diff->origin,
3910                     diff_fqdn_name,
3911                     diff_fqdn->all_rrset_added,
3912                     diff_fqdn->all_rrset_removed,
3913                     diff_fqdn->was_non_empty, diff_fqdn->will_be_non_empty,
3914                     was_covered, is_covered,
3915                     diff_fqdn->had_children, diff_fqdn->will_have_children,
3916                     diff_fqdn->was_at_delegation, diff_fqdn->at_delegation,
3917                     diff_fqdn->was_under_delegation, diff_fqdn->under_delegation,
3918                     diff_fqdn->type_map_changed);
3919 #endif
3920             if(was_covered || is_covered) // quickly cull the first 4 states of the table
3921             {
3922                 bool did_exist = diff_fqdn->had_children || diff_fqdn->was_non_empty;
3923                 bool will_exist = diff_fqdn->will_have_children || diff_fqdn->will_be_non_empty;
3924 
3925                 u8 ops = 0;
3926 
3927                 if( (diff_fqdn->had_children != diff_fqdn->will_have_children) ||
3928                     (diff_fqdn->all_rrset_added) ||
3929                     (diff_fqdn->all_rrset_removed) ||
3930                     (diff_fqdn->type_map_changed) ||
3931                     (is_covered != was_covered)
3932                     )
3933                 {
3934                     //ops_index = 3;  // means change
3935 
3936                     if(was_covered && did_exist)
3937                     {
3938                         //ops_index |= 8;
3939                         ops |= CHAIN_NODE_DEL;
3940                     }
3941 
3942                     if(is_covered && will_exist)
3943                     {
3944                         //ops_index |= 4;
3945                         ops |= CHAIN_NODE_ADD;
3946                     }
3947                 }
3948 
3949 #if DEBUG
3950                 log_debug2("update: %{dnsname}: dnssec: %{dnsname}: operation %x", diff->origin, diff_fqdn_name, ops);
3951 #endif
3952                 if(ops & CHAIN_NODE_DEL)
3953                 {
3954                     log_debug2("update: %{dnsname}: dnssec: %{dnsname}: removing chain node", diff->origin, diff_fqdn_name);
3955                     dnssec_chain_del_from_diff_fqdn(dc, diff_fqdn, 0);
3956                 }
3957 
3958                 if(ops & CHAIN_NODE_ADD)
3959                 {
3960                     log_debug2("update: %{dnsname}: dnssec: %{dnsname}: adding chain node", diff->origin, diff_fqdn_name);
3961                     dnssec_chain_add_from_diff_fqdn(dc, diff_fqdn, 0);
3962                 }
3963             }
3964         } // while fqdn names
3965     }
3966 }
3967 
3968 /**
3969  * Returns TRUE iff there are changes in the diff
3970  *
3971  * @param diff
3972  * @param dc can be NULL
3973  * @param rrset_to_sign_vector can be NULL
3974  *
3975  * @return TRUE iff there are changes in the diff
3976  */
3977 
3978 bool
zone_diff_has_changes(zone_diff * diff,ptr_vector * rrset_to_sign_vector)3979 zone_diff_has_changes(zone_diff *diff, ptr_vector *rrset_to_sign_vector)
3980 {
3981     if(ptr_vector_last_index(rrset_to_sign_vector) >= 0)
3982     {
3983 #if DEBUG
3984         for(s32 i = 0; i <= ptr_vector_last_index(rrset_to_sign_vector); ++i)
3985         {
3986             zone_diff_fqdn_rr_set *rr_set = (zone_diff_fqdn_rr_set*)ptr_vector_get(rrset_to_sign_vector, i);
3987 
3988             ptr_set_iterator rr_iter;
3989 
3990             // for all marked rr
3991 
3992             ptr_set_iterator_init(&rr_set->rr, &rr_iter);
3993             while(ptr_set_iterator_hasnext(&rr_iter))
3994             {
3995                 ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
3996                 zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
3997 
3998                 rdata_desc rdatadesc = {rr->rtype, rr->rdata_size, rr->rdata};
3999 
4000 
4001                 format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
4002 
4003                 log_debug1("zone-diff: changes: %{dnsname}: %02x: %w: %{dnsname} %i %{typerdatadesc}", diff->origin, rr->state, &temp_fw_0, rr->fqdn, rr->ttl, &rdatadesc);
4004             }
4005         }
4006 #endif
4007 
4008         return TRUE;
4009     }
4010 
4011     ptr_set_iterator fqdn_iter;
4012     ptr_set_iterator rr_iter;
4013 
4014     // for all fqdn
4015 
4016     ptr_set_iterator_init(&diff->fqdn, &fqdn_iter);
4017     while(ptr_set_iterator_hasnext(&fqdn_iter))
4018     {
4019         ptr_node *diff_fqdn_node = ptr_set_iterator_next_node(&fqdn_iter);
4020         zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)diff_fqdn_node->value;
4021 
4022         // for all records
4023 
4024         u32_set_iterator rrset_iter;
4025         u32_set_iterator_init(&diff_fqdn->rrset, &rrset_iter);
4026         while(u32_set_iterator_hasnext(&rrset_iter))
4027         {
4028             u32_node *rrset_node = u32_set_iterator_next_node(&rrset_iter);
4029 
4030             zone_diff_fqdn_rr_set *rr_set = (zone_diff_fqdn_rr_set*)rrset_node->value;
4031 
4032             // for all marked rr
4033 
4034             ptr_set_iterator_init(&rr_set->rr, &rr_iter);
4035             while(ptr_set_iterator_hasnext(&rr_iter))
4036             {
4037                 ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
4038                 zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
4039 #if DEBUG
4040                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
4041                 log_debug1("update: %{dnsname}: has-changes: state %02x: %{dnsname} %9i %{typerdatadesc}", diff->origin, rr->state, rr->fqdn, rr->ttl, &rd);
4042 #endif
4043                 if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE)) == ZONE_DIFF_RR_ADD)
4044                 {
4045                     // add
4046                     return TRUE;
4047                 }
4048                 else if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE)) == ZONE_DIFF_RR_REMOVE)
4049                 {
4050                     // remove
4051                     return TRUE;
4052                 }
4053             }
4054         }
4055     }
4056 
4057     return FALSE;
4058 }
4059 
4060 void
zone_diff_fqdn_rr_set_log(const zone_diff_fqdn_rr_set * rr_set,const u8 * origin,logger_handle * handle,int level)4061 zone_diff_fqdn_rr_set_log(const zone_diff_fqdn_rr_set *rr_set, const u8* origin, logger_handle *handle, int level)
4062 {
4063     ptr_set_iterator rr_iter;
4064 
4065     // for all marked rr
4066 
4067     ptr_set_iterator_init(&rr_set->rr, &rr_iter);
4068     while(ptr_set_iterator_hasnext(&rr_iter))
4069     {
4070         ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
4071         zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
4072 
4073         rdata_desc rdatadesc = {rr->rtype, rr->rdata_size, rr->rdata};
4074 
4075         format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
4076 
4077         logger_handle_msg_nocull(handle, level, LOG_TEXT_PREFIX "zone-diff: %{dnsname}: %{dnsname}: %02x: %w: %{dnsname} %i %{typerdatadesc}",
4078                 origin, rr->fqdn,
4079                 rr->state, &temp_fw_0, rr->fqdn, rr->ttl, &rdatadesc);
4080     }
4081 }
4082 
4083 void
zone_diff_fqdn_log(const zone_diff_fqdn * diff_fqdn,const u8 * origin,logger_handle * handle,int level)4084 zone_diff_fqdn_log(const zone_diff_fqdn* diff_fqdn, const u8 *origin, logger_handle *handle, int level)
4085 {
4086     if(!log_is_set(handle, level))
4087     {
4088         return;
4089     }
4090 
4091     // for all rrset
4092 
4093     const u8 *diff_fqdn_name = diff_fqdn->fqdn;
4094 
4095     if(origin == NULL)
4096     {
4097         origin = (const u8 *)"\004NULL";
4098     }
4099 
4100     format_writer temp_fw_1 = {zone_diff_fqdn_changes_format, diff_fqdn};
4101 
4102     logger_handle_msg_nocull(handle, level, LOG_TEXT_PREFIX "zone-diff: %{dnsname}: %{dnsname}: %w", origin, diff_fqdn_name, &temp_fw_1);
4103 
4104     // for all records
4105 
4106     u32_set_iterator rrset_iter;
4107     u32_set_iterator_init(&diff_fqdn->rrset, &rrset_iter);
4108     while(u32_set_iterator_hasnext(&rrset_iter))
4109     {
4110         u32_node *rrset_node = u32_set_iterator_next_node(&rrset_iter);
4111 
4112         zone_diff_fqdn_rr_set *rr_set = (zone_diff_fqdn_rr_set*)rrset_node->value;
4113 
4114         if(rr_set == NULL)
4115         {
4116             log_debug("zone-diff: %{dnsname}: %{dnsname} has no record set", origin, diff_fqdn_name);
4117             continue;
4118         }
4119 
4120         format_writer temp_fw_1 = {zone_diff_fqdn_changes_format, diff_fqdn};
4121         logger_handle_msg_nocull(handle, level, LOG_TEXT_PREFIX "zone-diff: %{dnsname}: %{dnsname}: %w", origin, diff_fqdn_name, &temp_fw_1);
4122 
4123         zone_diff_fqdn_rr_set_log(rr_set, origin, handle, level);
4124     }
4125 }
4126 
4127 void
zone_diff_log(const zone_diff * diff,logger_handle * handle,int level)4128 zone_diff_log(const zone_diff *diff, logger_handle *handle, int level)
4129 {
4130     if(!log_is_set(handle, level))
4131     {
4132         return;
4133     }
4134 
4135     ptr_set_iterator fqdn_iter;
4136 
4137     // for all fqdn
4138 
4139     ptr_set_iterator_init(&diff->fqdn, &fqdn_iter);
4140     while(ptr_set_iterator_hasnext(&fqdn_iter))
4141     {
4142         ptr_node *diff_fqdn_node = ptr_set_iterator_next_node(&fqdn_iter);
4143         zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)diff_fqdn_node->value;
4144         zone_diff_fqdn_log(diff_fqdn, diff->origin, handle, level);
4145     }
4146 }
4147 
4148 int
zone_diff_check_changes(const zone_diff * diff,logger_handle * handle,int level)4149 zone_diff_check_changes(const zone_diff *diff, logger_handle *handle, int level)
4150 {
4151     ptr_set_iterator fqdn_iter;
4152 
4153     int changes = 0;
4154 
4155     ptr_set_iterator_init(&diff->fqdn, &fqdn_iter);
4156     while(ptr_set_iterator_hasnext(&fqdn_iter))
4157     {
4158         ptr_node *diff_fqdn_node = ptr_set_iterator_next_node(&fqdn_iter);
4159         zone_diff_fqdn *diff_fqdn = (zone_diff_fqdn*)diff_fqdn_node->value;
4160 
4161         u32_set_iterator rrset_iter;
4162         u32_set_iterator_init(&diff_fqdn->rrset, &rrset_iter);
4163         while(u32_set_iterator_hasnext(&rrset_iter))
4164         {
4165             u32_node *rrset_node = u32_set_iterator_next_node(&rrset_iter);
4166 
4167             zone_diff_fqdn_rr_set *rr_set = (zone_diff_fqdn_rr_set*)rrset_node->value;
4168 
4169             ptr_set_iterator rr_iter;
4170 
4171             ptr_set_iterator_init(&rr_set->rr, &rr_iter);
4172             while(ptr_set_iterator_hasnext(&rr_iter))
4173             {
4174                 ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
4175                 zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
4176 
4177                 if(!(rr->state & ZONE_DIFF_RR_AUTOMATED))
4178                 {
4179                     if(rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE))
4180                     {
4181                         ++changes;
4182                     }
4183                 }
4184             }
4185         }
4186     }
4187 
4188 
4189     if(changes == 0)
4190     {
4191         zone_diff_log(diff, handle, level);
4192     }
4193 
4194     return changes;
4195 }
4196 
4197 /**
4198  * Signs RRSET with all active keys found in keys.
4199  * Doesn't do any pertinence tests.
4200  * It's only use now is to add RRSIG records to NSEC3 rrsets that have no valid signatures
4201  *
4202  */
4203 
4204 void
zone_diff_sign_rrset(zone_diff * diff,zdb_zone * zone,ptr_vector * keys,ptr_vector * add,zone_diff_fqdn_rr_set * rr_set,zone_diff_fqdn_rr_set * rrsig_rr_set)4205 zone_diff_sign_rrset(zone_diff *diff, zdb_zone *zone, ptr_vector *keys, ptr_vector *add, zone_diff_fqdn_rr_set *rr_set, zone_diff_fqdn_rr_set *rrsig_rr_set)
4206 {
4207     ptr_vector rrset = PTR_VECTOR_EMPTY;
4208     dnskey_signature ds;
4209     dnskey_signature_init(&ds);
4210 
4211     // setup the view for the RRSET (RRSET abstraction for the part that generates signatures)
4212 
4213     struct resource_record_view rrv = {NULL, &zone_diff_label_rr_rrv_vtbl};
4214     rrv.data = rr_set;
4215 
4216     ptr_vector_clear(&rrset);
4217 
4218     //const u8* rr_fqdn = NULL;
4219 
4220     u8 rrsig_state_mask = ZONE_DIFF_RR_AUTOMATED;
4221 
4222     // accumulate records
4223 
4224     FOREACH_PTR_SET(void*,value, &rr_set->rr)
4225     {
4226         zone_diff_label_rr* rr = (zone_diff_label_rr*)value;
4227         //rr_fqdn = rr->fqdn;
4228 
4229         // if the RR will exist in the zone (A.K.A: not removed), add it to the collection to sign
4230         if((rr->state & ZONE_DIFF_RR_REMOVE) == 0)
4231         {
4232 #if DEBUG
4233             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
4234             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
4235             log_debug2("update: %{dnsname}: covers %w %{dnsname} %9i %{typerdatadesc}%s", diff->origin, &temp_fw_0, rr->fqdn, rr->ttl, &rd,
4236                        ((rr->state & ZONE_DIFF_RR_AUTOMATED)!=0)?"<AUTOMATED>":"");
4237 #endif
4238             rrsig_state_mask &= rr->state;
4239 
4240             ptr_vector_append(&rrset, value);
4241         }
4242         else
4243         {
4244 #if DEBUG
4245             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
4246             format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
4247             log_debug2("update: %{dnsname}: ignore %w %{dnsname} %9i %{typerdatadesc}", diff->origin, &temp_fw_0, rr->fqdn, rr->ttl, &rd);
4248 #endif
4249         }
4250     }
4251 
4252     for(int j = 0; j <= ptr_vector_last_index(keys); ++j)
4253     {
4254         const dnssec_key *key = (dnssec_key*)ptr_vector_get(keys, j);
4255 
4256         // check if the key has private components
4257 
4258         if(!dnskey_is_private(key))
4259         {
4260             log_debug("update: %{dnsname}: key K%{dnsname}+%03d+%05d is not private", diff->origin,
4261                       dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4262             continue;
4263         }
4264 
4265         zone_diff_label_rr *rrsig_rr = NULL;
4266 
4267         ya_result ret;
4268 
4269         s32 maxinterval = diff_generate_signature_interval(diff);
4270 
4271         // rrset_to_sign;
4272         if(ISOK(ret = dnskey_sign_rrset_with_maxinterval(key, &rrset, TRUE, &rrv, maxinterval, (void **) &rrsig_rr)))
4273         {
4274             // add the key to the add set
4275 
4276             log_debug2("update: %{dnsname}: signed %{dnsname} %{dnstype} rrset with key %03d %05d",diff->origin,
4277                        rrsig_rr->fqdn, &rr_set->rtype,
4278                        dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4279 
4280             s32 signature_valid_until = rrsig_get_valid_until_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size);
4281 
4282             // if the signature expires in this time
4283 
4284             if(signature_valid_until > 0)
4285             {
4286                 if(signature_valid_until < dnskey_get_inactive_epoch(key))
4287                 {
4288                     s32 signature_regeneration_time = signature_valid_until - diff->rrsig_validity_regeneration;
4289 
4290                     if(zone->progressive_signature_update.earliest_signature_expiration > signature_regeneration_time)
4291                     {
4292                         zone->progressive_signature_update.earliest_signature_expiration = signature_regeneration_time;
4293                     }
4294                 }
4295                 else
4296                 {
4297                     if(zone->progressive_signature_update.earliest_signature_expiration > signature_valid_until)
4298                     {
4299                         zone->progressive_signature_update.earliest_signature_expiration = signature_valid_until;
4300                     }
4301                 }
4302             }
4303 
4304             rrsig_rr->state |= rrsig_state_mask;
4305 #if 0 /* fix */
4306 #else
4307             zone_diff_label_rr *final_rrsig_rr = zone_diff_fqdn_rr_set_rr_add_get(rrsig_rr_set, rrsig_rr);
4308             if((final_rrsig_rr->state & ZONE_DIFF_RR_IN_ZONE) == 0)
4309             {
4310                 ptr_vector_append(add, final_rrsig_rr);
4311             }
4312 #endif
4313         }
4314         else
4315         {
4316             log_warn("update: %{dnsname}: failed to sign with key %03d %05d: %r",
4317                      diff->origin,
4318                      dnskey_get_algorithm(key), dnskey_get_tag_const(key), ret);
4319             // ...
4320         }
4321     } // for each key
4322 }
4323 
4324 /**
4325  * Appends RRSIGs to remove/add vector, following the the need-to-be-signed RR set, using keys from KSK and ZSK vectors.
4326  *
4327  * @param diff
4328  * @param rrset_to_sign_vector
4329  * @param ksks
4330  * @param zsks
4331  * @param remove
4332  * @param add
4333  */
4334 
4335 ya_result
zone_diff_sign(zone_diff * diff,zdb_zone * zone,ptr_vector * rrset_to_sign_vector,ptr_vector * ksks,ptr_vector * zsks,ptr_vector * remove,ptr_vector * add)4336 zone_diff_sign(zone_diff *diff, zdb_zone *zone, ptr_vector* rrset_to_sign_vector, ptr_vector *ksks, ptr_vector *zsks, ptr_vector *remove, ptr_vector* add)
4337 {
4338     /**************************************************************************
4339      * SIGNATURES HANDLING
4340      **************************************************************************/
4341 
4342     (void)remove;
4343 
4344     // if there are no rrset to be signed, don't bother going further
4345 
4346     if(ptr_vector_last_index(rrset_to_sign_vector) < 0)
4347     {
4348         return SUCCESS;
4349     }
4350 
4351     // eliminate potential duplicates (rare case)
4352 
4353     {
4354         ptr_vector_qsort(rrset_to_sign_vector, ptr_vector_compare_pointers_callback);
4355 
4356         void *prev = ptr_vector_get(rrset_to_sign_vector, 0);
4357         for(s32 i = 1; i <= ptr_vector_last_index(rrset_to_sign_vector);)
4358         {
4359             void *item = ptr_vector_get(rrset_to_sign_vector, i);
4360             if(item == prev)
4361             {
4362                 ptr_vector_remove_at(rrset_to_sign_vector, i);
4363                 continue;
4364             }
4365 
4366             prev = item;
4367 
4368             ++i;
4369         }
4370     }
4371 
4372     /*
4373      * for each rrset in rrset_to_sign
4374      *   for each valid zsk in the keyring
4375      *     start new signature
4376      *     add each record
4377      *     generate signature
4378      */
4379 
4380     log_debug("update: %{dnsname}: signing differences", diff->origin);
4381 
4382 #if DEBUG
4383     zone_diff_log(diff, MODULE_MSG_HANDLE, MSG_DEBUG2);
4384 #endif
4385 
4386     // if there is a chain, proceed with the changes
4387 
4388     ptr_vector rrset = PTR_VECTOR_EMPTY;
4389     dnskey_signature ds;
4390     dnskey_signature_init(&ds);
4391 
4392     // setup the view for the RRSET (RRSET abstraction for the part that generates signatures)
4393 
4394     struct resource_record_view rrv = {NULL, &zone_diff_label_rr_rrv_vtbl};
4395 
4396     // for each RRSET
4397 
4398     for(int i = 0; i <= ptr_vector_last_index(rrset_to_sign_vector); ++i)
4399     {
4400         zone_diff_fqdn_rr_set *rr_set = (zone_diff_fqdn_rr_set*)ptr_vector_get(rrset_to_sign_vector, i);
4401 
4402         log_debug1("update: %{dnsname}: signing (trying) %{dnstype} rrset @%p", diff->origin, &rr_set->rtype, rr_set);
4403 
4404         rrv.data = rr_set;
4405 
4406         ptr_vector_clear(&rrset);
4407 
4408         u8 rrsig_state_mask = ZONE_DIFF_RR_AUTOMATED;
4409 
4410         // for each record in the RRSET
4411 
4412         const u8* rr_fqdn = NULL;
4413 
4414         // accumulate records
4415 
4416         FOREACH_PTR_SET(void*,value, &rr_set->rr)
4417         {
4418             zone_diff_label_rr* rr = (zone_diff_label_rr*)value;
4419             rr_fqdn = rr->fqdn; // keep the fqdn from the first match
4420 
4421 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
4422             if(rr_set->rtype == TYPE_DNSKEY)
4423             {
4424                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
4425                 log_info("update: %{dnsname}: [%02x] %{dnsname} %9i %{typerdatadesc}", diff->origin, rr->state, rr->fqdn, rr->ttl, &rd);
4426             }
4427 #endif
4428 
4429             // if the RR will exist in the zone (A.K.A: not removed), add it to the collection to sign
4430             if((rr->state & ZONE_DIFF_RR_REMOVE) == 0)
4431             {
4432 #if DEBUG
4433                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
4434                 format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
4435                 log_debug2("update: %{dnsname}: covers %w %{dnsname} %9i %{typerdatadesc}%s", diff->origin, &temp_fw_0, rr->fqdn, rr->ttl, &rd,
4436                           ((rr->state & ZONE_DIFF_RR_AUTOMATED)!=0)?"<AUTOMATED>":"");
4437 #endif
4438                 rrsig_state_mask &= rr->state;
4439 
4440                 ptr_vector_append(&rrset, value);
4441             }
4442             else
4443             {
4444 #if DEBUG
4445                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
4446                 format_writer temp_fw_0 = {zone_diff_record_state_format, &rr->state};
4447                 log_debug2("update: %{dnsname}: ignore %w %{dnsname} %9i %{typerdatadesc}", diff->origin, &temp_fw_0, rr->fqdn, rr->ttl, &rd);
4448 #endif
4449             }
4450         }
4451 
4452         if(rr_fqdn == NULL)
4453         {
4454             continue;
4455         }
4456 
4457 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
4458         if(rr_set->rtype == TYPE_DNSKEY)
4459         {
4460             log_info("update: %{dnsname}: DNSKEY records may be updated", diff->origin);
4461         }
4462 #endif
4463 
4464         // if the collection is empty, nothing more to do for this RRSET
4465 
4466         zone_diff_fqdn *rrsig_label = zone_diff_fqdn_add(diff, rr_fqdn, NULL);
4467 
4468         if(ptr_vector_last_index(&rrset) < 0)
4469         {
4470             // except removing all signatures associated with it ...
4471 
4472             if(rrsig_label != NULL)
4473             {
4474 
4475 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
4476                 if(rr_set->rtype == TYPE_DNSKEY)
4477                 {
4478                     log_info("update: %{dnsname}: DNSKEY rrset empty, all its signatures will be removed", diff->origin);
4479                 }
4480 #endif
4481 
4482                 zone_diff_fqdn_rr_set *rrsig_label_rrset = zone_diff_fqdn_rr_set_add(rrsig_label, TYPE_RRSIG);
4483 
4484                 FOREACH_PTR_SET(void*,value, &rrsig_label_rrset->rr)
4485                 {
4486                     zone_diff_label_rr* rrsig_rr = (zone_diff_label_rr*)value;
4487 
4488                     if(rrsig_get_type_covered_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size) == rr_set->rtype)
4489                     {
4490 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
4491                         if(rr_set->rtype == TYPE_DNSKEY)
4492                         {
4493                             rdata_desc rd = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
4494                             log_info("update: %{dnsname}: will remove %{dnsname} %9i %{typerdatadesc}", diff->origin, rrsig_rr->fqdn, rrsig_rr->ttl, &rd);
4495                         }
4496 #endif
4497                         if((rrsig_rr->state & ZONE_DIFF_RR_REMOVED) == 0)
4498                         {
4499                             rrsig_rr->state &= ~ZONE_DIFF_RR_ADD;
4500                             rrsig_rr->state |= ZONE_DIFF_RR_REMOVE;
4501 #if DEBUG
4502                             rdata_desc rd = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
4503                             log_debug("update: %{dnsname}: will remove %{dnsname} %9i %{typerdatadesc}", diff->origin, rrsig_rr->fqdn, rrsig_rr->ttl, &rd);
4504 #endif
4505                             ptr_vector_append(remove, rrsig_rr);
4506                             rrsig_rr->state |= ZONE_DIFF_RR_REMOVED;
4507                         }
4508                     }
4509                 }
4510             }
4511             continue;
4512         }
4513 
4514         yassert(rrsig_label != NULL);
4515 
4516         zone_diff_fqdn_rr_set *rrsig_label_rrset = zone_diff_fqdn_rr_set_add(rrsig_label, TYPE_RRSIG);
4517 
4518         yassert(rrsig_label_rrset != NULL);
4519 
4520         // take note that some RRSIG records will be added
4521 
4522         rrsig_state_mask |= ZONE_DIFF_RR_ADD;
4523 
4524         bool canonize = TRUE;
4525 
4526         ptr_vector *keys;
4527 
4528         yassert(rr_set->rtype != TYPE_RRSIG);
4529 
4530         // use the adequate DNSKEY collection
4531 
4532         keys = (rr_set->rtype != TYPE_DNSKEY)?zsks:ksks;
4533 
4534         // for all keys from said collection
4535 
4536         for(int j = 0; j <= ptr_vector_last_index(keys); ++j)
4537         {
4538             const dnssec_key *key = (dnssec_key*)ptr_vector_get(keys, j);
4539 
4540             // check if the key is to be used (using the key_mask)
4541 
4542             if((rr_set->key_mask & (1ULL << j)) == 0)
4543             {
4544 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
4545                 if(rr_set->rtype == TYPE_DNSKEY)
4546                 {
4547                     log_info("update: %{dnsname} DNSKEY will not use key %03d %05d as the signature doesn't need an update", diff->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4548                 }
4549 #endif
4550 
4551 #if DEBUG
4552                 zone_diff_label_rr* rr = ptr_vector_get(&rrset, 0);
4553 
4554                 log_debug2("update: %{dnsname}: %{dnsname} %{dnstype} does not need a signature update for key %03d %05d",
4555                         diff->origin,
4556                         rr->fqdn, &rr->rtype,
4557                         dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4558 #endif
4559                 continue; // skip
4560             }
4561 
4562             // check if the key has private components
4563 
4564             if(!dnskey_is_private(key))
4565             {
4566 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
4567                 if(rr_set->rtype == TYPE_DNSKEY)
4568                 {
4569                     log_info("update: %{dnsname} DNSKEY cannot use key %03d %05d as it is not private",
4570                             diff->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4571                 }
4572 #endif
4573                 log_debug("update: %{dnsname}: key K%{dnsname}+%03d+%05d is not private", diff->origin,
4574                         dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4575                 continue;
4576             }
4577 
4578             if(dnskey_is_deactivated(key, time(NULL) - 5)) // don't generate it if it's about to expire
4579             {
4580 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
4581                 if(rr_set->rtype == TYPE_DNSKEY)
4582                 {
4583                     log_info("update: %{dnsname} DNSKEY cannot use key %03d %05d as its deactivated",
4584                             diff->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4585                 }
4586 #endif
4587                 log_debug("update: %{dnsname}: key K%{dnsname}+%03d+%05d is about to be deactivated", diff->origin,
4588                           dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4589                 continue;
4590             }
4591 
4592             zone_diff_label_rr *rrsig_rr = NULL;
4593 
4594             ya_result ret;
4595 
4596             s32 maxinterval = diff_generate_signature_interval(diff);
4597 
4598             // rrset_to_sign;
4599             if(ISOK(ret = dnskey_sign_rrset_with_maxinterval(key, &rrset, canonize, &rrv, maxinterval, (void **)&rrsig_rr)))
4600             {
4601                 canonize = FALSE;
4602 
4603                 // add the key to the add set
4604 
4605 #if DYNUPDATE_DIFF_DETAILED_DNSKEY_LOG
4606                 if(rr_set->rtype == TYPE_DNSKEY)
4607                 {
4608                     log_info("update: %{dnsname} DNSKEY has been signed with key %03d %05d", diff->origin, dnskey_get_domain(key), dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4609                 }
4610 #endif
4611 
4612                 log_debug2("update: %{dnsname}: signed %{dnsname} %{dnstype} rrset with key %03d %05d",diff->origin,
4613                         rrsig_rr->fqdn, &rr_set->rtype,
4614                         dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4615 
4616                 s32 signature_valid_until = rrsig_get_valid_until_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size);
4617 
4618                 // if the signature expires in this time
4619 
4620                 if(signature_valid_until > 0)
4621                 {
4622                     if(signature_valid_until < dnskey_get_inactive_epoch(key))
4623                     {
4624                         s32 signature_regeneration_time = signature_valid_until - diff->rrsig_validity_regeneration;
4625 
4626                         if(zone->progressive_signature_update.earliest_signature_expiration > signature_regeneration_time)
4627                         {
4628                             zone->progressive_signature_update.earliest_signature_expiration = signature_regeneration_time;
4629                         }
4630                     }
4631                     else
4632                     {
4633                         if(zone->progressive_signature_update.earliest_signature_expiration > signature_valid_until)
4634                         {
4635                             zone->progressive_signature_update.earliest_signature_expiration = signature_valid_until;
4636                         }
4637                     }
4638                 }
4639 
4640                 rrsig_rr->state |= rrsig_state_mask;
4641 #if 0 /* fix */
4642 #else
4643 #if DEBUG
4644                 {
4645                     rdata_desc rrsig_rr_desc = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
4646                     log_debug6("update: %{dnsname}: signature <= %p [%02x] %{dnsname} %i %{typerdatadesc}", diff->origin, rrsig_rr, rrsig_rr->state, rrsig_rr->fqdn, rrsig_rr->ttl, &rrsig_rr_desc);
4647                 }
4648 #endif
4649                 zone_diff_label_rr *final_rrsig_rr = zone_diff_fqdn_rr_set_rr_add_get(rrsig_label_rrset, rrsig_rr); // replace is right (should be unique)
4650 #if DEBUG
4651                 {
4652                     rdata_desc rrsig_rr_desc = {final_rrsig_rr->rtype, final_rrsig_rr->rdata_size, final_rrsig_rr->rdata};
4653                     log_debug6("update: %{dnsname}: signature => %p [%02x] %{dnsname} %i %{typerdatadesc}", diff->origin, final_rrsig_rr, final_rrsig_rr->state, final_rrsig_rr->fqdn, final_rrsig_rr->ttl, &rrsig_rr_desc);
4654                 }
4655 #endif
4656                 if((final_rrsig_rr->state & ZONE_DIFF_RR_IN_ZONE) == 0)
4657                 {
4658                     ptr_vector_append(add, final_rrsig_rr);
4659 
4660                     if(rrsig_label != NULL)
4661                     {
4662                         // int rrsig_count = 0;
4663 
4664                         FOREACH_PTR_SET(void*,value, &rrsig_label_rrset->rr)
4665                         {
4666                             zone_diff_label_rr* rrsig_rr = (zone_diff_label_rr*)value;
4667 
4668                             if((rrsig_rr->state & (ZONE_DIFF_RR_IN_ZONE|ZONE_DIFF_RR_REMOVE|ZONE_DIFF_RR_REMOVED)) == ZONE_DIFF_RR_IN_ZONE)   // if the key is marked as being removed, no need to remove it twice
4669                             {
4670                                 // key is kept or added
4671 
4672                                 u16 ctype = rrsig_get_type_covered_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size); // type covered by the signature
4673                                 if(ctype == rr_set->rtype)
4674                                 {
4675                                     u16 keytag = rrsig_get_key_tag_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size);
4676 
4677                                     if(keytag == dnskey_get_tag_const(key))
4678                                     {
4679                                         u8 keyalg = rrsig_get_algorithm_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size);
4680 
4681                                         if(keyalg == dnskey_get_algorithm(key))
4682                                         {
4683 #if DEBUG
4684                                             rdata_desc rrsig_rr_desc = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
4685                                             log_debug6("update: %{dnsname}: [%02x] %{dnsname} %i %{typerdatadesc} is obsolete", diff->origin, rrsig_rr->state, rrsig_rr->fqdn, rrsig_rr->ttl, &rrsig_rr_desc);
4686 #endif
4687                                             rrsig_rr->state |= ZONE_DIFF_RR_REMOVE;
4688                                             ptr_vector_append(remove, rrsig_rr);
4689                                             rrsig_rr->state |= ZONE_DIFF_RR_REMOVED;
4690                                         }
4691                                     }
4692                                 }
4693                             }
4694                         }
4695                     }
4696                 }
4697 #endif
4698                 //(void)rrsig_rr_set;
4699             }
4700             else
4701             {
4702                 log_warn("update: %{dnsname}: failed to sign with key %03d %05d: %r",
4703                         diff->origin,
4704                         dnskey_get_algorithm(key), dnskey_get_tag_const(key), ret);
4705                 // ...
4706             }
4707         } // for each key
4708 
4709         // remove signatures not covered by an active key
4710 
4711         if(rrsig_label != NULL)
4712         {
4713             int rrsig_count = 0;
4714             int rrsig_known = 0;
4715             int rrsig_ignored = 0;
4716 
4717             FOREACH_PTR_SET(void*,value, &rrsig_label_rrset->rr)
4718             {
4719                 ++rrsig_known;
4720 
4721                 zone_diff_label_rr* rrsig_rr = (zone_diff_label_rr*)value;
4722 #if DEBUG
4723                 rdata_desc rrsig_rr_desc = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
4724                 log_debug6("update: %{dnsname}: [%02x] %{dnsname} %i %{typerdatadesc}", diff->origin, rrsig_rr->state, rrsig_rr->fqdn, rrsig_rr->ttl, &rrsig_rr_desc);
4725 #endif
4726                 if(rrsig_rr->state & ZONE_DIFF_RR_REMOVE)   // if the key is marked as being removed, no need to remove it twice
4727                 {
4728                     rrsig_label->rrsig_removed = 1;
4729                     continue;
4730                 }
4731 
4732                 // key is kept or added
4733 
4734                 u16 ctype = rrsig_get_type_covered_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size); // type covered by the signature
4735                 if(ctype == rr_set->rtype)
4736                 {
4737                     u16 keytag = rrsig_get_key_tag_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size);
4738                     u8 keyalg = rrsig_get_algorithm_from_rdata(rrsig_rr->rdata, rrsig_rr->rdata_size);
4739 
4740                     bool keep = FALSE;
4741 
4742                     ++rrsig_ignored;
4743 
4744                     for(int j = 0; j <= ptr_vector_last_index(keys); ++j)
4745                     {
4746                         const dnssec_key *key = (dnssec_key*)ptr_vector_get(keys, j);
4747 
4748                         if((dnskey_get_algorithm(key) == keyalg) && (dnskey_get_tag_const(key) == keytag))
4749                         {
4750                             --rrsig_ignored;
4751                             ++rrsig_count;
4752                             keep = TRUE;
4753                             break;
4754                         }
4755                     }
4756 
4757                     if(keep)
4758                     {
4759                         if(rrsig_rr->state & ZONE_DIFF_RR_ADD)
4760                         {
4761                             rrsig_label->rrsig_added = 1;       // new
4762                         }
4763                         else
4764                         {
4765                             rrsig_label->rrsig_kept = 1;        // already in zone
4766                         }
4767                     }
4768                     else
4769                     {
4770 #if DEBUG
4771                         rdata_desc rd = {rrsig_rr->rtype, rrsig_rr->rdata_size, rrsig_rr->rdata};
4772                         log_debug("update: %{dnsname}: will remove %{dnsname} %9i %{typerdatadesc}", diff->origin, rrsig_rr->fqdn, rrsig_rr->ttl, &rd);
4773 #endif
4774                         rrsig_rr->state |= ZONE_DIFF_RR_REMOVE;
4775                         ptr_vector_append(remove, rrsig_rr);
4776                         rrsig_rr->state |= ZONE_DIFF_RR_REMOVED;
4777                     }
4778                 }
4779             } // for all RRSIG in the RRSIG rrset
4780 
4781             if(rrsig_count == 0)
4782             {
4783                 // record set cannot be properly signed
4784 
4785                 log_warn("update: %{dnsname}: %{dnsname} %{dnstype} not covered by a signature (%i signatures in the set, %i ignored for the type)",
4786                         diff->origin, rr_fqdn, &rr_set->rtype,
4787                         rrsig_known, rrsig_ignored);
4788 
4789                 if(rrsig_label != NULL)
4790                 {
4791                     int rrsig_index = 0;
4792                     FOREACH_PTR_SET(void*,value, &rrsig_label_rrset->rr)
4793                     {
4794                         zone_diff_label_rr* rrsig_rr = (zone_diff_label_rr*)value;
4795 
4796                         rdata_desc rrsig_record = {TYPE_RRSIG, rrsig_rr->rdata_size, rrsig_rr->rdata};
4797                         log_warn("update: %{dnsname}: %02i [%02x] %{dnsname} %5i %{typerdatadesc}", diff->origin, rrsig_index, rrsig_rr->state, rrsig_rr->fqdn, rrsig_rr->ttl, &rrsig_record);
4798                         ++rrsig_index;
4799                     }
4800                 }
4801 
4802                 dnskey_signature_finalize(&ds);
4803                 ptr_vector_destroy(&rrset);
4804 
4805                 return DNSSEC_ERROR_RRSIG_NOUSABLEKEYS;
4806             }
4807             else
4808             {
4809                 // record set cannot be properly signed and has no valid signatures
4810 #if DEBUG
4811                 log_debug1("update: %{dnsname}: %{dnsname} %{dnstype} is covered by a signature", diff->origin, rr_fqdn, &rr_set->rtype);
4812 #endif
4813             }
4814         } // if(rrsig_label != NULL)
4815     } // for(int i = 0; i <= ptr_vector_last_index(rrset_to_sign_vector); ++i) // FOR EACH RRSET
4816 
4817     dnskey_signature_finalize(&ds);
4818     ptr_vector_destroy(&rrset);
4819 
4820     return SUCCESS;
4821 }
4822 
4823 void
zone_diff_store_diff_dnskey_get_keys(zone_diff * diff,ptr_vector * ksks,ptr_vector * zsks)4824 zone_diff_store_diff_dnskey_get_keys(zone_diff *diff, ptr_vector *ksks, ptr_vector *zsks)
4825 {
4826     // remove all signing keys that are about to be removed
4827     // add all activated signing keys that are being added
4828 
4829     const zone_diff_fqdn *apex = diff->apex;
4830     const zone_diff_fqdn_rr_set *dnskey_rrset = zone_diff_fqdn_rr_get_const(apex, TYPE_DNSKEY);
4831 
4832     if(dnskey_rrset != NULL)
4833     {
4834         // for all keys, handle added and removed ones
4835 
4836         time_t now = time(NULL);
4837 
4838         dnssec_key *key;
4839 
4840         ptr_set_iterator rr_iter;
4841         ptr_set_iterator_init(&dnskey_rrset->rr, &rr_iter);
4842         while(ptr_set_iterator_hasnext(&rr_iter))
4843         {
4844             ptr_node *node = ptr_set_iterator_next_node(&rr_iter);
4845             zone_diff_label_rr *rr = (zone_diff_label_rr *)node->key;
4846 #if DEBUG
4847             log_debug("update: DNSKEY: 'K%{dnsname}+%03d+%05hd': key listed (%02x)", diff->origin,
4848                      dnskey_get_algorithm_from_rdata(rr->rdata),
4849                      dnskey_get_tag_from_rdata(rr->rdata, rr->rdata_size), rr->state);
4850 #endif
4851             if((rr->state & ZONE_DIFF_RR_REMOVE) == 0) // exists or is being added
4852             {
4853                 key = NULL;
4854                 ya_result ret = dnssec_keystore_load_private_key_from_rdata(rr->rdata, rr->rdata_size, rr->fqdn, &key);
4855 
4856                 if(ISOK(ret))
4857                 {
4858                     ptr_vector *keys = NULL;
4859 
4860                     if(dnskey_get_flags(key) == DNSKEY_FLAGS_KSK)
4861                     {
4862                         keys = ksks;
4863                     }
4864                     else if(dnskey_get_flags(key) == DNSKEY_FLAGS_ZSK)
4865                     {
4866                         keys = zsks;
4867                     }
4868 
4869                     // if key is activated, and not already in the (signing) set, add it
4870 #if DEBUG
4871                     log_debug("update: DNSKEY: 'K%{dnsname}+%03d+%05hd': key found, exists or is about to be added", diff->origin,
4872                              dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4873 #endif
4874                     if(dnskey_is_activated_lenient(key, now, 5))
4875                     {
4876 #if DEBUG
4877                         log_debug("update: DNSKEY: 'K%{dnsname}+%03d+%05hd': private key is active", diff->origin,
4878                                  dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4879 #endif
4880 
4881 #if DEBUG
4882                         log_debug("update: DNSKEY: 'K%{dnsname}+%03d+%05hd': private key added in signers", diff->origin,
4883                                  dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4884 #endif
4885                         ptr_vector_append(keys, key);
4886                     }
4887                     else
4888                     {
4889 #if DEBUG
4890                         log_debug("update: DNSKEY: 'K%{dnsname}+%03d+%05hd': private key is not active", diff->origin,
4891                                   dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4892 #endif
4893                     }
4894                 }
4895                 else // key is being removed
4896                 {
4897                     ya_result ret = dnssec_keystore_load_public_key_from_rdata(rr->rdata, rr->rdata_size, rr->fqdn, &key);
4898 
4899                     if(ISOK(ret))
4900                     {
4901 #if DEBUG
4902                         log_debug("update: DNSKEY: 'K%{dnsname}+%03d+%05hd': key found, about to be removed", diff->origin,
4903                                   dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4904 #endif
4905                         ptr_vector *keys = NULL;
4906 
4907                         if(dnskey_get_flags(key) == DNSKEY_FLAGS_KSK)
4908                         {
4909                             keys = ksks;
4910                         }
4911                         else if(dnskey_get_flags(key) == DNSKEY_FLAGS_ZSK)
4912                         {
4913                             keys = zsks;
4914                         }
4915 #if DEBUG
4916                         log_debug("update: DNSKEY: 'K%{dnsname}+%03d+%05hd': private key not loaded: %r", diff->origin,
4917                                  dnskey_get_algorithm_from_rdata(rr->rdata),
4918                                  dnskey_get_tag_from_rdata(rr->rdata, rr->rdata_size), ret);
4919 #endif
4920                         ptr_vector_append(keys, key);
4921                     }
4922                     else
4923                     {
4924                         log_err("update: DNSKEY: 'K%{dnsname}+%03d+%05hd': public key not loaded: %r", diff->origin,
4925                                  dnskey_get_algorithm_from_rdata(rr->rdata),
4926                                  dnskey_get_tag_from_rdata(rr->rdata, rr->rdata_size), ret);
4927                     }
4928                 }
4929             }
4930         }
4931 
4932     } // else would be surprising
4933 
4934 #if DEBUG
4935     for(int i = 0; i <= ptr_vector_last_index(ksks); ++i)
4936     {
4937         dnssec_key *key = (dnssec_key*)ptr_vector_get(ksks, i);
4938         log_debug3("update: DNSKEY: KSK: 'K%{dnsname}+%03d+%05hd': final state", diff->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4939     }
4940 
4941     for(int i = 0; i <= ptr_vector_last_index(zsks); ++i)
4942     {
4943         dnssec_key *key = (dnssec_key*)ptr_vector_get(zsks, i);
4944         log_debug3("update: DNSKEY: ZSK: 'K%{dnsname}+%03d+%05hd': final state", diff->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4945     }
4946 #endif
4947 }
4948 
4949 static ya_result
zone_diff_verify_dnskey_presence(zone_diff * diff,zdb_zone * zone,ptr_vector * rrset_to_sign,ptr_vector * ksks,ptr_vector * zsks)4950 zone_diff_verify_dnskey_presence(zone_diff *diff, zdb_zone *zone, ptr_vector *rrset_to_sign, ptr_vector *ksks, ptr_vector *zsks)
4951 {
4952     ya_result ret = SUCCESS;
4953     u8 maintain_mode = zone_get_maintain_mode(zone);
4954 
4955     if(maintain_mode > ZDB_ZONE_MAINTAIN_NOSEC)
4956     {
4957         for(int i = 0; i <= ptr_vector_last_index(ksks); ++i)
4958         {
4959             dnssec_key *key = (dnssec_key*)ptr_vector_get(ksks, i);
4960             log_debug3("update: DNSKEY: KSK: 'K%{dnsname}+%03d+%05hd': key visible", zone->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4961         }
4962 
4963         for(int i = 0; i <= ptr_vector_last_index(zsks); ++i)
4964         {
4965             dnssec_key *key = (dnssec_key*)ptr_vector_get(zsks, i);
4966             log_debug3("update: DNSKEY: ZSK: 'K%{dnsname}+%03d+%05hd': key visible", zone->origin, dnskey_get_algorithm(key), dnskey_get_tag_const(key));
4967         }
4968 
4969         zone_diff_fqdn *apex = zone_diff_fqdn_get(diff, diff->origin);
4970 
4971         if(!zone_diff_will_have_rrset_type(apex, TYPE_DNSKEY))
4972         {
4973             log_err("update: %{dnsname}: there are no DNSKEY in the zone", zone->origin);
4974             ret = ZDB_ERROR_ZONE_NO_ACTIVE_DNSKEY_FOUND;
4975         }
4976 
4977         for(int i = 0; i <= ptr_vector_last_index(rrset_to_sign); ++i)
4978         {
4979             zone_diff_fqdn_rr_set *rr_set = (zone_diff_fqdn_rr_set*)ptr_vector_get(rrset_to_sign, i);
4980 
4981             if(!diff->rrsig_update_allowed)
4982             {
4983                 if(rr_set->rtype != TYPE_DNSKEY)
4984                 {
4985                     if(ptr_vector_last_index(zsks) < 0)
4986                     {
4987                         log_warn("update: %{dnsname}: %{dnstype} record set is being modified but no ZSK can sign it", zone->origin, &rr_set->rtype);
4988                     }
4989                 }
4990                 else
4991                 {
4992                     if(ptr_vector_last_index(ksks) < 0)
4993                     {
4994                         log_warn("update: %{dnsname} DNSKEY record set is being modified but no KSK can sign it", zone->origin);
4995                     }
4996                 }
4997             }
4998         }
4999     }
5000 
5001     return ret;
5002 }
5003 
5004 static ya_result
zone_diff_store_diff(zone_diff * diff,zdb_zone * zone,ptr_vector * remove,ptr_vector * add)5005 zone_diff_store_diff(zone_diff *diff, zdb_zone *zone, ptr_vector *remove, ptr_vector *add)
5006 {
5007     // for all fqdn
5008     //   for all rrset
5009     //     for all marked rr (add or remove)
5010     //       put the rr(s) in the relevant vector
5011     //       proceed with dnssec on the side
5012     //     if changed and the rr must be signed
5013     //       put all signatures rr in the remove set
5014     //       generate relevant signatures and add them to the add set
5015 
5016     // add the dnssec changes, including signatures
5017 
5018     // then, because it's Y2 and not Y3, apply the changes into the DB with the journal ready to write
5019 
5020     // so ..
5021 
5022     ya_result ret;
5023 
5024     if(FAIL(ret = zone_diff_set_soa(diff, NULL)))
5025     {
5026         return ret;
5027     }
5028 
5029     /**************************************************************************
5030      * DIFF COMPUTATIONS
5031      **************************************************************************/
5032 
5033     // initialise the chain(s)
5034 
5035     dnssec_chain dc;
5036 
5037     u8 maintain_mode = zone_get_maintain_mode(zone);
5038 
5039     switch(maintain_mode)
5040     {
5041         case ZDB_ZONE_MAINTAIN_NSEC3:
5042         case ZDB_ZONE_MAINTAIN_NSEC3_OPTOUT:
5043         {
5044             dnssec_chain_init(&dc, (maintain_mode == ZDB_ZONE_MAINTAIN_NSEC3)?dynupdate_nsec3_chain_get_vtbl():dynupdate_nsec3_optout_chain_get_vtbl(), diff);
5045 
5046             nsec3_zone *n3 = zone->nsec.nsec3;
5047 
5048             while(n3 != NULL)
5049             {
5050                 const u8 *nsec3param_rdata = n3->rdata;
5051                 u8 nsec3_chain_status = 0;
5052                 nsec3_zone_get_status_from_rdata(zone, nsec3param_rdata, NSEC3_ZONE_RDATA_SIZE(n3), &nsec3_chain_status);
5053 
5054                 dnssec_chain_add_chain(&dc, (dnssec_chain_head_t)n3, (nsec3_chain_status & NSEC3_ZONE_REMOVING) != 0);
5055                 n3 = n3->next;
5056             }
5057             break;
5058         }
5059         case ZDB_ZONE_MAINTAIN_NSEC:
5060         {
5061             u8 nsec_chain_status = 0;
5062             nsec_zone_get_status(zone, &nsec_chain_status);
5063 
5064             dnssec_chain_init(&dc, dynupdate_nsec_chain_get_vtbl(), diff);
5065             dnssec_chain_add_chain(&dc, (dnssec_chain_head_t)zone->nsec.nsec, (nsec_chain_status & NSEC_ZONE_REMOVING) != 0);
5066             break;
5067         }
5068         default:
5069         {
5070             dnssec_chain_init(&dc, dynupdate_nosec_chain_get_vtbl(), diff);
5071             break;
5072         }
5073     }
5074 
5075     // update statuses, validates
5076 
5077     if(ISOK(ret = zone_diff_validate(diff)))
5078     {
5079         ptr_vector ksks = PTR_VECTOR_EMPTY;
5080         ptr_vector zsks = PTR_VECTOR_EMPTY;
5081         ptr_vector rrset_to_sign = PTR_VECTOR_EMPTY;
5082 
5083         // store changes in vectors and get the RR sets to sign
5084 
5085         s32 mandatory_changes = zone_diff_get_changes(diff, &rrset_to_sign, &ksks, &zsks, remove, add);
5086 
5087 #if DYNUPDATE_DIFF_DETAILED_LOG
5088         for(int i = 0; i <= ptr_vector_last_index(remove); ++i)
5089         {
5090             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(remove, i);
5091             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
5092 
5093             log_debug3("update: changes: %{dnsname}: - %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
5094         }
5095 
5096         for(int i = 0; i <= ptr_vector_last_index(add); ++i)
5097         {
5098             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add, i);
5099             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
5100 
5101             log_debug3("update: changes: %{dnsname}: + %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
5102         }
5103 #endif
5104 
5105 #if DEBUG
5106         log_debug1("update: %{dnsname}: diff changes edited", zone->origin);
5107         zone_diff_log(diff, MODULE_MSG_HANDLE, MSG_DEBUG2);
5108 #endif
5109 
5110         const bool changes_happened = (mandatory_changes > 0);
5111 
5112         if(changes_happened)
5113         {
5114             ret = zone_diff_verify_dnskey_presence(diff, zone, &rrset_to_sign, &ksks, &zsks);
5115 
5116             if(ISOK(ret))
5117             {
5118                 // sign the records, store the changes in vectors
5119 
5120                 ret = zone_diff_sign(diff, zone, &rrset_to_sign, &ksks, &zsks, remove, add);
5121 
5122 #if DYNUPDATE_DIFF_DETAILED_LOG
5123                 for(int i = 0; i <= ptr_vector_last_index(remove); ++i)
5124                 {
5125                     zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(remove, i);
5126                     rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
5127 
5128                     log_debug3("update: sign: %{dnsname}: - %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
5129                 }
5130 
5131                 for(int i = 0; i <= ptr_vector_last_index(add); ++i)
5132                 {
5133                     zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add, i);
5134                     rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
5135 
5136                     log_debug3("update: sign: %{dnsname}: + %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
5137                 }
5138 #endif
5139 
5140                 ptr_vector_destroy(&rrset_to_sign);
5141 
5142                 if(ISOK(ret))
5143                 {
5144                     zone_diff_get_chain_changes(diff, &dc);
5145 
5146                     // chain deletes should use the existing maps if possible (speed) or generate from the local state (all 'exists')
5147                     // chain adds should use the local state (all exists not removed + all adds)
5148 #if DEBUG
5149                     zone_diff_log(diff, MODULE_MSG_HANDLE, MSG_DEBUG2);
5150 #endif
5151                     dnssec_chain_store_diff(&dc, diff, &zsks, remove, add);
5152 
5153 #if DYNUPDATE_DIFF_DETAILED_LOG
5154                     for(int i = 0; i <= ptr_vector_last_index(remove); ++i)
5155                     {
5156                         zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(remove, i);
5157                         rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
5158 
5159                         log_debug3("update: store: %{dnsname}: - %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
5160                     }
5161 
5162                     for(int i = 0; i <= ptr_vector_last_index(add); ++i)
5163                     {
5164                         zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add, i);
5165                         rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
5166 
5167                         log_debug3("update: store: %{dnsname}: + %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
5168                     }
5169 #endif
5170                 }
5171             }
5172             else
5173             {
5174                 zone_diff_label_rr_vector_clear(remove);
5175                 zone_diff_label_rr_vector_clear(add);
5176                 ptr_vector_destroy(&rrset_to_sign);
5177             }
5178         }
5179         else
5180         {
5181             zone_diff_label_rr_vector_clear(remove);
5182             zone_diff_label_rr_vector_clear(add);
5183             ptr_vector_destroy(&rrset_to_sign);
5184 
5185             if(FAIL(mandatory_changes))
5186             {
5187                 log_warn("update: %{dnsname} update rejected: %r", zone->origin, mandatory_changes);
5188             }
5189         }
5190 
5191         dnssec_keystore_release_keys_from_vector(&zsks);
5192         dnssec_keystore_release_keys_from_vector(&ksks);
5193 
5194         ptr_vector_destroy(&zsks);
5195         ptr_vector_destroy(&ksks);
5196     }
5197 
5198     dnssec_chain_finalize(&dc);
5199 
5200     return ret;
5201 }
5202 
5203 #if ZDB_HAS_DNSSEC_SUPPORT
5204 
5205 /**
5206  * Get all DNSKEY records from the zone.
5207  * Load the private keys of these DNSKEY records in the keystore.
5208  *
5209  * @param zone
5210  * @return
5211  */
5212 
5213 ya_result
dynupdate_diff_load_private_keys(zdb_zone * zone)5214 dynupdate_diff_load_private_keys(zdb_zone *zone)
5215 {
5216     ya_result return_code = SUCCESS;
5217 
5218     // ensure all the private keys are available or servfail
5219 
5220     const zdb_packed_ttlrdata *dnskey_rrset = zdb_zone_get_dnskey_rrset(zone); // zone is locked
5221 
5222     int ksk_count = 0;
5223     int zsk_count = 0;
5224 
5225     if(dnskey_rrset != NULL)
5226     {
5227         do
5228         {
5229             u16 flags = DNSKEY_FLAGS(*dnskey_rrset);
5230             u8  algorithm = DNSKEY_ALGORITHM(*dnskey_rrset);
5231             u16 tag = DNSKEY_TAG(*dnskey_rrset);                  // note: expensive
5232             dnssec_key *key = NULL;
5233 
5234             if(!((flags == DNSKEY_FLAGS_KSK) && zdb_zone_get_rrsig_push_allowed(zone)))
5235             {
5236                 if(ISOK(return_code = dnssec_keystore_load_private_key_from_parameters(algorithm, tag, flags, zone->origin, &key))) // key properly released
5237                 {
5238                     dnskey_release(key);
5239                 }
5240                 else
5241                 {
5242                     log_warn("update: unable to load the private key 'K%{dnsname}+%03d+%05hd': %r", zone->origin, algorithm, tag, return_code);
5243                 }
5244             }
5245             else
5246             {
5247                 // on an RRSIG-push-allowed zone, don't try to load a KSK
5248             }
5249 
5250             if(flags == DNSKEY_FLAGS_KSK)
5251             {
5252                 ++ksk_count;
5253             }
5254             else if(flags == DNSKEY_FLAGS_ZSK)
5255             {
5256                 ++zsk_count;
5257             }
5258 
5259             dnskey_rrset = dnskey_rrset->next;
5260         }
5261         while(dnskey_rrset != NULL);
5262 
5263         return_code = ksk_count + zsk_count;
5264     }
5265     else
5266     {
5267         log_warn("update: there are no private keys in the zone %{dnsname}", zone->origin);
5268 
5269         return_code = DNSSEC_ERROR_RRSIG_NOZONEKEYS;
5270     }
5271 
5272     return return_code;
5273 }
5274 
5275 #endif
5276 
5277 /**
5278  * Writes the del then add records to the journal,
5279  * deletes the records marked as volatile,
5280  * exchanges the locks of the zone,
5281  * replays the journal
5282  * exchanges the locks back.
5283  *
5284  * Returns the result of the replay or SUCCESS if there was nothing to replay.
5285  *
5286  * @param zone
5287  * @param secondary_lock
5288  * @param del_vector
5289  * @param add_vector
5290  * @return
5291  */
5292 
5293 ya_result
dynupdate_diff_write_to_journal_and_replay(zdb_zone * zone,u8 secondary_lock,ptr_vector * del_vector,ptr_vector * add_vector)5294 dynupdate_diff_write_to_journal_and_replay(zdb_zone *zone, u8 secondary_lock, ptr_vector *del_vector, ptr_vector *add_vector)
5295 {
5296     ya_result ret = 0;
5297 
5298     bool changes_occurred = (ptr_vector_size(add_vector) + ptr_vector_size(del_vector)) > 2;
5299 
5300     if(changes_occurred)
5301     {
5302         // instead of storing to a buffer and back, could write an inputstream
5303         // translating the ptr_vector content on the fly
5304 
5305         s32 total_size_in_bytes = 0;
5306 
5307         for(int i = 0; i <= ptr_vector_last_index(del_vector); ++i)
5308         {
5309             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(del_vector, i);
5310             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
5311 
5312             log_debug2("update: %{dnsname}: - %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
5313 
5314             total_size_in_bytes += dnsname_len(rr->fqdn);
5315             total_size_in_bytes += 10;
5316             total_size_in_bytes += rr->rdata_size;
5317         }
5318 
5319         for(int i = 0; i <= ptr_vector_last_index(add_vector); ++i)
5320         {
5321             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add_vector, i);
5322             rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
5323 
5324             log_debug2("update: %{dnsname}: + %{dnsname} %9i %{typerdatadesc} ; (W+R)", zone->origin, rr->fqdn, rr->ttl, &rd);
5325 
5326 #if DEBUG
5327             switch(rr->rtype)
5328             {
5329                 case TYPE_NSEC:
5330                 {
5331                     const u8 *fqdn = rr->rdata;
5332                     const u8 *tbm = &fqdn[dnsname_len(fqdn)];
5333 
5334                     if((tbm - fqdn) == 0)
5335                     {
5336                         log_err("NSEC record has no type bitmap");
5337                         abort();
5338                     }
5339 
5340                     break;
5341                 }
5342                 default:
5343                 {
5344                     break;
5345                 }
5346             }
5347 #endif
5348 
5349             total_size_in_bytes += dnsname_len(rr->fqdn);
5350             total_size_in_bytes += 10;
5351             total_size_in_bytes += rr->rdata_size;
5352         }
5353 
5354         log_debug("update: %{dnsname}: writing message", zone->origin);
5355 
5356         output_stream baos;
5357 
5358         bytearray_output_stream_init(&baos, NULL, total_size_in_bytes);
5359 
5360         for(int i = 0; i <= ptr_vector_last_index(del_vector); ++i)
5361         {
5362             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(del_vector, i);
5363 
5364             output_stream_write_dnsname(&baos, rr->fqdn);
5365             output_stream_write_u16(&baos, rr->rtype);
5366             output_stream_write_u16(&baos, rr->rclass);
5367             output_stream_write_nu32(&baos, rr->ttl);
5368             output_stream_write_nu16(&baos, rr->rdata_size);
5369             output_stream_write(&baos, rr->rdata, rr->rdata_size);
5370 
5371             if((rr->state & ZONE_DIFF_RR_VOLATILE) != 0)
5372             {
5373                 zone_diff_label_rr_delete(rr);
5374             }
5375         }
5376 
5377         for(int i = 0; i <= ptr_vector_last_index(add_vector); ++i)
5378         {
5379             zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(add_vector, i);
5380 
5381             output_stream_write_dnsname(&baos, rr->fqdn);
5382             output_stream_write_u16(&baos, rr->rtype);
5383             output_stream_write_u16(&baos, rr->rclass);
5384             output_stream_write_nu32(&baos, rr->ttl);
5385             output_stream_write_nu16(&baos, rr->rdata_size);
5386             output_stream_write(&baos, rr->rdata, rr->rdata_size);
5387 
5388             if((rr->state & ZONE_DIFF_RR_VOLATILE) != 0)
5389             {
5390                 zone_diff_label_rr_delete(rr);
5391             }
5392         }
5393 
5394         log_debug1("update: %{dnsname}: message ready", zone->origin);
5395 
5396         input_stream bais;
5397 
5398         bytearray_input_stream_init(&bais, bytearray_output_stream_buffer(&baos), bytearray_output_stream_size(&baos), FALSE);
5399 
5400         log_debug("update: %{dnsname}: acquiring journal", zone->origin);
5401 
5402         journal* jnl = NULL;
5403         if(ISOK(ret = journal_acquire_from_zone_ex(&jnl, zone, TRUE)))
5404         {
5405             jnl->vtbl->minimum_serial_update(jnl, zone->text_serial);
5406 
5407             u32 journal_max_size = zone->wire_size / 3;
5408             zdb_zone_info_get_zone_max_journal_size(zone->origin, &journal_max_size);
5409             jnl->vtbl->maximum_size_update(jnl, journal_max_size);
5410 
5411             if(ISOK(ret = journal_append_ixfr_stream(jnl, &bais))) // writes a single page
5412             {
5413                 log_debug("update: %{dnsname}: wrote %i bytes to the journal", zone->origin, total_size_in_bytes);
5414 
5415                 bytearray_input_stream_reset(&bais);
5416 
5417                 u32 current_serial = 0;
5418 
5419                 if(secondary_lock != 0)
5420                 {
5421                     zdb_zone_exchange_locks(zone, ZDB_ZONE_MUTEX_SIMPLEREADER, secondary_lock);
5422                 }
5423 
5424                 ret = zdb_icmtl_replay_commit(zone, &bais, &current_serial);
5425 
5426                 if(secondary_lock != 0)
5427                 {
5428                     zdb_zone_exchange_locks(zone, secondary_lock, ZDB_ZONE_MUTEX_SIMPLEREADER);
5429                 }
5430 
5431                 if(ISOK(ret))
5432                 {
5433                     log_info("update: %{dnsname}: applied %u changes (%u bytes), serial=%u", zone->origin, ret, total_size_in_bytes, current_serial);
5434 
5435                     ret = total_size_in_bytes;
5436                 }
5437                 else
5438                 {
5439                     log_err("update: %{dnsname}: could not apply changes: %r", zone->origin, ret);
5440                 }
5441             }
5442             else if(ret == ZDB_JOURNAL_MUST_SAFEGUARD_CONTINUITY)
5443             {
5444                 log_info("update: %{dnsname}: could not write %i bytes to the journal as it is full and the zone needs to be locally stored first", zone->origin, total_size_in_bytes);
5445             }
5446             else
5447             {
5448                 log_err("update: %{dnsname}: could not write %i bytes to the journal: %r", zone->origin, total_size_in_bytes, ret);
5449             }
5450 
5451             journal_release(jnl);
5452         }
5453         else
5454         {
5455             log_err("update: %{dnsname}: could not acquire journal: %r", zone->origin, ret);
5456 	    }
5457 
5458         input_stream_close(&bais);
5459         output_stream_close(&baos);
5460     }
5461 
5462     return ret;
5463 }
5464 
5465 /**
5466  *
5467  * Computes the diff of an update.
5468  *
5469  * @param zone
5470  * @param reader
5471  * @param count
5472  * @param dryrun
5473  * @return
5474  */
5475 
5476 ya_result
dynupdate_diff(zdb_zone * zone,packet_unpack_reader_data * reader,u16 count,u8 secondary_lock,bool dryrun)5477 dynupdate_diff(zdb_zone *zone, packet_unpack_reader_data *reader, u16 count, u8 secondary_lock, bool dryrun)
5478 {
5479     yassert(zdb_zone_islocked(zone));
5480 
5481 #if DEBUG
5482     log_debug("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i)",
5483             zone->origin, zone, reader, count, secondary_lock, dryrun);
5484 #endif
5485 
5486     if(zdb_zone_invalid(zone))
5487     {
5488 #if DEBUG
5489         log_debug("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with ZDB_ERROR_ZONE_INVALID",
5490             zone->origin, zone, reader, count, secondary_lock, dryrun);
5491 #endif
5492         return ZDB_ERROR_ZONE_INVALID;
5493     }
5494 
5495     if(count == 0)
5496     {
5497 #if DEBUG
5498         log_debug("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) success with count == 0",
5499             zone->origin, zone, reader, count, secondary_lock, dryrun);
5500 #endif
5501         return SUCCESS;
5502     }
5503 
5504     if(packet_reader_opcode(reader) != (OPCODE_UPDATE >> OPCODE_SHIFT))
5505     {
5506 #if DEBUG
5507         log_debug("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) not an update message",
5508                   zone->origin, zone, reader, count, secondary_lock, dryrun);
5509 #endif
5510         return INVALID_STATE_ERROR;
5511     }
5512 
5513     // if the status was already set, stop
5514 
5515     if((zdb_zone_set_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF) & ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF) != 0)
5516     {
5517         return INVALID_STATE_ERROR; // already
5518     }
5519 
5520     zdb_packed_ttlrdata* soa = zdb_record_find(&zone->apex->resource_record_set, TYPE_SOA);
5521 
5522     if(soa == NULL)
5523     {
5524 #if DEBUG
5525         log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with ZDB_ERROR_NOSOAATAPEX",
5526             zone->origin, zone, reader, count, secondary_lock, dryrun);
5527 #endif
5528 
5529         zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5530 
5531         return ZDB_ERROR_NOSOAATAPEX;
5532     }
5533 
5534 #if DEBUG
5535     {
5536         u32 soa_serial = 0;
5537         rr_soa_get_serial(ZDB_PACKEDRECORD_PTR_RDATAPTR(soa), ZDB_PACKEDRECORD_PTR_RDATASIZE(soa), &soa_serial);
5538         log_debug("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) from serial %u",
5539                 zone->origin, zone, reader, count, secondary_lock, dryrun, soa_serial);
5540     }
5541 #endif
5542 
5543     zone_diff diff;
5544     zone_diff_init(&diff, zone, zdb_zone_get_rrsig_push_allowed(zone));
5545 
5546     dnsname_vector name_path;
5547 
5548 #if DEBUG
5549     memset(&name_path, 0xff, sizeof(name_path));
5550 #endif
5551 
5552     u8 *rname;
5553     u8 *rdata;
5554     //u32 rname_size;
5555     u32 rttl;
5556     ya_result ret; // = SUCCESS;
5557     ya_result ret_status = 0;
5558     //s32 zsk_key_update_mask = 0;
5559     u16 rtype;
5560     u16 rclass;
5561     u16 rdata_size;
5562     s8 has_valid_ksk = -1; // unknown (don't care yet)
5563 
5564     u8 wire[MAX_DOMAIN_LENGTH + 10 + 65535];
5565 
5566 #if DEBUG
5567     //rdata = (u8*)~0; // DEBUG
5568     //rname_size = ~0; // DEBUG
5569     //rttl = ~0;       // DEBUG
5570     rtype = ~0;      // DEBUG
5571     rclass = ~0;     // DEBUG
5572     //rdata_size = ~0; // DEBUG
5573 #endif
5574 
5575     bool changes_occurred = FALSE;
5576 
5577 #if ZDB_HAS_DNSSEC_SUPPORT
5578     // zone load private keys
5579 
5580     bool dnssec_zone = zdb_zone_is_maintained(zone);
5581     bool check_for_last_nsec3param_removal = FALSE;
5582 
5583     if(dnssec_zone)
5584     {
5585         dynupdate_diff_load_private_keys(zone);
5586     }
5587 #endif
5588 
5589     log_debug1("update: %{dnsname}: reading message", zone->origin);
5590 
5591     // marks the SOA as being automatically removed (as the serial will increase)
5592 
5593     zone_diff_record_remove_automated(&diff, zone->apex, zone->origin, TYPE_SOA, soa->ttl, ZDB_PACKEDRECORD_PTR_RDATASIZE(soa), ZDB_PACKEDRECORD_PTR_RDATAPTR(soa));
5594 
5595     int record_index = 0;
5596 
5597     do
5598     {
5599         u8 *p = wire;
5600         int s = sizeof(wire);
5601 
5602         if(FAIL(ret = packet_reader_read_fqdn(reader, p, s)))
5603         {
5604             log_err("update: %{dnsname}: failed reading next record fqdn: %r", zone->origin, ret);
5605 
5606             zone_diff_finalize(&diff);
5607 
5608 #if DEBUG
5609             log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed at fqdn with %r",
5610                     zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_FORMERR));
5611 #endif
5612             zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5613 
5614             return RCODE_ERROR_CODE(RCODE_FORMERR);
5615         }
5616 
5617         rname = p;
5618         //rname_size = ret;
5619         p += ret;
5620         s -= ret;
5621 
5622         if(!dnsname_locase_verify_charspace(rname))
5623         {
5624             log_err("update: %{dnsname}: fqdn contains illegal characters", zone->origin);
5625             log_memdump(MODULE_MSG_HANDLE,MSG_ERR, rname, dnsname_len(rname), 32);
5626 
5627             zone_diff_finalize(&diff);
5628 
5629 #if DEBUG
5630             log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5631                     zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_FORMERR));
5632 #endif
5633 
5634             zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5635 
5636             return RCODE_ERROR_CODE(RCODE_FORMERR);
5637         }
5638 
5639         if(!dnsname_is_subdomain(rname, zone->origin))
5640         {
5641             log_err("update: %{dnsname}: %{dnsname} is not a sub-domain", zone->origin, rname);
5642 
5643             zone_diff_finalize(&diff);
5644 
5645 #if DEBUG
5646             log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5647                     zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_NOTZONE));
5648 #endif
5649             zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5650 
5651             return RCODE_ERROR_CODE(RCODE_NOTZONE);
5652         }
5653 
5654         if((ret = packet_reader_read(reader, p, 10)) != 10)
5655         {
5656             ret = UNEXPECTED_EOF;
5657 
5658             log_err("update: %{dnsname}: failed reading next record fields: %r", zone->origin, ret);
5659 
5660             zone_diff_finalize(&diff);
5661 
5662 #if DEBUG
5663             log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5664                     zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_FORMERR));
5665 #endif
5666 
5667             zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5668 
5669             return RCODE_ERROR_CODE(RCODE_FORMERR);
5670         }
5671 
5672         rtype = GET_U16_AT(p[0]);
5673         rclass = GET_U16_AT(p[2]);
5674         rttl = ntohl(GET_U32_AT(p[4]));
5675         rdata_size = ntohs(GET_U16_AT(p[8]));
5676 
5677         if((rdata_size > 0) && (rclass == CLASS_ANY))
5678         {
5679             log_err("update: %{dnsname}: next record has non-empty rdata with class ANY: %r", zone->origin, RCODE_ERROR_CODE(RCODE_FORMERR));
5680 
5681             zone_diff_finalize(&diff);
5682 
5683 #if DEBUG
5684             log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5685                     zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_FORMERR));
5686 #endif
5687             zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5688 
5689             return RCODE_ERROR_CODE(RCODE_FORMERR);
5690         }
5691 
5692         /*
5693          * Simple consistency test:
5694          */
5695 
5696         if((rdata_size == 0) && (rclass != CLASS_ANY))
5697         {
5698             log_err("update: %{dnsname}: next record has empty rdata with non-ANY class: %r", zone->origin, RCODE_ERROR_CODE(RCODE_FORMERR));
5699 
5700             zone_diff_finalize(&diff);
5701 
5702 #if DEBUG
5703             log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5704                     zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_FORMERR));
5705 #endif
5706             zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5707 
5708             return RCODE_ERROR_CODE(RCODE_FORMERR);
5709         }
5710 
5711         if(rdata_size > 0)
5712         {
5713             if(FAIL(ret = packet_reader_read_rdata(reader, rtype, rdata_size, p, s)))
5714             {
5715                 log_err("update: %{dnsname}: failed reading next record rdata: %r", zone->origin, ret);
5716 
5717                 zone_diff_finalize(&diff);
5718 
5719 #if DEBUG
5720                 log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5721                         zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_FORMERR));
5722 #endif
5723                 zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5724 
5725                 return RCODE_ERROR_CODE(RCODE_FORMERR);
5726             }
5727 
5728             rdata = p;
5729             rdata_size = ret;
5730 
5731             rdata_desc wire_rdatadesc = {rtype, rdata_size, rdata};
5732             log_debug1("update: %{dnsname}: record [%2i]: %{dnsname} %i %{dnsclass} %{dnstype} %{rdatadesc}",
5733                        zone->origin, record_index, rname, rttl, &rclass, &rtype, &wire_rdatadesc);
5734         }
5735         else
5736         {
5737             rdata = NULL;
5738 
5739             log_debug1("update: %{dnsname}: record [%2i]: %{dnsname} %i %{dnsclass} %{dnstype}",
5740                        zone->origin, record_index, rname, rttl, &rclass, &rtype);
5741         }
5742 
5743         ++record_index;
5744 
5745         dnsname_to_dnsname_vector(rname, &name_path);
5746 
5747         s32 idx;
5748 
5749         for(idx = 0; idx < zone->origin_vector.size; idx++)
5750         {
5751             if(!dnslabel_equals(zone->origin_vector.labels[zone->origin_vector.size - idx], name_path.labels[name_path.size - idx]))
5752             {
5753                 log_err("update: %{dnsname}: %{dnsname} manual add/del of %{dnstype} records refused", zone->origin, rname, &rtype);
5754 
5755                 zone_diff_finalize(&diff);
5756 #if DEBUG
5757                 log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5758                         zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_NOTZONE));
5759 #endif
5760                 zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5761 
5762                 return RCODE_ERROR_CODE(RCODE_NOTZONE);
5763             }
5764         }
5765 
5766         if((rtype == TYPE_NSEC) || (rtype == TYPE_NSEC3))
5767         {
5768             // reject any dynupdate operation on a dnssec-maintained record.
5769 
5770             log_err("update: %{dnsname}: %{dnsname} manual add/del of %{dnstype} records refused", zone->origin, rname, &rtype);
5771 
5772             zone_diff_finalize(&diff);
5773 
5774 #if DEBUG
5775             log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5776                     zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_REFUSED));
5777 #endif
5778             zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5779 
5780             return RCODE_ERROR_CODE(RCODE_REFUSED);
5781         }
5782 
5783 #if ZDB_HAS_NSEC3_SUPPORT // sanity checks
5784         // If the record is an NSEC3PARAM at the APEX
5785         if(rtype == TYPE_NSEC3PARAM)
5786         {
5787             if(!dnsname_equals_ignorecase(zone->origin, rname))
5788             {
5789                 // reject adding NSEC3PARAM anywhere else than in the apex
5790 
5791                 log_err("update: %{dnsname}: %{dnsname} NSEC3PARAM : type is only allowed in the apex", zone->origin, rname);
5792 
5793                 zone_diff_finalize(&diff);
5794 
5795                 zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5796 
5797                 return RCODE_ERROR_CODE(RCODE_REFUSED);
5798             }
5799 
5800             if(!ZONE_HAS_NSEC3PARAM(zone) && zdb_zone_has_nsec_chain(zone))
5801             {
5802                 // don't add/del NSEC3PARAM on a zone that is not already NSEC3 (it works if the zone is not secure but only if the zone has keys already. So for now : disabled)
5803 
5804                 log_err("update: %{dnsname}: %{dnsname} NSEC3PARAM add/del refused on an non-dnssec3 zone", zone->origin, rname);
5805 
5806                 zone_diff_finalize(&diff);
5807 
5808 #if DEBUG
5809                 log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5810                         zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_REFUSED));
5811 #endif
5812                 zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5813 
5814                 return RCODE_ERROR_CODE(RCODE_REFUSED);
5815             }
5816             else
5817             {
5818                 if((rdata != NULL) && (NSEC3_RDATA_ALGORITHM(rdata) != NSEC3_DIGEST_ALGORITHM_SHA1))
5819                 {
5820                     // don't touch an unsupported digest
5821 
5822                     log_err("update: %{dnsname}: %{dnsname} NSEC3PARAM with unsupported digest algorithm %d", zone->origin, rname, NSEC3_RDATA_ALGORITHM(rdata));
5823 #if DEBUG
5824                     log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5825                         zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_NOTIMP));
5826 #endif
5827                     zone_diff_finalize(&diff);
5828 
5829                     zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5830 
5831                     return RCODE_ERROR_CODE(RCODE_NOTIMP);
5832                 }
5833 
5834                 if(rclass == CLASS_ANY) // remove all
5835                 {
5836                     // don't remove all NSEC3PARAMs from an NSEC3 zone
5837 
5838                     log_err("update: %{dnsname}: %{dnsname} cannot remove all NSEC3PARAM of an NSEC3 zone", zone->origin, rname);
5839 
5840                     zone_diff_finalize(&diff);
5841 #if DEBUG
5842                     log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5843                         zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_REFUSED));
5844 #endif
5845                     zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5846 
5847                     return RCODE_ERROR_CODE(RCODE_REFUSED);
5848                 }
5849                 else if(rclass == CLASS_NONE) // remove one
5850                 {
5851                     /// @note important: don't remove the first NSEC3PARAM from an NSEC3 zone if no other is available
5852                     ///       also note that given the new mechanisms, an NSEC3PARAM being added will not count as one until
5853                     ///       the whole chain has been created
5854                     ///       This condition is tested later.
5855 
5856                     check_for_last_nsec3param_removal = TRUE;
5857                 }
5858                 else
5859                 {
5860                     // scan-build false positive : assumes rdata_size < 0 => impossible
5861                     //                                  or ((rdata_size == 0) & (rclass == CLASS_ANY)) => this would branch in the first "if" a few lines above
5862                     ret = nsec3_zone_set_status(zone, ZDB_ZONE_MUTEX_DYNUPDATE, NSEC3PARAM_RDATA_ALGORITHM(rdata), 0, NSEC3PARAM_RDATA_ITERATIONS(rdata), NSEC3PARAM_RDATA_SALT(rdata), NSEC3PARAM_RDATA_SALT_LEN(rdata), NSEC3_ZONE_ENABLED|NSEC3_ZONE_GENERATING);
5863                     continue;
5864                 }
5865             }
5866         } // type == TYPE_NSEC3PARAM
5867 #endif // ZDB_HAS_NSEC3_SUPPORT
5868 
5869         if(rclass == CLASS_NONE)
5870         {
5871             assert(rdata != NULL);
5872 
5873             // delete from an rrset
5874 
5875             if(rttl != 0)
5876             {
5877                 zone_diff_finalize(&diff);
5878 
5879                 log_err("update: %{dnsname}: %{dnsname} record delete expected a TTL set to 0", zone->origin, rname);
5880 
5881 #if DEBUG
5882                 log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5883                         zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_FORMERR));
5884 #endif
5885                 zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5886 
5887                 return RCODE_ERROR_CODE(RCODE_FORMERR);
5888             }
5889 
5890             if(name_path.size <= zone->origin_vector.size)
5891             {
5892                 if((rtype == TYPE_SOA) || (rtype == TYPE_ANY))
5893                 {
5894                     // refused
5895 
5896                     log_err("update: %{dnsname}: refused", zone->origin);
5897 
5898                     zone_diff_finalize(&diff);
5899 
5900 #if DEBUG
5901                     log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
5902                             zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_REFUSED));
5903 #endif
5904                     zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
5905 
5906                     return RCODE_ERROR_CODE(RCODE_REFUSED);
5907                 }
5908 
5909                 if(rtype == TYPE_DNSKEY)
5910                 {
5911                     u16 key_flags = DNSKEY_FLAGS_FROM_RDATA(rdata); // scan-build false positive
5912                                                                     // (rdata == NULL) && (rdata_size == 0) can only occur if (rclass == CLASS_ANY)
5913                                                                     // the condition is tested and exited for a FORMERR around line 5557
5914 
5915                     if(key_flags == DNSKEY_FLAGS_ZSK)
5916                     {
5917                         ret_status |= DYNUPDATE_DIFF_RETURN_DNSKEY_REMOVED;
5918                     }
5919 
5920                     if(has_valid_ksk < 0)
5921                     {
5922                         has_valid_ksk = dnssec_keystore_has_usable_ksk(zone->origin, time(NULL))?1:0;
5923                     }
5924                 }
5925             }
5926 
5927 #if DEBUG
5928             log_debug("update: %{dnsname}: delete %{dnsname} %{dnstype} any", zone->origin, rname, &rtype);
5929 #endif
5930             zdb_rr_label* rr_label = zdb_rr_label_find_exact(zone->apex, name_path.labels, (name_path.size - zone->origin_vector.size) - 1);
5931             if(rr_label != NULL)
5932             {
5933 #if DEBUG
5934                 if(RR_LABEL_IRRELEVANT(rr_label)) // debug
5935                 {
5936                     log_debug("update: %{dnsname}: %{dnsname} is irrelevant (0)", zone->origin, rname);
5937                 }
5938 #endif
5939                 zdb_packed_ttlrdata *rr;
5940                 if((rr = zdb_record_find(&rr_label->resource_record_set, rtype)) != NULL)
5941                 {
5942                     bool exists = FALSE;
5943                     do
5944                     {
5945                         if(ZDB_PACKEDRECORD_PTR_RDATASIZE(rr) == rdata_size)
5946                         {
5947                             // scan-build false positive : rdata cannot be NULL
5948                             // (rdata == NULL) && (rdata_size == 0) can only occur if (rclass == CLASS_ANY)
5949                             // the condition is tested and exited for a FORMERR around line 5557
5950 
5951                             if(memcmp(ZDB_PACKEDRECORD_PTR_RDATAPTR(rr), rdata, rdata_size) == 0)
5952                             {
5953                                 exists = TRUE;
5954                                 break;
5955                             }
5956                         }
5957                         rr = rr->next;
5958                     }
5959                     while(rr != NULL);
5960 
5961                     if(exists)
5962                     {
5963                         if(rr_label != zone->apex)
5964                         {
5965 #if 0 /* fix */
5966 #else
5967                             zone_diff_add_fqdn_children(&diff, rname, rr_label);
5968                             zone_diff_add_fqdn_parents_up_to_below_apex(&diff, rname, zone);
5969 #endif
5970                         }
5971 #if 0 /* fix */
5972 #else
5973                         if(!zone_diff_record_remove_existing(&diff, rr_label, rname, rtype, rttl, rdata_size, rdata))
5974                         {
5975                             rdata_desc rd = {rtype, rdata_size, rdata};
5976                             log_warn("update: %{dnsname}: delete %{dnsname} %{typerdatadesc} not in zone", zone->origin, rname, &rd);
5977                         }
5978 #endif
5979                     }
5980                     else
5981                     {
5982                         log_debug("update: %{dnsname}: delete %{dnsname} NONE %{dnstype}: no record match", zone->origin, rname, &rtype);
5983                     }
5984                 }
5985                 else
5986                 {
5987                     log_debug("update: %{dnsname}: delete %{dnsname} NONE %{dnstype}: no type match", zone->origin, rname, &rtype);
5988                 }
5989             }
5990             else
5991             {
5992                 log_debug("update: %{dnsname}: delete %{dnsname} NONE %{dnstype}: no label match", zone->origin, rname, &rtype);
5993             }
5994         }
5995         else if(rclass == CLASS_ANY) // delete all RRSETs
5996         {
5997             if((rttl != 0) || (rdata_size != 0))
5998             {
5999                 log_err("update: %{dnsname}: format error", zone->origin);
6000 
6001                 zone_diff_finalize(&diff);
6002 #if DEBUG
6003                 log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
6004                         zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_FORMERR));
6005 #endif
6006                 zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
6007 
6008                 return RCODE_ERROR_CODE(RCODE_FORMERR);
6009             }
6010 
6011             if(name_path.size <= zone->origin_vector.size)
6012             {
6013                 if((rtype == TYPE_SOA) || (rtype == TYPE_ANY))
6014                 {
6015                     // refused
6016 
6017                     log_err("update: %{dnsname}: refused", zone->origin);
6018                     zone_diff_finalize(&diff);
6019 #if DEBUG
6020                     log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
6021                             zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_REFUSED));
6022 #endif
6023                     zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
6024 
6025                     return RCODE_ERROR_CODE(RCODE_REFUSED);
6026                 }
6027 
6028                 if(rtype == TYPE_DNSKEY)
6029                 {
6030                     // get all keys from the zone_diff
6031                     // if one of these keys is a ZSK, set the ret_status flag accordingly
6032 
6033                     const zone_diff_fqdn *apex = zone_diff_fqdn_get_const(&diff, zone->origin);
6034                     const zone_diff_fqdn_rr_set *dnskey_rrset = zone_diff_fqdn_rr_get_const(apex, TYPE_DNSKEY);
6035 
6036                     if(dnskey_rrset != NULL)
6037                     {
6038                         ptr_set_iterator rr_iter;
6039 
6040                         ptr_set_iterator_init(&dnskey_rrset->rr, &rr_iter);
6041 
6042                         while(ptr_set_iterator_hasnext(&rr_iter))
6043                         {
6044                             ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
6045                             zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
6046                             if((rr->state & ZONE_DIFF_RR_IN_ZONE) != 0)
6047                             {
6048                                 u16 key_flags = DNSKEY_FLAGS_FROM_RDATA(rr->rdata);
6049                                 if(key_flags == DNSKEY_FLAGS_ZSK)
6050                                 {
6051                                     ret_status |= DYNUPDATE_DIFF_RETURN_DNSKEY_REMOVED;
6052                                 }
6053 
6054                                 if(has_valid_ksk < 0)
6055                                 {
6056                                     has_valid_ksk = dnssec_keystore_has_usable_ksk(zone->origin, time(NULL))?1:0;
6057                                 }
6058 
6059                                 diff.may_add_dnskey = TRUE;
6060                                 break;
6061                             }
6062                         }
6063 
6064                         diff.may_remove_dnskey = TRUE;
6065 
6066                         if(has_valid_ksk < 0)
6067                         {
6068                             has_valid_ksk = dnssec_keystore_has_usable_ksk(zone->origin, time(NULL))?1:0;
6069                         }
6070                     }
6071                     else
6072                     {
6073                         diff.may_remove_dnskey = FALSE;
6074                         has_valid_ksk = FALSE;
6075                     }
6076                 }
6077             }
6078 
6079             if(rtype != TYPE_ANY)
6080             {
6081                 // delete an rrset
6082 
6083 #if DEBUG
6084                 log_debug2("update: %{dnsname}: delete %{dnsname} %{dnstype} ...", zone->origin, rname, &rtype);
6085 #endif
6086                 zdb_rr_label *rr_label = zdb_rr_label_find_exact(zone->apex, name_path.labels, (name_path.size - zone->origin_vector.size) - 1);
6087                 if(rr_label != NULL)
6088                 {
6089 #if DEBUG
6090                     if(RR_LABEL_IRRELEVANT(rr_label)) // debug
6091                     {
6092                         log_debug2("update: %{dnsname}: %{dnsname} is irrelevant (1)", zone->origin, rname);
6093                     }
6094 #endif
6095                     if(zdb_record_find(&rr_label->resource_record_set, rtype) != NULL)
6096                     {
6097                         if(rr_label != zone->apex)
6098                         {
6099 #if 0 /* fix */
6100 #else
6101                             zone_diff_add_fqdn_children(&diff, rname, rr_label);
6102                             zone_diff_add_fqdn_parents_up_to_below_apex(&diff, rname, zone);
6103 #endif
6104                         }
6105                         zone_diff_record_remove_all(&diff, rr_label, rname, rtype);
6106                     }
6107                     else
6108                     {
6109                         log_debug("update: %{dnsname}: delete %{dnsname} %{dnstype} ANY: no type match", zone->origin, rname, &rtype);
6110                     }
6111                 }
6112                 else
6113                 {
6114                     log_debug("update: %{dnsname}: delete %{dnsname} %{dnstype} ANY: no label match", zone->origin, rname, &rtype);
6115                 }
6116             }
6117             else
6118             {
6119                 // delete all rrsets
6120 
6121 #if DEBUG
6122                 log_debug2("update: %{dnsname}: delete %{dnsname} %{dnstype} ...", zone->origin, rname, &rtype);
6123 #endif
6124                 zdb_rr_label* rr_label = zdb_rr_label_find_exact(zone->apex, name_path.labels, (name_path.size - zone->origin_vector.size) - 1);
6125                 if(rr_label != NULL)
6126                 {
6127 #if DEBUG
6128                     if(RR_LABEL_IRRELEVANT(rr_label)) // debug
6129                     {
6130                         log_debug2("update: %{dnsname}: %{dnsname} is irrelevant (2)", zone->origin, rname);
6131                     }
6132                     if(RR_LABEL_EMPTY_TERMINAL(rr_label))
6133                     {
6134                         log_debug2("update: %{dnsname}: %{dnsname} is an empty terminal (2)", zone->origin, rname);
6135                     }
6136 #endif
6137                     if(rr_label != zone->apex)
6138                     {
6139 #if 0 /* fix */
6140 #else
6141                         zone_diff_add_fqdn_children(&diff, rname, rr_label);
6142                         zone_diff_add_fqdn_parents_up_to_below_apex(&diff, rname, zone);
6143 #endif
6144                         zone_diff_record_remove_all_sets(&diff, rr_label, rname);
6145                     }
6146                     else
6147                     {
6148                         // apex
6149 
6150                         log_err("update: %{dnsname}: removing all records from the apex is forbidden", zone->origin);
6151 
6152                         zone_diff_finalize(&diff);
6153 
6154 #if DEBUG
6155                         log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
6156                                 zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_REFUSED));
6157 #endif
6158                         zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
6159 
6160                         return RCODE_ERROR_CODE(RCODE_REFUSED);
6161                     }
6162                 }
6163                 else
6164                 {
6165                     log_debug("update: %{dnsname}: delete %{dnsname} %{dnstype} ANY: no label match", zone->origin, rname, &rtype);
6166                 }
6167             }
6168         }
6169         else
6170         {
6171             // add record to an rrset
6172 
6173             assert(rdata != NULL); // to help scan-build
6174 
6175             // scan-build false positive : rdata cannot be NULL
6176             // (rdata == NULL) && (rdata_size == 0) can only occur if (rclass == CLASS_ANY)
6177             // the condition is tested and exited for a FORMERR around line 5557
6178 
6179             zdb_rr_label* rr_label = zdb_rr_label_find_exact(zone->apex, name_path.labels, (name_path.size - zone->origin_vector.size) - 1);
6180             zone_diff_record_add(&diff, rr_label, rname, rtype, rttl, rdata_size, rdata);
6181 
6182             const u8 *above_fqdn = rname;
6183             for(int index = 1; index < name_path.size; ++index)
6184             {
6185                 zdb_rr_label* above_rr_label = zdb_rr_label_find_exact(zone->apex, name_path.labels + index, (name_path.size - index - zone->origin_vector.size) - 1);
6186                 above_fqdn += above_fqdn[0] + 1;
6187                 zone_diff_fqdn_add(&diff, above_fqdn, above_rr_label);
6188             }
6189 
6190             if(rr_label != NULL)
6191             {
6192                 if(rr_label != zone->apex)
6193                 {
6194 #if 0 /* fix */
6195 #else
6196                     zone_diff_add_fqdn_children(&diff, rname, rr_label);
6197                     zone_diff_add_fqdn_parents_up_to_below_apex(&diff, rname, zone);
6198 #endif
6199                 }
6200                 else
6201                 {
6202                     if(rtype == TYPE_DNSKEY)
6203                     {
6204                         u16 key_flags = DNSKEY_FLAGS_FROM_RDATA(rdata);
6205                         if(key_flags == DNSKEY_FLAGS_ZSK)
6206                         {
6207                             ret_status |= DYNUPDATE_DIFF_RETURN_DNSKEY_ADDED;
6208                         }
6209 
6210                         diff.may_add_dnskey = TRUE;
6211                     }
6212                 }
6213             }
6214         }
6215     }
6216     while(--count > 0);
6217 
6218     if(check_for_last_nsec3param_removal)
6219     {
6220         bool at_least_one_nsec3param_remains = FALSE;
6221 
6222         // look if there is any NSEC3PARAM remaining in the zone
6223         const zone_diff_fqdn *apex = zone_diff_fqdn_get_const(&diff, zone->origin);
6224         const zone_diff_fqdn_rr_set *nsec3param_rrset = zone_diff_fqdn_rr_get_const(apex, TYPE_NSEC3PARAM);
6225 
6226         if(nsec3param_rrset != NULL)
6227         {
6228             ptr_set_iterator rr_iter;
6229 
6230             ptr_set_iterator_init(&nsec3param_rrset->rr, &rr_iter);
6231 
6232             while(ptr_set_iterator_hasnext(&rr_iter))
6233             {
6234                 ptr_node *rr_node = ptr_set_iterator_next_node(&rr_iter);
6235                 zone_diff_label_rr *rr = (zone_diff_label_rr*)rr_node->value;
6236                 if((rr->state & (ZONE_DIFF_RR_ADD|ZONE_DIFF_RR_REMOVE)) != ZONE_DIFF_RR_REMOVE)
6237                 {
6238                     at_least_one_nsec3param_remains = TRUE;
6239                     break;
6240                 }
6241             }
6242 
6243             if(!at_least_one_nsec3param_remains)
6244             {
6245                 log_err("update: %{dnsname}: %{dnsname} cannot remove the last NSEC3PARAM of an NSEC3 zone", zone->origin, rname);
6246 
6247                 zone_diff_finalize(&diff);
6248 
6249 #if DEBUG
6250                 log_err("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) failed with %r",
6251                         zone->origin, zone, reader, count, secondary_lock, dryrun, RCODE_ERROR_CODE(RCODE_REFUSED));
6252 #endif
6253                 zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
6254 
6255                 return RCODE_ERROR_CODE(RCODE_REFUSED);
6256             }
6257         }
6258         // else there was no NSEC3PARAM to begin with
6259     }
6260 
6261     if(ISOK(ret) && !dryrun)
6262     {
6263         ptr_vector add = PTR_VECTOR_EMPTY;
6264         ptr_vector del = PTR_VECTOR_EMPTY;
6265 
6266 #if DEBUG
6267         log_debug1("update: %{dnsname}: storing diff", zone->origin);
6268         zone_diff_log(&diff, MODULE_MSG_HANDLE, MSG_DEBUG2);
6269 #endif
6270         if(ISOK(ret = zone_diff_store_diff(&diff, zone, &del, &add)))
6271         {
6272             zdb_zone_error_status_clear(zone, ZDB_ZONE_ERROR_STATUS_DIFF_FAILEDNOUSABLE_KEYS);
6273 
6274 #if DEBUG
6275             log_debug1("update: %{dnsname}: stored diff", zone->origin);
6276 
6277             for(int i = 0; i <= ptr_vector_last_index(&del); ++i)
6278             {
6279                 zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&del, i);
6280                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
6281                 log_debug1("update: %{dnsname}: - %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
6282             }
6283 
6284             for(int i = 0; i <= ptr_vector_last_index(&add); ++i)
6285             {
6286                 zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&add, i);
6287                 rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
6288                 log_debug1("update: %{dnsname}: + %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
6289             }
6290 #endif
6291 
6292             changes_occurred = (ptr_vector_size(&add) + ptr_vector_size(&del)) > 2;
6293 
6294 #if DEBUG
6295             log_debug1("update: %{dnsname}: changes: %i", zone->origin, changes_occurred);
6296 #endif
6297 
6298             if(changes_occurred)
6299             {
6300                 // instead of storing to a buffer and back, could write an inputstream
6301                 // translating the ptr_vector content on the fly
6302 
6303                 s32 total = 0;
6304 
6305                 for(int i = 0; i <= ptr_vector_last_index(&del); ++i)
6306                 {
6307                     zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&del, i);
6308                     rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
6309 
6310                     log_debug2("update: %{dnsname}: - %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
6311 
6312                     total += dnsname_len(rr->fqdn);
6313                     total += 10;
6314                     total += rr->rdata_size;
6315                 }
6316 
6317                 for(int i = 0; i <= ptr_vector_last_index(&add); ++i)
6318                 {
6319                     zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&add, i);
6320                     rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
6321 
6322                     log_debug2("update: %{dnsname}: + %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
6323 
6324                     total += dnsname_len(rr->fqdn);
6325                     total += 10;
6326                     total += rr->rdata_size;
6327                 }
6328 
6329                 output_stream baos;
6330 
6331                 bytearray_output_stream_init(&baos, NULL, total);
6332 
6333                 for(int i = 0; i <= ptr_vector_last_index(&del); ++i)
6334                 {
6335                     zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&del, i);
6336                     /*
6337                     rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
6338                     log_debug("update: %{dnsname}: - %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
6339                     */
6340                     output_stream_write_dnsname(&baos, rr->fqdn);
6341                     output_stream_write_u16(&baos, rr->rtype);
6342                     output_stream_write_u16(&baos, rr->rclass);
6343                     output_stream_write_nu32(&baos, rr->ttl);
6344                     output_stream_write_nu16(&baos, rr->rdata_size);
6345                     output_stream_write(&baos, rr->rdata, rr->rdata_size);
6346                 }
6347 
6348                 for(int i = 0; i <= ptr_vector_last_index(&add); ++i)
6349                 {
6350                     zone_diff_label_rr *rr = (zone_diff_label_rr*)ptr_vector_get(&add, i);
6351                     /*
6352                     rdata_desc rd = {rr->rtype, rr->rdata_size, rr->rdata};
6353                     log_debug("update: %{dnsname}: + %{dnsname} %9i %{typerdatadesc}", zone->origin, rr->fqdn, rr->ttl, &rd);
6354                     */
6355                     output_stream_write_dnsname(&baos, rr->fqdn);
6356                     output_stream_write_u16(&baos, rr->rtype);
6357                     output_stream_write_u16(&baos, rr->rclass);
6358                     output_stream_write_nu32(&baos, rr->ttl);
6359                     output_stream_write_nu16(&baos, rr->rdata_size);
6360                     output_stream_write(&baos, rr->rdata, rr->rdata_size);
6361                 }
6362 
6363                 input_stream bais;
6364 
6365                 bytearray_input_stream_init(&bais, bytearray_output_stream_buffer(&baos), bytearray_output_stream_size(&baos), FALSE);
6366 
6367                 journal* jnl = NULL;
6368                 if(ISOK(ret = journal_acquire_from_zone_ex(&jnl, zone, TRUE)))
6369                 {
6370                     jnl->vtbl->minimum_serial_update(jnl, zone->text_serial);
6371 
6372                     u32 journal_max_size = zone->wire_size / 3;
6373                     zdb_zone_info_get_zone_max_journal_size(zone->origin, &journal_max_size);
6374                     jnl->vtbl->maximum_size_update(jnl, journal_max_size);
6375 
6376                     if(ISOK(ret = journal_append_ixfr_stream(jnl, &bais))) // writes a single page
6377                     {
6378                         log_debug("update: %{dnsname}: wrote %i bytes to the journal", zone->origin, total);
6379 
6380                         bytearray_input_stream_reset(&bais);
6381 
6382                         u32 current_serial = 0;
6383 
6384                         if(secondary_lock != 0)
6385                         {
6386                             zdb_zone_exchange_locks(zone, ZDB_ZONE_MUTEX_SIMPLEREADER, secondary_lock);
6387                         }
6388 
6389                         ret = zdb_icmtl_replay_commit(zone, &bais, &current_serial);
6390 
6391                         if(secondary_lock != 0)
6392                         {
6393                             zdb_zone_exchange_locks(zone, secondary_lock, ZDB_ZONE_MUTEX_SIMPLEREADER);
6394                         }
6395 
6396                         if(ISOK(ret))
6397                         {
6398                             log_debug("update: %{dnsname}: applied journal changes", zone->origin);
6399 
6400                             if(ret_status & (DYNUPDATE_DIFF_RETURN_DNSKEY_ADDED|DYNUPDATE_DIFF_RETURN_DNSKEY_REMOVED))
6401                             {
6402                                 ret_status |= DYNUPDATE_DIFF_RETURN_DNSKEY_UPDATED;
6403                             }
6404                         }
6405                         else
6406                         {
6407                             log_err("update: %{dnsname}: could not apply journal changes: %r", zone->origin, ret);
6408                         }
6409                     }
6410                     else
6411                     {
6412                         if(ret == ZDB_JOURNAL_SERIAL_RANGE_LOCKED)
6413                         {
6414                             log_notice("update: %{dnsname}: could not write %i bytes to the journal as it is full and busy", zone->origin, total);
6415                         }
6416                         else if(ret == ZDB_JOURNAL_MUST_SAFEGUARD_CONTINUITY)
6417                         {
6418                             log_info("update: %{dnsname}: could not write %i bytes to the journal as it is full and the zone needs to be locally stored first", zone->origin, total);
6419                         }
6420                         else
6421                         {
6422                             log_err("update: %{dnsname}: could not write %i bytes to the journal: %r", zone->origin, total, ret);
6423                         }
6424                     }
6425 
6426                     journal_release(jnl);
6427                 }
6428 
6429                 input_stream_close(&bais);
6430                 output_stream_close(&baos);
6431             }
6432         } // storediff succeeded
6433         else
6434         {
6435             if(zdb_zone_error_status_getnot_set(zone, ZDB_ZONE_ERROR_STATUS_DIFF_FAILEDNOUSABLE_KEYS))
6436             {
6437                 log_err("update: %{dnsname}: diff failed: %r", zone->origin, ret);
6438             }
6439         }
6440 
6441         zone_diff_label_rr_vector_clear(&del);
6442         zone_diff_label_rr_vector_clear(&add);
6443 
6444         ptr_vector_destroy(&add);
6445         ptr_vector_destroy(&del);
6446     }
6447 
6448 #if DEBUG
6449     {
6450         zdb_packed_ttlrdata *soa = zdb_record_find(&zone->apex->resource_record_set, TYPE_SOA);
6451         if(soa != NULL)
6452         {
6453             u32 soa_serial = 0;
6454             rr_soa_get_serial(ZDB_PACKEDRECORD_PTR_RDATAPTR(soa), ZDB_PACKEDRECORD_PTR_RDATASIZE(soa), &soa_serial);
6455             log_debug("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) to serial %u",
6456                     zone->origin, zone, reader, count, secondary_lock, dryrun, soa_serial);
6457         }
6458         else
6459         {
6460             log_debug("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) has no SOA anymore",
6461                       zone->origin, zone, reader, count, secondary_lock, dryrun);
6462         }
6463     }
6464 #endif
6465 
6466     log_debug("update: %{dnsname}: done", zone->origin);
6467 
6468     zone_diff_finalize(&diff);
6469 
6470     if(ISOK(ret))
6471     {
6472         ret = ret_status;
6473     }
6474 
6475 #if DEBUG
6476     log_debug("dynupdate_diff(%{dnsname}@%p, %p, %i, %x, %i) returned with %r",
6477                         zone->origin, zone, reader, count, secondary_lock, dryrun, ret);
6478 #endif
6479     zdb_zone_clear_status(zone, ZDB_ZONE_STATUS_IN_DYNUPDATE_DIFF);
6480 
6481     return ret;
6482 }
6483