Example #1
0
void
nsec3_icmtl_replay_label_add(nsec3_icmtl_replay *replay, const u8 *fqdn, dnslabel_vector_reference labels, s32 label_top)
{
    zdb_rr_label *rr_label = zdb_rr_label_add(replay->zone, labels, label_top);

    u16 flags = rr_label->flags;

    if((flags & ZDB_RR_LABEL_UNDERDELEGATION) == 0) /** @todo !zdb_rr_label_is_glue(label) */
    {
        /* APEX or NS+DS */

        if( ((flags & ZDB_RR_LABEL_APEX) != 0) ||
            (((flags & ZDB_RR_LABEL_DELEGATION) != 0) && (zdb_record_find(&rr_label->resource_record_set, TYPE_DS) != NULL) ) )
        {
            treeset_node *node = treeset_avl_insert(&replay->nsec3_labels, (u8*)fqdn);

            if(node->data == NULL)
            {
#ifndef NDEBUG
                log_debug("journal: NSEC3: queue: %{dnsname} for NSEC3 update", fqdn);
#endif
                node->key = dnsname_dup(fqdn);
                node->data = rr_label;
            }
        }
    }
}
Example #2
0
void
nsec_icmtl_replay_nsec_add(nsec_icmtl_replay *replay, const u8* fqdn)
{
    treeset_node *node = treeset_avl_insert(&replay->nsec_add, (u8*)fqdn);

    node->key = dnsname_dup(fqdn);
    node->data = NULL;
}
Example #3
0
static void
string_set_insert(const char *name, u32 value)
{
    string_node *node;
    u8 fqdn[MAX_DOMAIN_LENGTH];

    cstr_to_dnsname(fqdn, name);

    node = string_set_avl_insert(&hmac_algorithms, (char*)dnsname_dup(fqdn));
    node->value = value;
}
Example #4
0
void nsec3_icmtl_replay_nsec3_add(nsec3_icmtl_replay *replay, const u8* fqdn, const zdb_ttlrdata *ttlrdata)
{
    assert(ttlrdata->next == NULL);
    
    treeset_node *node = treeset_avl_insert(&replay->nsec3_add, (u8*)fqdn);
    if(node->data == NULL)
    {
        node->key = dnsname_dup(fqdn);
        node->data = zdb_ttlrdata_clone(ttlrdata);
    }
    else
    {
        zdb_ttlrdata_delete(node->data);
        node->data = zdb_ttlrdata_clone(ttlrdata);
    }
}
Example #5
0
void nsec3_icmtl_replay_nsec3_rrsig_del(nsec3_icmtl_replay *replay, const u8* fqdn, const zdb_ttlrdata *ttlrdata)
{
    assert(ttlrdata->next == NULL);

    treeset_node *node = treeset_avl_insert(&replay->nsec3rrsig_del, (u8*)fqdn);
    if(node->data == NULL)
    {
        node->key = dnsname_dup(fqdn);
        node->data = zdb_ttlrdata_clone(ttlrdata);
    }
    else
    {
        //zdb_ttlrdata_delete(node->data);
        zdb_ttlrdata *newone = zdb_ttlrdata_clone(ttlrdata);
        newone->next = (zdb_ttlrdata*)node->data;
        node->data = newone;
    }
}
Example #6
0
void nsec3_icmtl_replay_nsec3_rrsig_add(nsec3_icmtl_replay *replay, const u8* fqdn, zdb_packed_ttlrdata *packed_ttlrdata)
{
    treeset_node *node = treeset_avl_insert(&replay->nsec3rrsig_add, (u8*)fqdn);
    
    if(node->data == NULL)
    {
        node->key = dnsname_dup(fqdn);
        node->data = packed_ttlrdata;
    }
    else
    {
        //zdb_ttlrdata_delete(node->data);

        zdb_packed_ttlrdata **nsec3_rrsig_ptr = (zdb_packed_ttlrdata **)&node->data;
        zdb_packed_ttlrdata *nsec3_rrsig = (zdb_packed_ttlrdata *)node->data;

        while(nsec3_rrsig != NULL)
        {
            if((nsec3_rrsig->rdata_size == packed_ttlrdata->rdata_size) && (RRSIG_KEY_NATIVETAG(nsec3_rrsig) == RRSIG_KEY_NATIVETAG(packed_ttlrdata)))
            {
                /* got a match ? */
                
                if(memcmp(&nsec3_rrsig->rdata_start[0], &packed_ttlrdata->rdata_start[0], nsec3_rrsig->rdata_size) == 0)
                {
                    /* got a match ! delete the previous one (?) */
                    packed_ttlrdata->next = nsec3_rrsig->next;
                    ZDB_RECORD_ZFREE(nsec3_rrsig);
                    break;
                }
            }
            
            nsec3_rrsig_ptr = &nsec3_rrsig->next;
            nsec3_rrsig = nsec3_rrsig->next;
        }
        
        *nsec3_rrsig_ptr = packed_ttlrdata;
    }
}
Example #7
0
static ya_result
xfr_query_callback(void* data)
{
    xfr_query_schedule_param *xqsp = (xfr_query_schedule_param*)data;
    ya_result return_value;
	ya_result scheduler_status;
    u32 refreshed_time = 0;
    u32 retried_time = time(NULL);
    
    /*
     * Zone refresh will be enabled (again) here.
     * 
     * xfr_query_mark_zone_loaded
     * 
     */

    
    if(ISOK(xqsp->return_value))
    {
        /* success but type == 0 => transfer done */
        
        if(xqsp->type != 0)
        {
            log_info("slave: %{dnstype}: proceeding with transfer of %{dnsname}", &xqsp->type, xqsp->origin);
        }
    }
    else
    {
        log_err("slave: %{dnstype}: transfer of %{dnsname} failed: %r", &xqsp->type, xqsp->origin, xqsp->return_value);
        
        /*
         * Here (?) put the invalid zone placeholder if it is not there yet
         */
    }

    switch(xqsp->type)
    {
        case TYPE_AXFR:
        {
            /*
             * Load the axfr
             */
            
            if(ISOK(xqsp->return_value))
            {
                axfr_query_axfr_load_param *aqalp;
                MALLOC_OR_DIE(axfr_query_axfr_load_param*, aqalp, sizeof(axfr_query_axfr_load_param), GENERIC_TAG);
                
                log_info("slave: opening AXFR for %{dnsname} %d", xqsp->origin, xqsp->loaded_serial);

                if(ISOK(return_value = zone_axfr_reader_open_with_serial(g_config->xfr_path, xqsp->origin, xqsp->loaded_serial, &aqalp->zr)))
                {
                    /*
                     * The system is ready to load an AXFR
                     * A zone is already in place (by design), there may be an old zone that is now irrelevant.
                     * At this point ...
                     * 
                     * If the serial are not the same
                     *      MT the old zone must be destroyed (if it exist)
                     *       then
                     *      MT The new zone has to be created (loaded from the AXFR file)
                     * Else
                     *      ST The new zone is actually the old zone, and the old zone is non-existent
                     * EndIf
                     * 
                     * then
                     * 
                     * ST The new zone and the placeholder zone have to be swapped, then the placeholder has to be destroyed
                     * 
                     */
                    
                    zdb_zone *zone = zdb_zone_find_from_dnsname((zdb*)xqsp->db, xqsp->origin, CLASS_IN);

                    if(zone != NULL)
                    {
                        zdb_zone_truncate_invalidate(zone);
                    }                    

                    /**
                     * schedule the axfr load
                     */

                    aqalp->old_zone = NULL; /* old zone */
                    aqalp->new_zone = NULL;
                    aqalp->db = xqsp->db;
                    aqalp->origin = dnsname_dup(xqsp->origin);

                    scheduler_status = SCHEDULER_TASK_PROGRESS;

                    thread_pool_schedule_job(xfr_query_axfr_load_thread, aqalp, NULL, "axfr load");
                    
                    break;
                }
            }
            
            /**
             * @todo If the "old_zone" exists, then ... (destroy ? swap back ? expired ?)
             * 
             */
            
            /*
             * An issue occurred (AXFR transfer, load)
             */
			
            log_err("slave: unable to load the axfr (retry set in %d seconds)", g_config->axfr_retry_delay);

            alarm_event_node *event = alarm_event_alloc();

            event->epoch = time(NULL) + g_config->axfr_retry_delay;

            if(g_config->axfr_retry_jitter > 0)
            {
                random_ctx rndctx = thread_pool_get_random_ctx();
                u32 jitter = random_next(rndctx) % g_config->axfr_retry_jitter;
                event->epoch += jitter;
            }

            event->function = scheduler_axfr_query_alarm;
            event->args = xqsp;
            event->key = ALARM_KEY_ZONE_AXFR_QUERY;
            event->flags = ALARM_DUP_NOP;
            event->text = "scheduler_axfr_query_alarm";

            zdb *db = (zdb*)xqsp->db;
            alarm_set(db->alarm_handle, event);

            /* DO NOT FREE xqsp, SO DO NOT BREAK : return now */
            
            return SCHEDULER_TASK_FINISHED;
        }
        
        case TYPE_IXFR:
        {
            /**
             * Load the ixfr (single thread, zone locked)
             */

			scheduler_status = SCHEDULER_TASK_FINISHED;

            if(ISOK(xqsp->return_value))
            {
                zdb_zone *dbzone = zdb_zone_find_from_dnsname((zdb*)xqsp->db, xqsp->origin, CLASS_IN);

                if(dbzone == NULL)
                {
                    log_err("slave: zone %{dnsname} journal has vanished", xqsp->origin);
                    break;
                }
                
                log_info("slave: opening journal for '%{dnsname}'", xqsp->origin);

                zdb_zone_lock(dbzone, ZDB_ZONE_MUTEX_XFR);
                
                if(ISOK(return_value = zdb_icmtl_replay(dbzone, g_config->xfr_path, xqsp->serial_start_offset, xqsp->loaded_serial, ZDB_ICMTL_REPLAY_SERIAL_OFFSET|ZDB_ICMTL_REPLAY_SERIAL_LIMIT)))
                {
                    log_info("slave: replayed %d records changes", return_value);
                    
                    dbzone->apex->flags &= ~ZDB_RR_LABEL_INVALID_ZONE;
                    
                    refreshed_time = time(NULL);
                }
                else
                {
                    log_err("slave: replay error: %r this is bad: invalidate the zone and ask for an AXFR", return_value);
                    
                    /** @todo INVALIDATE AND AXFR */
                }
                
                /** @todo invalitate if retried_time > expired_time */

                zdb_zone_unlock(dbzone, ZDB_ZONE_MUTEX_XFR);
            }
            
            log_info("slave: journal transfer done");

            xfr_query_mark_zone_loaded((zdb*)g_config->database, xqsp->origin, refreshed_time, retried_time);
            
			scheduler_status = SCHEDULER_TASK_FINISHED;
            break;            
        }
        default:
        {
            refreshed_time = time(NULL);
            
            log_info("slave: transfer done");

            xfr_query_mark_zone_loaded((zdb*)g_config->database, xqsp->origin, refreshed_time, retried_time);
            
			scheduler_status = SCHEDULER_TASK_FINISHED;
            break;
        }
    }   /* switch return_value */
Example #8
0
ya_result
zdb_icmtl_replay_commit(zdb_zone *zone, input_stream *is, u32 *current_serialp)
{
    ya_result ret;
    /*
     * 0: DELETE, 1: ADD
     * The mode is switched every time an SOA is found.
     */

    yassert(zdb_zone_islocked(zone));
    
#if ZDB_HAS_NSEC3_SUPPORT
    bool has_nsec3 = zdb_zone_is_nsec3(zone);
#endif
#if ZDB_HAS_NSEC_SUPPORT
    bool has_nsec = zdb_zone_is_nsec(zone);
#endif
    
#if ZDB_HAS_NSEC3_SUPPORT && ZDB_HAS_NSEC_SUPPORT
    if(has_nsec3 && has_nsec)
    {
        log_err("journal: %{dnsname}: zone has both NSEC and NSEC3 status, which is not supported by YADIFA", zone->origin);
        return ERROR;
    }
#endif
    
    u8 mode = 1; // the first SOA will switch the mode to delete
    s32 changes = 0;
    
    zdb_ttlrdata ttlrdata;
    dns_resource_record rr;
    dns_resource_record_init(&rr);
    const u8 *fqdn = rr.name;
    dnslabel_vector labels;

    ttlrdata.next = NULL;
    


    /*
     * The plan for NSEC3 :
     * Store the fqdn + type class ttl rdata in collections
     * => the delete collection
     * => the add collection
     * Then there is the NSEC3 covered labels: keep a reference to them for later
     *
     * When a pass of SOA-/SOA+ has finished:
     * _ replace the NSEC3 in both collections (reading from delete)
     * _ delete NSEC3 to delete
     * _ add NSEC3 to add
     *
     * _ and finally update the NSEC3 for the labels kept above
     */

#if ZDB_HAS_NSEC3_SUPPORT
    chain_replay nsec3replay;
    nsec3_chain_replay_init(&nsec3replay, zone);
#endif
    
#if ZDB_HAS_NSEC_SUPPORT
    chain_replay nsecreplay;
    nsec_chain_replay_init(&nsecreplay, zone);
#endif
    
#if ZDB_HAS_NSEC3_SUPPORT
    ptr_set downed_fqdn = PTR_SET_DNSNAME_EMPTY;
#endif

    /* 
     * At this point : the next record, if it exists AND is not an SOA , has to be deleted
     * 
     */
    
    bool did_remove_soa = FALSE;

    // something has to be committed

    for(;;)
    {
        /*
         * read the full record
         * 
         * == 0 : no record (EOF)
         *  < 0 : failed
         */

        if((ret = dns_resource_record_read(&rr, is)) <= 0)
        {
            if(ISOK(ret))
            {
                log_info("journal: %{dnsname}: reached the end of the journal page", zone->origin);
            }
            else
            {
                log_err("journal: %{dnsname}: broken journal: %r", zone->origin, ret);
                logger_flush(); // broken journal (bad, keep me)
            }

            break;
        }
        
        ttlrdata.ttl = ntohl(rr.tctr.ttl);
        ttlrdata.rdata_pointer = rr.rdata;
        ttlrdata.rdata_size = rr.rdata_size;

        /*
         * Stop at the SOA
         */

        if(rr.tctr.qtype == TYPE_SOA)
        {
            mode ^= 1;

            if(mode == 0)
            {
                /* ADD */

#if ZDB_HAS_NSEC3_SUPPORT
                // NSEC3
                {
                    ret = nsec3replay.vtbl->execute(&nsec3replay);

                    if(FAIL(ret))
                    {
                        dns_resource_record_clear(&rr);
                        // DO NOT: input_stream_close(is);

                        nsec3replay.vtbl->finalise(&nsec3replay);
                        
#if ZDB_HAS_NSEC_SUPPORT
                        nsecreplay.vtbl->finalise(&nsecreplay);
#endif // ZDB_HAS_NSEC_SUPPORT
                        return ret;
                    }
                }
#endif
                
#if ZDB_HAS_NSEC_SUPPORT
                // NSEC
                ret = nsecreplay.vtbl->execute(&nsecreplay);
#endif //ZDB_HAS_NSEC_SUPPORT
            }
        }

        if(!did_remove_soa)
        {
            log_info("journal: %{dnsname}: removing obsolete SOA", zone->origin);

            if(FAIL(ret = zdb_record_delete(&zone->apex->resource_record_set, TYPE_SOA)))
            {
                /**
                * complain
                */

                log_err("journal: %{dnsname}: removing current SOA gave an error: %r", zone->origin, ret);

                /* That's VERY bad ... */

                changes = ret;

                break;
            }

            did_remove_soa = TRUE;
        }

        s32 top = dnsname_to_dnslabel_vector(fqdn, labels);

        if(mode == 0)
        {
            /*
             * "TO DEL" record
             */

#if ICMTL_DUMP_JOURNAL_RECORDS
            rdata_desc type_len_rdata = {rr.tctr.qtype, rr.rdata_size, rr.rdata };
            log_debug("journal: del %{dnsname} %{typerdatadesc}", fqdn, &type_len_rdata);
            logger_flush();
#endif
            bool added_in_chain = FALSE;

#if ZDB_HAS_NSEC3_SUPPORT
            
            // 0 : proceed
            // 1 : ignore
            // ? : error
            
            if((added_in_chain = (nsec3replay.vtbl->record_del(&nsec3replay, fqdn, rr.tctr.qtype, &ttlrdata) != 0)))
            {
                // add everything up until a non-empty terminal is found (the apex will thus be automatically avoided)
                // if the record is a delegation, add everything down too
                /*
                if((top > zone->origin_vector.size) &&
                        (rr.tctr.qtype != TYPE_NSEC3) &&
                        (   (rr.tctr.qtype != TYPE_RRSIG) ||
                            ((rr.tctr.qtype == TYPE_RRSIG) && (GET_U16_AT_P(ZDB_RECORD_PTR_RDATAPTR(&ttlrdata)) != TYPE_NSEC3))
                        )
                    )
                {
                    
                }
                */
                ++changes;
            }
            else
            {
                if(top > zone->origin_vector.size)
                {
                    const u8 *above_fqdn = fqdn;
                    for(int i = 1; i < top - zone->origin_vector.size; ++i)
                    {
                        zdb_rr_label *above = zdb_rr_label_find_exact(zone->apex, &labels[i], top - zone->origin_vector.size - 1 - i);
                        if(above != NULL)
                        {
                            if(btree_notempty(above->resource_record_set))
                            {
                                break;
                            }
                        }

                        above_fqdn += above_fqdn[0] + 1;
                        nsec3replay.vtbl->record_del(&nsec3replay, above_fqdn, TYPE_NONE, NULL);
                    }

                    zdb_rr_label *rr_label = zdb_rr_label_find_exact(zone->apex, labels, (top - zone->origin_vector.size) - 1);
                    if(rr_label != NULL)
                    {
                        zdb_rr_label_forall_children_of_fqdn(rr_label, fqdn, zdb_icmtl_replay_commit_label_forall_nsec3_del_cb, &nsec3replay);
                    }
                }
            }
#endif
#if ZDB_HAS_NSEC_SUPPORT
            
            // 0 : proceed
            // 1 : ignore
            // ? : error
            
            if(!added_in_chain && (added_in_chain = nsecreplay.vtbl->record_del(&nsecreplay, fqdn, rr.tctr.qtype, &ttlrdata) != 0))
            {
                ++changes;
            }
            //else
#endif
            if(!added_in_chain)
            switch(rr.tctr.qtype)
            {
                case TYPE_SOA:
                {
                    rdata_desc rdata = {TYPE_SOA, ttlrdata.rdata_size, ttlrdata.rdata_pointer};
                    log_info("journal: %{dnsname}: SOA: del %{dnsname} %{typerdatadesc}", zone->origin, fqdn, &rdata);

                    s32 m1 = (top - zone->origin_vector.size) - 1;

                    if(m1 == -1)
                    {
                        if(FAIL(ret = zdb_record_delete_exact(&zone->apex->resource_record_set, TYPE_SOA, &ttlrdata))) /* FB done, APEX : no delegation, source is the journal */
                        {
                            if(!did_remove_soa)
                            {
                                log_err("journal: %{dnsname}: SOA: %r", zone->origin, ret);
                            }
                        }
                    }
                    else
                    {
                        if(FAIL(ret = zdb_rr_label_delete_record_exact(zone, labels, (top - zone->origin_vector.size) - 1, rr.tctr.qtype, &ttlrdata))) // source is journal
                        {
                            if(!did_remove_soa)
                            {
                                log_err("journal: %{dnsname}: SOA: (2) %r", zone->origin, ret);
                            }
                        }
                    }
                    break;
                }

                default:
                {
#if ZDB_HAS_NSEC3_SUPPORT
                    // NSEC3
                    if(rr.tctr.qtype != TYPE_NSEC3)
                    {
                        if((rr.tctr.qtype != TYPE_RRSIG) && (rrsig_get_type_covered_from_rdata(rr.rdata, rr.rdata_size) != TYPE_NSEC3))
                        {
                            if(ptr_set_avl_find(&downed_fqdn, fqdn) == NULL)
                            {
                                ptr_set_avl_insert(&downed_fqdn, dnsname_dup(fqdn));
                            }
                        }
                    }
                    else
                    {
                        if(!NSEC3_RDATA_IS_OPTOUT(rr.rdata))
                        {
                            zone->_flags &= ~ZDB_ZONE_HAS_OPTOUT_COVERAGE;
                        }
                    }
#endif
                    if(FAIL(ret = zdb_rr_label_delete_record_exact(zone, labels, (top - zone->origin_vector.size) - 1, rr.tctr.qtype, &ttlrdata))) // source is journal
                    {
                        // signatures can be removed automatically by maintenance
                        
                        if((rr.tctr.qtype != TYPE_RRSIG) && (ret != ZDB_ERROR_KEY_NOTFOUND))
                        {
                            log_err("journal: %{dnsname}: del %{dnsrr}", zone->origin, &rr);
                            log_err("journal: %{dnsname}: %{dnstype}: %r", zone->origin, &rr.tctr.qtype, ret);
                        }
                        else
                        {
                            log_debug("journal: %{dnsname}: del %{dnsrr}", zone->origin, &rr);
                            log_debug("journal: %{dnsname}: %{dnstype}: %r", zone->origin, &rr.tctr.qtype, ret);
                        }
                    }
                }
            }
        }
        else
        {
            /*
             * "TO ADD" record
             */
            
            bool added_in_chain = FALSE;
            
#if ZDB_HAS_NSEC3_SUPPORT

            // returns the number of changes taken into account (0 or 1)
            // 0 : proceed
            // 1 : ignore
            // ? : error
            
            if((added_in_chain = (nsec3replay.vtbl->record_add(&nsec3replay, fqdn, rr.tctr.qtype, &ttlrdata) != 0)))
            {
                ++changes;
            }
            else
            {
                if( (top > zone->origin_vector.size) &&
                    (rr.tctr.qtype != TYPE_NSEC3) &&
                    (   (rr.tctr.qtype != TYPE_RRSIG) ||
                        ((rr.tctr.qtype == TYPE_RRSIG) && (GET_U16_AT_P(ZDB_RECORD_PTR_RDATAPTR(&ttlrdata)) != TYPE_NSEC3))
                        )
                    )
                {
                    const u8 *above_fqdn = fqdn;
                    for(int i = 1; i < top - zone->origin_vector.size; ++i)
                    {
                        zdb_rr_label *above = zdb_rr_label_find_exact(zone->apex, &labels[i], top - zone->origin_vector.size - 1 - i);
                        if(above != NULL)
                        {
                            if(btree_notempty(above->resource_record_set))
                            {
                                break;
                            }
                        }

                        above_fqdn += above_fqdn[0] + 1;
                        nsec3replay.vtbl->record_add(&nsec3replay, above_fqdn, TYPE_NONE, NULL);
                    }

                    zdb_rr_label *rr_label = zdb_rr_label_find_exact(zone->apex, labels, (top - zone->origin_vector.size) - 1);
                    if(rr_label != NULL)
                    {
                        zdb_rr_label_forall_children_of_fqdn(rr_label, fqdn, zdb_icmtl_replay_commit_label_forall_nsec3_add_cb, &nsec3replay);
                    }
                }
            }
#endif
#if ZDB_HAS_NSEC_SUPPORT
            
            // returns the number of changes taken into account (0 or 1)
            // 0 : proceed
            // 1 : ignore
            // ? : error
            
            if(!added_in_chain && (added_in_chain = (nsecreplay.vtbl->record_add(&nsecreplay, fqdn, rr.tctr.qtype, &ttlrdata) != 0)))
            {
                ++changes;
            }
            //else
#endif
            if(!added_in_chain)
            switch(rr.tctr.qtype)
            {
#if ZDB_HAS_NSEC3_SUPPORT
                case TYPE_NSEC3CHAINSTATE:
                {
                    // create chain if missing ...
                    
                    nsec3_zone_add_from_rdata(zone, rr.rdata_size, rr.rdata);
                    
                    // add the record
                    
                    zdb_packed_ttlrdata *packed_ttlrdata;

                    ZDB_RECORD_ZALLOC_EMPTY(packed_ttlrdata, ttlrdata.ttl, rr.rdata_size);
                    packed_ttlrdata->next = NULL;
                    MEMCOPY(ZDB_PACKEDRECORD_PTR_RDATAPTR(packed_ttlrdata), rr.rdata, rr.rdata_size);
                    zdb_zone_record_add(zone, labels, top, rr.tctr.qtype, packed_ttlrdata); // class is implicit, flow verified
                    
                    break;
                }
#endif // ZDB_HAS_NSEC3_SUPPORT
#
                default:
                {
                    zdb_packed_ttlrdata *packed_ttlrdata;

                    ZDB_RECORD_ZALLOC_EMPTY(packed_ttlrdata, ttlrdata.ttl, rr.rdata_size);
                    packed_ttlrdata->next = NULL;
                    MEMCOPY(ZDB_PACKEDRECORD_PTR_RDATAPTR(packed_ttlrdata), rr.rdata, rr.rdata_size);

#if ICMTL_DUMP_JOURNAL_RECORDS
                    rdata_desc type_len_rdata = {rr.tctr.qtype, rr.rdata_size, ZDB_PACKEDRECORD_PTR_RDATAPTR(packed_ttlrdata) };
                    log_debug("journal: add %{dnsname} %{typerdatadesc}", fqdn, &type_len_rdata);
                    logger_flush();
#endif
                    if(rr.tctr.qtype == TYPE_SOA)
                    {
                        rr_soa_get_serial(ZDB_PACKEDRECORD_PTR_RDATAPTR(packed_ttlrdata), ZDB_PACKEDRECORD_PTR_RDATASIZE(packed_ttlrdata), current_serialp);
                        rdata_desc rdata = {TYPE_SOA, ZDB_PACKEDRECORD_PTR_RDATASIZE(packed_ttlrdata), ZDB_PACKEDRECORD_PTR_RDATAPTR(packed_ttlrdata)};
                        log_info("journal: %{dnsname}: SOA: add %{dnsname} %{typerdatadesc}", zone->origin, fqdn, &rdata);
                    }
                    
                    zdb_zone_record_add(zone, labels, top, rr.tctr.qtype, packed_ttlrdata); // class is implicit, flow verified
                }
            }            
        } // end if ADD

        changes++;
    }

    /*
     * Yes, I know.  If 2^32 changes (add batch + del batch) occurs then it will be seen as an error ...
     */

    if(ISOK(changes))
    {
#if ZDB_HAS_NSEC3_SUPPORT && ZDB_HAS_NSEC_SUPPORT
        
        if(has_nsec3 && has_nsec)
        {
            log_warn("journal: %{dnsname}: both NSEC3 and NSEC operations happened, which is not supported by YADIFA. Keeping the original one.", zone->origin);
            
            has_nsec3 = zdb_zone_is_nsec3(zone);
            has_nsec = zdb_zone_is_nsec(zone);
        }        
#endif
        
#if ZDB_HAS_NSEC3_SUPPORT
        nsec3replay.vtbl->execute(&nsec3replay);
#endif

#if ZDB_HAS_NSEC_SUPPORT
        nsecreplay.vtbl->execute(&nsecreplay);
#endif
    }
    
#if ZDB_HAS_NSEC3_SUPPORT
    // has_nsec3 = zdb_zone_is_nsec3(zone);
    nsec3replay.vtbl->finalise(&nsec3replay);
#endif
#if ZDB_HAS_NSEC_SUPPORT
    // has_nsec = zdb_zone_is_nsec(zone);
    nsecreplay.vtbl->finalise(&nsecreplay);
#endif
    
    dns_resource_record_clear(&rr);


    
    return changes;
}