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; } } } }
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; }
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; }
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); } }
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; } }
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; } }
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 */
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; }