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; }
static ya_result journal_ix_append_ixfr_stream(journal *jh, input_stream *ixfr_wire_is) { journal_ix *jix = (journal_ix*)jh; journal_ix_writelock(jix); /* * Move at the end of the file * Check that the wire starts with the last soa/serial * Append the wire * update the last serial */ // read the record ya_result return_value; dns_resource_record rr; dns_resource_record_init(&rr); if((return_value = dns_resource_record_read(&rr, ixfr_wire_is)) <= 0) { /* FAIL or EOF */ dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to read record: %r", return_value); return return_value; } /* * The first record is an SOA and our starting point (to be deleted) */ #ifdef DEBUG rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: DEL %{dnsname} %{typerdatadesc}", rr.name, &rdatadesc); #endif if(rr.tctr.qtype != TYPE_SOA) { u16 rtype = rr.tctr.qtype; dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: expected SOA record but got %{dnstype} instead", &rtype); return ZDB_JOURNAL_SOA_RECORD_EXPECTED; } /* * check the journal file exists/is defined * do it now if not * proceed */ if(((jix->first_serial == 0) && (jix->last_serial == 0)) || (jix->fd == -1)) { /* the file does not exists yet */ if(FAIL(return_value = rr_soa_get_serial(rr.rdata, rr.rdata_size, &jix->first_serial))) { dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to read record: %r", return_value); return return_value; } int fd = open_create_ex(jix->journal_name, O_RDWR|O_CREAT, 0644); if(fd < 0) { return_value = ERRNO_ERROR; dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to open journal file '%s': %r", jix->journal_name, return_value); return return_value; } log_info("journal: ix: journal file created '%s'", jix->journal_name); jix->fd = fd; } if(FAIL(return_value = journal_ix_ensure_opened(jix))) { return return_value; } u64 valid_offset = lseek(jix->fd, 0, SEEK_END); u64 current_offset = valid_offset; u32 valid_serial = jix->last_serial; u32 potential_serial = valid_serial; s64 valid_page_offset = jix->last_page_offset; s64 potential_page_offset = current_offset; #ifdef DEBUG log_debug("journal: ix: ready to append to journal after serial %08x (%d) at offset %lld", valid_serial, valid_serial, valid_offset); #endif u8 mode = 0; /* 0: del, 1: add */ output_stream fos; output_stream bos; fd_output_stream_attach(&fos, jix->fd); buffer_output_stream_init(&bos, &fos, 512); for(;;) { /* write the first */ if(FAIL(return_value = dns_resource_record_write(&rr, &bos))) { /* this is VERY bad */ log_err("journal: ix: error writing a record to the journal: %r", return_value); break; } /* update the current offset */ current_offset += return_value; if((return_value = dns_resource_record_read(&rr, ixfr_wire_is)) <= 0) /* no bytes read OR error, there is no macro for this */ { /* error or end of stream */ if(return_value == 0) /* end of stream */ { if(mode != 0) /* on add mode so everything should be fine */ { valid_offset = current_offset; valid_serial = potential_serial; valid_page_offset = potential_page_offset; } else /* but on delete mode instead of add mode */ { log_err("journal: ix: ixfr stream unexpected eof"); return_value = UNEXPECTED_EOF; /* we have an error */ } } break; } if(rr.tctr.qtype == TYPE_SOA) { mode ^= 1; #ifdef DEBUG rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: %s %{dnsname} %{typerdatadesc}", (mode!=0)?"add":"del", rr.name, &rdatadesc); #endif if(mode == 0) { /* * new SOA to delete * * it's a new "page" (delete -> add) * * the offset before we write this record is the highest valid one in the file * so the error correcting truncation will be made at that offset */ valid_offset = current_offset; /* * the serial number that has been added with the previous page */ valid_serial = potential_serial; /* * the offset of the previous page */ valid_page_offset = potential_page_offset; /* * the new page starts here : update */ potential_page_offset = current_offset; } else { /* * new SOA add * * this is the second half of the page, we know what serial it is about */ if(FAIL(return_value = rr_soa_get_serial(rr.rdata, rr.rdata_size, &potential_serial))) { break; } } } #ifdef DEBUG else { rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: %s %{dnsname} %{typerdatadesc}", (mode!=0)?"add":"del", rr.name, &rdatadesc); } #endif } if(FAIL(return_value)) { /* * The journal is only valid up to valid_offset with serial ... */ log_err("journal: ix: rewinding journal up to last valid point (%lld)", valid_offset); ftruncate(jix->fd, valid_offset); } #ifdef DEBUG log_debug("journal: ix: page offset got from %d to %d", jix->last_page_offset, valid_page_offset); log_debug("journal: ix: serial got from %d to %d", jix->last_serial, valid_serial); #endif jix->last_page_offset = valid_page_offset; jix->last_serial = valid_serial; /* * rename the file */ if(ISOK(return_value)) { char new_name[PATH_MAX]; memcpy(new_name, jix->journal_name, jix->journal_name_len); snformat(&new_name[jix->journal_name_len - FIRST_FROM_END], 8 + 1 + 8 + 1 + IX_EXT_STRLEN + 1, "%08x-%08x." IX_EXT , jix->first_serial, jix->last_serial); if(rename(jix->journal_name, new_name) >= 0) { memcpy(jix->journal_name, new_name, jix->journal_name_len); } } /* */ #ifdef DEBUG log_debug("journal: ix: fd=%i from=%08x to=%08x soa@%lld file=%s", jix->fd, jix->first_serial, jix->last_serial, jix->last_page_offset, (jix->journal_name!=NULL)?jix->journal_name:"NONE-YET"); #endif output_stream_flush(&bos); fd_output_stream_detach(buffer_output_stream_get_filtered(&bos)); output_stream_close(&bos); dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); if(ISOK(return_value)) { #ifdef DEBUG log_debug("journal: ix: page added (fd=%i from=%08x to=%08x soa@%lld file=%s): %r", jix->fd, jix->first_serial, jix->last_serial, jix->last_page_offset, (jix->journal_name!=NULL)?jix->journal_name:"NONE-YET", return_value); #endif return TYPE_IXFR; /* that's what the caller expects to handle the new journal pages */ } else { log_err("journal: ix: failed to add page"); return return_value; } }
ya_result zdb_zone_update_ixfr(zdb* db, input_stream* is) { u8 rname[MAX_DOMAIN_LENGTH]; u16 rtype; u16 rclass; u32 rttl; u16 rdata_size; u8* rdata; zdb_packed_ttlrdata* soa_ttlrdata; zdb_packed_ttlrdata* tmp_ttlrdata; zdb_packed_ttlrdata* ttlrdata; dnsname_vector name; dnsname_vector entry_name; ya_result err; /* Get the first SOA */ if(FAIL(err = input_stream_read_rr_header(is, rname, sizeof (rname), &rtype, &rclass, &rttl, &rdata_size))) { return err; } if(rtype != TYPE_SOA) { return ZDB_ERROR_GENERAL; } DEBUG_RESET_dnsname(name); dnsname_to_dnsname_vector(rname, &name); ZDB_RECORD_ZALLOC_EMPTY(soa_ttlrdata, rttl, rdata_size); if(FAIL(err = input_stream_read_fully(is, ZDB_PACKEDRECORD_PTR_RDATAPTR(soa_ttlrdata), rdata_size))) { return err; } #ifndef NDEBUG #if DUMP_ICMTL_UPDATE != 0 if(err != 0) { format("zdb_zone_update_ixfr H: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype); print_rdata(rtype, ZDB_PACKEDRECORD_PTR_RDATAPTR(soa_ttlrdata), rdata_size); println(""); } #endif #endif u32 serial_first; if(FAIL(err = rr_soa_get_serial(soa_ttlrdata->rdata_start, soa_ttlrdata->rdata_size, &serial_first))) { return err; } zdb_zone_label* zone_label = zdb_zone_label_find(db, &name, rclass); if((zone_label == NULL) || (zone_label->zone == NULL)) { /* Not loaded */ return ZDB_ERROR_GENERAL; } zdb_zone* zone = zone_label->zone; u32 serial_current; if(FAIL(err = zdb_zone_getserial(zone, &serial_current))) { return err; } #if ZDB_NSEC3_SUPPORT != 0 nsec3_load_context nsec3_context; nsec3_load_init(&nsec3_context, zone); #endif MALLOC_OR_DIE(zdb_packed_ttlrdata*, tmp_ttlrdata, sizeof (zdb_ttlrdata) + RDATA_MAX_LENGTH, ZDB_RDATABUF_TAG); /* We do not need tmp_ttlrdata and rdata at the same time, let's spare memory */ rdata = ZDB_PACKEDRECORD_PTR_RDATAPTR(tmp_ttlrdata); /* Read the next SOA (sub or end) */ if(FAIL(err = input_stream_read_rr_header(is, rname, sizeof (rname), &rtype, &rclass, &rttl, &rdata_size))) { return err; } if(rtype != TYPE_SOA) { return ZDB_ERROR_GENERAL; } for(;;) { if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size))) { break; } #ifndef NDEBUG #if DUMP_ICMTL_UPDATE != 0 if(err != 0) { format("zdb_zone_update_ixfr F: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype); print_rdata(rtype, rdata, rdata_size); println(""); } #endif #endif /* The SOA serial is supposed to match our current one or the first one * * If it's the first one, then the task is done * * If it's the current one, we are moving forward * * If it's something else, there is an error * */ u32 serial_from; if(FAIL(err = rr_soa_get_serial(rdata, rdata_size, &serial_from))) { break; } /* Check the serial */ if(serial_from != serial_current) { if(serial_from == serial_first) { /* IXFR done */ err = SUCCESS; break; } /* Serial sequence is not right */ err = ZDB_ERROR_GENERAL; break; } tmp_ttlrdata->ttl = rttl; tmp_ttlrdata->rdata_size = rdata_size; zdb_zone_record_delete(zone, NULL, -1, TYPE_SOA, tmp_ttlrdata); for(;;) { /* Load the next record without the data (sub) */ if(FAIL(err = input_stream_read_rr_header(is, rname, sizeof (rname), &rtype, &rclass, &rttl, &rdata_size))) { break; } /* If we got an SOA, it's the one that starts the "add" sequence */ if(rtype == TYPE_SOA) { break; } /* Load the data */ if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size))) { break; } #ifndef NDEBUG #if DUMP_ICMTL_UPDATE != 0 if(err != 0) { format("zdb_zone_update_ixfr R: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype); print_rdata(rtype, rdata, rdata_size); println(""); } #endif #endif tmp_ttlrdata->ttl = rttl; tmp_ttlrdata->rdata_size = rdata_size; /* The vector is not always required, but it's so much easier to * read putting it here */ DEBUG_RESET_dnsname(entry_name); dnsname_to_dnsname_vector(rname, &entry_name); #if ZDB_NSEC3_SUPPORT != 0 if(rtype == TYPE_NSEC3PARAM) { /* Remove it from the zone */ zdb_zone_record_delete(zone, name.labels, (entry_name.size - name.size) - 1, rtype, tmp_ttlrdata); /* Destroy the whole NSEC3 collection associated to the NSEC3PARAM */ nsec3_remove_nsec3param_by_record(zone, tmp_ttlrdata); continue; } if(rtype == TYPE_NSEC3) { /* Remove the NSEC3 label (and its signature) * * The previous record will have its signature changed, no doubt. * But I cannot edit the previous one about this. * Since we are in an IXFR, the previous NSEC3 is supposed to be * removed too, until one of the previous is also added in the * next section (soa add) * * zdb_zone_record_delete is not the right call, it's * * nsec3_... * */ nsec3_remove_nsec3(zone, tmp_ttlrdata); continue; } if(rtype == TYPE_RRSIG) { if((GET_U16_AT(*rdata)) == TYPE_NSEC3) /** @note : NATIVETYPE */ { /* Remove the RRSIG from the NSEC3 label * * zdb_zone_record_delete is not the right call, it's * * nsec3_... * */ nsec3_remove_rrsig(zone, tmp_ttlrdata); continue; } } #endif /* Remove from the zone */ zdb_zone_record_delete(zone, name.labels, (entry_name.size - name.size) - 1, rtype, tmp_ttlrdata); } /* Remove records */ /* The current header is the "ADD" SOA */ if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size))) { break; } #ifndef NDEBUG #if DUMP_ICMTL_UPDATE != 0 if(err != 0) { format("zdb_zone_update_ixfr T: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype); print_rdata(rtype, rdata, rdata_size); println(""); } #endif #endif /* The SOA serial is supposed to be bigger than the previous one */ u32 serial_to; if(FAIL(err = rr_soa_get_serial(rdata, rdata_size, &serial_to))) { break; } /* * After the "add" sequence is done, serial_current will be serial_to */ do { /* Load the record without the data (add) */ if(FAIL(err = input_stream_read_rr_header(is, rname, sizeof (rname), &rtype, &rclass, &rttl, &rdata_size))) { break; } #if ZDB_NSEC3_SUPPORT != 0 if(rtype == TYPE_NSEC3PARAM) { /* Load the data */ if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size))) { break; } #ifndef NDEBUG #if DUMP_ICMTL_UPDATE != 0 if(err != 0) { format("zdb_zone_update_ixfr A: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype); print_rdata(rtype, rdata, rdata_size); println(""); } #endif #endif /* Add it to the nsec3 context */ nsec3_load_add_nsec3param(&nsec3_context, rdata, rdata_size); /* Add it to the zone */ DEBUG_RESET_dnsname(entry_name); dnsname_to_dnsname_vector(rname, &entry_name); ZDB_RECORD_ZALLOC(ttlrdata, rttl, rdata_size, rdata); zdb_zone_record_add(zone, entry_name.labels, (entry_name.size - name.size) - 1, rtype, ttlrdata); } else if(rtype == TYPE_NSEC3) { /* Load the data */ if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size))) { break; } #ifndef NDEBUG #if DUMP_ICMTL_UPDATE != 0 if(err != 0) { format("zdb_zone_update_ixfr A: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype); print_rdata(rtype, rdata, rdata_size); println(""); } #endif #endif /* Add it to the nsec3 context */ if(FAIL(err = nsec3_load_add_nsec3(&nsec3_context, rname, rttl, rdata, rdata_size))) { break; } } else if(rtype == TYPE_RRSIG) { /* Load the data */ if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size))) { break; } #if DUMP_ICMTL_UPDATE != 0 if(err != 0) { format("zdb_zone_update_ixfr A: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype); print_rdata(rtype, rdata, rdata_size); println(""); } #endif if((GET_U16_AT(*rdata)) == TYPE_NSEC3) /** @note : NATIVETYPE */ { /* Add it to the nsec3 context */ if(FAIL(err = nsec3_load_add_rrsig(&nsec3_context, rname, rttl, rdata, rdata_size))) { break; } } else { /* Add it to the zone */ DEBUG_RESET_dnsname(entry_name); dnsname_to_dnsname_vector(rname, &entry_name); ZDB_RECORD_ZALLOC(ttlrdata, rttl, rdata_size, rdata); zdb_zone_record_add(zone, entry_name.labels, (entry_name.size - name.size) - 1, rtype, ttlrdata); } } else { #endif /* Add it to the zone */ ZDB_RECORD_ZALLOC_EMPTY(ttlrdata, rttl, rdata_size); if(FAIL(err = input_stream_read_fully(is, ZDB_PACKEDRECORD_PTR_RDATAPTR(ttlrdata), rdata_size))) { break; } #ifndef NDEBUG #if DUMP_ICMTL_UPDATE != 0 if(err != 0) { format("zdb_zone_update_ixfr A: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype); print_rdata(rtype, ttlrdata->rdata_start, rdata_size); println(termout, ""); } #endif #endif DEBUG_RESET_dnsname(entry_name); dnsname_to_dnsname_vector(rname, &entry_name); zdb_zone_record_add(zone, entry_name.labels, (entry_name.size - name.size) - 1, rtype, ttlrdata); /* class is implicit */ #if ZDB_NSEC3_SUPPORT != 0 } #endif } while(rtype != TYPE_SOA); /* * The record is either the first of another SOA pair (sub, add) * Either the final one. */ /* Update the current serial */ serial_current = serial_to; break; } free(tmp_ttlrdata); if(ISOK(err)) { /* * soa_ttlrdata is the new SOA */ zdb_zone_record_add(zone, NULL, -1, TYPE_SOA, soa_ttlrdata); #if ZDB_NSEC3_SUPPORT != 0 /** * Check if there is both NSEC & NSEC3. Reject if yes. * compile NSEC if any * compile NSEC3 if any * * @todo: I'm only doing NSEC3 here. Do NSEC as well */ err = nsec3_load_compile(&nsec3_context); if((nsec3_context.nsec3_rejected > 0)||(nsec3_context.nsec3_discarded > 0)) { err = DNSSEC_ERROR_NSEC3_INVALIDZONESTATE; } if(ISOK(err)) { nsec3_load_destroy(&nsec3_context); #endif zone_label->zone = zone; return err; #if ZDB_NSEC3_SUPPORT != 0 } #endif } #if ZDB_NSEC3_SUPPORT != 0 nsec3_load_destroy(&nsec3_context); #endif /** * @note : do NOT use these to unload a zone. * zdb_zone_label_delete(db, &name, zone->zclass); * zdb_zone_destroy(zone); */ zdb_zone_unload(db, &name, zdb_zone_getclass(zone)); return err; }