diff options
author | Jakob Schlyter <jakob@cvs.openbsd.org> | 2010-01-15 19:25:03 +0000 |
---|---|---|
committer | Jakob Schlyter <jakob@cvs.openbsd.org> | 2010-01-15 19:25:03 +0000 |
commit | 19923c28d4d7acc78ae1d4acf0332333ca85c456 (patch) | |
tree | e4bef5687db33b36c97eff67782ab2f868c25091 /usr.sbin/nsd/difffile.c | |
parent | 76bf05a40c6dd9acce15c44b07a38135e5faa628 (diff) |
NSD v3.2.4
Diffstat (limited to 'usr.sbin/nsd/difffile.c')
-rw-r--r-- | usr.sbin/nsd/difffile.c | 2092 |
1 files changed, 772 insertions, 1320 deletions
diff --git a/usr.sbin/nsd/difffile.c b/usr.sbin/nsd/difffile.c index 5510714f364..c11f5e7faaf 100644 --- a/usr.sbin/nsd/difffile.c +++ b/usr.sbin/nsd/difffile.c @@ -7,33 +7,28 @@ * */ -#include "config.h" +#include <config.h> #include <assert.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include "difffile.h" -#include "xfrd-disk.h" #include "util.h" #include "packet.h" #include "rdata.h" -#include "udb.h" -#include "udbzone.h" -#include "nsec3.h" -#include "nsd.h" -#include "rrl.h" static int -write_64(FILE *out, uint64_t val) +write_32(FILE *out, uint32_t val) { + val = htonl(val); return write_data(out, &val, sizeof(val)); } static int -write_32(FILE *out, uint32_t val) +write_16(FILE *out, uint16_t val) { - val = htonl(val); + val = htons(val); return write_data(out, &val, sizeof(val)); } @@ -53,117 +48,143 @@ write_str(FILE *out, const char* str) } void -diff_write_packet(const char* zone, const char* pat, uint32_t old_serial, - uint32_t new_serial, uint32_t seq_nr, uint8_t* data, size_t len, - struct nsd* nsd, uint64_t filenumber) +diff_write_packet(const char* zone, uint32_t new_serial, uint16_t id, + uint32_t seq_nr, uint8_t* data, size_t len, nsd_options_t* opt) { - FILE* df = xfrd_open_xfrfile(nsd, filenumber, seq_nr?"a":"w"); - if(!df) { - log_msg(LOG_ERR, "could not open transfer %s file %lld: %s", - zone, (long long)filenumber, strerror(errno)); + const char* filename = opt->difffile; + struct timeval tv; + FILE *df; + uint32_t file_len = sizeof(uint32_t) + strlen(zone) + + sizeof(new_serial) + sizeof(id) + sizeof(seq_nr) + len; + + if (gettimeofday(&tv, NULL) != 0) { + log_msg(LOG_ERR, "could not set timestamp for %s: %s", + filename, strerror(errno)); return; } - /* if first part, first write the header */ - if(seq_nr == 0) { - struct timeval tv; - if (gettimeofday(&tv, NULL) != 0) { - log_msg(LOG_ERR, "could not get timestamp for %s: %s", - zone, strerror(errno)); - } - if(!write_32(df, DIFF_PART_XFRF) || - !write_8(df, 0) /* notcommitted(yet) */ || - !write_32(df, 0) /* numberofparts when done */ || - !write_64(df, (uint64_t) tv.tv_sec) || - !write_32(df, (uint32_t) tv.tv_usec) || - !write_32(df, old_serial) || - !write_32(df, new_serial) || - !write_64(df, (uint64_t) tv.tv_sec) || - !write_32(df, (uint32_t) tv.tv_usec) || - !write_str(df, zone) || - !write_str(df, pat)) { - log_msg(LOG_ERR, "could not write transfer %s file %lld: %s", - zone, (long long)filenumber, strerror(errno)); - fclose(df); - return; - } + df = fopen(filename, "a"); + if(!df) { + log_msg(LOG_ERR, "could not open file %s for append: %s", + filename, strerror(errno)); + return; } - if(!write_32(df, DIFF_PART_XXFR) || - !write_32(df, len) || + if(!write_32(df, DIFF_PART_IXFR) || + !write_32(df, (uint32_t) tv.tv_sec) || + !write_32(df, (uint32_t) tv.tv_usec) || + !write_32(df, file_len) || + !write_str(df, zone) || + !write_32(df, new_serial) || + !write_16(df, id) || + !write_32(df, seq_nr) || !write_data(df, data, len) || - !write_32(df, len)) + !write_32(df, file_len)) { - log_msg(LOG_ERR, "could not write transfer %s file %lld: %s", - zone, (long long)filenumber, strerror(errno)); + log_msg(LOG_ERR, "could not write to file %s: %s", + filename, strerror(errno)); } + fflush(df); fclose(df); } void -diff_write_commit(const char* zone, uint32_t old_serial, uint32_t new_serial, - uint32_t num_parts, uint8_t commit, const char* log_str, - struct nsd* nsd, uint64_t filenumber) +diff_write_commit(const char* zone, uint32_t old_serial, + uint32_t new_serial, uint16_t id, uint32_t num_parts, + uint8_t commit, const char* log_str, nsd_options_t* opt) { + const char* filename = opt->difffile; struct timeval tv; - FILE* df; + FILE *df; + uint32_t len; if (gettimeofday(&tv, NULL) != 0) { log_msg(LOG_ERR, "could not set timestamp for %s: %s", - zone, strerror(errno)); + filename, strerror(errno)); + return; } - /* overwrite the first part of the file with 'committed = 1', - * as well as the end_time and number of parts. - * also write old_serial and new_serial, so that a bad file mixup - * will result in unusable serial numbers. */ - - df = xfrd_open_xfrfile(nsd, filenumber, "r+"); + df = fopen(filename, "a"); if(!df) { - log_msg(LOG_ERR, "could not open transfer %s file %lld: %s", - zone, (long long)filenumber, strerror(errno)); + log_msg(LOG_ERR, "could not open file %s for append: %s", + filename, strerror(errno)); return; } - if(!write_32(df, DIFF_PART_XFRF) || - !write_8(df, commit) /* committed */ || - !write_32(df, num_parts) || - !write_64(df, (uint64_t) tv.tv_sec) || + + len = strlen(zone) + sizeof(len) + sizeof(old_serial) + + sizeof(new_serial) + sizeof(id) + sizeof(num_parts) + + sizeof(commit) + strlen(log_str) + sizeof(len); + + if(!write_32(df, DIFF_PART_SURE) || + !write_32(df, (uint32_t) tv.tv_sec) || !write_32(df, (uint32_t) tv.tv_usec) || + !write_32(df, len) || + !write_str(df, zone) || !write_32(df, old_serial) || - !write_32(df, new_serial)) + !write_32(df, new_serial) || + !write_16(df, id) || + !write_32(df, num_parts) || + !write_8(df, commit) || + !write_str(df, log_str) || + !write_32(df, len)) { - log_msg(LOG_ERR, "could not write transfer %s file %lld: %s", - zone, (long long)filenumber, strerror(errno)); - fclose(df); - return; - } - - /* append the log_str to the end of the file */ - if(fseek(df, 0, SEEK_END) == -1) { - log_msg(LOG_ERR, "could not fseek transfer %s file %lld: %s", - zone, (long long)filenumber, strerror(errno)); - fclose(df); - return; - } - if(!write_str(df, log_str)) { - log_msg(LOG_ERR, "could not write transfer %s file %lld: %s", - zone, (long long)filenumber, strerror(errno)); - fclose(df); - return; - + log_msg(LOG_ERR, "could not write to file %s: %s", + filename, strerror(errno)); } fflush(df); fclose(df); } +/* + * Checksum to signal no data change occured (for example, by a + * zonec run. + */ int -diff_read_64(FILE *in, uint64_t* result) +db_crc_different(namedb_type* db) { - if (fread(result, sizeof(*result), 1, in) == 1) { - return 1; - } else { - return 0; + FILE *fd = fopen(db->filename, "r"); + uint32_t crc_file; + char buf[NAMEDB_MAGIC_SIZE]; + if(fd == NULL) { + log_msg(LOG_ERR, "unable to load %s: %s", + db->filename, strerror(errno)); + return -1; + } + + /* seek to position of CRC, check it and magic no */ + if(fseeko(fd, db->crc_pos, SEEK_SET)==-1) { + log_msg(LOG_ERR, "unable to fseeko %s: %s. db changed?", + db->filename, strerror(errno)); + fclose(fd); + return -1; + } + + if(fread(&crc_file, sizeof(crc_file), 1, fd) != 1) { + if(!feof(fd)) + log_msg(LOG_ERR, "could not read %s CRC: %s. " + "db changed?", db->filename, strerror(errno)); + fclose(fd); + return -1; + } + crc_file = ntohl(crc_file); + + if(fread(buf, sizeof(char), sizeof(buf), fd) != sizeof(buf)) { + if(!feof(fd)) + log_msg(LOG_ERR, "could not read %s magic: %s. " + "db changed?", db->filename, strerror(errno)); + fclose(fd); + return -1; + } + if(memcmp(buf, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE) != 0) { + fclose(fd); + return -1; } + + fclose(fd); + + if(db->crc == crc_file) + return 0; + return 1; } int @@ -178,6 +199,17 @@ diff_read_32(FILE *in, uint32_t* result) } int +diff_read_16(FILE *in, uint16_t* result) +{ + if (fread(result, sizeof(*result), 1, in) == 1) { + *result = ntohs(*result); + return 1; + } else { + return 0; + } +} + +int diff_read_8(FILE *in, uint8_t* result) { if (fread(result, sizeof(*result), 1, in) == 1) { @@ -227,7 +259,7 @@ has_data_below(domain_type* top) assert(d != NULL); /* in the canonical ordering subdomains are after this name */ d = domain_next(d); - while(d != NULL && domain_is_subdomain(d, top)) { + while(d != NULL && dname_is_subdomain(domain_dname(d), domain_dname(top))) { if(d->is_existing) return 1; d = domain_next(d); @@ -235,29 +267,6 @@ has_data_below(domain_type* top) return 0; } -/** check if domain with 0 rrsets has become empty (nonexist) */ -static void -rrset_zero_nonexist_check(domain_type* domain) -{ - /* is the node now an empty node (completely deleted) */ - if(domain->rrsets == 0) { - /* if there is no data below it, it becomes non existing. - also empty nonterminals above it become nonexisting */ - /* check for data below this node. */ - if(!has_data_below(domain)) { - /* nonexist this domain and all parent empty nonterminals */ - domain_type* p = domain; - while(p != NULL && p->rrsets == 0) { - if(has_data_below(p)) - break; - p->is_existing = 0; - p = p->parent; - } - } - } -} - -/** remove rrset. Adjusts zone params. Does not remove domain */ static void rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset) { @@ -274,355 +283,98 @@ rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset) *pp = rrset->next; DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete rrset of %s type %s", - domain_to_string(domain), + dname_to_string(domain_dname(domain),0), rrtype_to_string(rrset_rrtype(rrset)))); /* is this a SOA rrset ? */ if(rrset->zone->soa_rrset == rrset) { rrset->zone->soa_rrset = 0; + rrset->zone->updated = 1; } if(rrset->zone->ns_rrset == rrset) { rrset->zone->ns_rrset = 0; } +#ifdef DNSSEC if(domain == rrset->zone->apex && rrset_rrtype(rrset) == TYPE_RRSIG) { for (i = 0; i < rrset->rr_count; ++i) { - if(rr_rrsig_type_covered(&rrset->rrs[i])==TYPE_DNSKEY) { + if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_SOA) { rrset->zone->is_secure = 0; break; } } } +#endif /* recycle the memory space of the rrset */ for (i = 0; i < rrset->rr_count; ++i) add_rdata_to_recyclebin(db, &rrset->rrs[i]); region_recycle(db->region, rrset->rrs, sizeof(rr_type) * rrset->rr_count); - rrset->rr_count = 0; region_recycle(db->region, rrset, sizeof(rrset_type)); + + /* is the node now an empty node (completely deleted) */ + if(domain->rrsets == 0) { + /* if there is no data below it, it becomes non existing. + also empty nonterminals above it become nonexisting */ + /* check for data below this node. */ + if(!has_data_below(domain)) { + /* nonexist this domain and all parent empty nonterminals */ + domain_type* p = domain; + while(p != NULL && p->rrsets == 0) { + p->is_existing = 0; + p = p->parent; + } + } + } + rrset->rr_count = 0; } static int -rdatas_equal(rdata_atom_type *a, rdata_atom_type *b, int num, uint16_t type, - int* rdnum, char** reason) +rdatas_equal(rdata_atom_type *a, rdata_atom_type *b, int num, uint16_t type) { - int k, start, end; - start = 0; - end = num; - /** - * SOA RDATA comparisons in XFR are more lenient, - * only serial rdata is checked. - **/ - if (type == TYPE_SOA) { - start = 2; - end = 3; - } - for(k = start; k < end; k++) + int k; + for(k = 0; k < num; k++) { if(rdata_atom_is_domain(type, k)) { if(dname_compare(domain_dname(a[k].domain), - domain_dname(b[k].domain))!=0) { - *rdnum = k; - *reason = "dname data"; + domain_dname(b[k].domain))!=0) return 0; - } - } else if(rdata_atom_is_literal_domain(type, k)) { - /* literal dname, but compare case insensitive */ - if(a[k].data[0] != b[k].data[0]) { - *rdnum = k; - *reason = "literal dname len"; - return 0; /* uncompressed len must be equal*/ - } - if(!dname_equal_nocase((uint8_t*)(a[k].data+1), - (uint8_t*)(b[k].data+1), a[k].data[0])) { - *rdnum = k; - *reason = "literal dname data"; - return 0; - } } else { /* check length */ - if(a[k].data[0] != b[k].data[0]) { - *rdnum = k; - *reason = "rdata len"; + if(a[k].data[0] != b[k].data[0]) return 0; - } /* check data */ - if(memcmp(a[k].data+1, b[k].data+1, a[k].data[0])!=0) { - *rdnum = k; - *reason = "rdata data"; + if(memcmp(a[k].data+1, b[k].data+1, a[k].data[0])!=0) return 0; - } } } return 1; } -static void -debug_find_rr_num(rrset_type* rrset, uint16_t type, uint16_t klass, - rdata_atom_type *rdatas, ssize_t rdata_num) -{ - int i, rd; - char* reason = ""; - - for(i=0; i < rrset->rr_count; ++i) { - if (rrset->rrs[i].type != type) { - log_msg(LOG_WARNING, "diff: RR <%s, %s> does not match " - "RR num %d type %s", - dname_to_string(rrset->rrs[i].owner->dname,0), - rrtype_to_string(type), i, - rrtype_to_string(rrset->rrs[i].type)); - } - if (rrset->rrs[i].klass != klass) { - log_msg(LOG_WARNING, "diff: RR <%s, %s> class %d " - "does not match RR num %d class %d", - dname_to_string(rrset->rrs[i].owner->dname,0), - rrtype_to_string(type), - klass, i, - rrset->rrs[i].klass); - } - if (rrset->rrs[i].rdata_count != rdata_num) { - log_msg(LOG_WARNING, "diff: RR <%s, %s> rdlen %u " - "does not match RR num %d rdlen %d", - dname_to_string(rrset->rrs[i].owner->dname,0), - rrtype_to_string(type), - (unsigned) rdata_num, i, - (unsigned) rrset->rrs[i].rdata_count); - } - if (!rdatas_equal(rdatas, rrset->rrs[i].rdatas, rdata_num, type, - &rd, &reason)) { - log_msg(LOG_WARNING, "diff: RR <%s, %s> rdata element " - "%d differs from RR num %d rdata (%s)", - dname_to_string(rrset->rrs[i].owner->dname,0), - rrtype_to_string(type), - rd, i, reason); - } - } -} - static int -find_rr_num(rrset_type* rrset, uint16_t type, uint16_t klass, - rdata_atom_type *rdatas, ssize_t rdata_num, int add) +find_rr_num(rrset_type* rrset, + uint16_t type, uint16_t klass, + rdata_atom_type *rdatas, ssize_t rdata_num) { - int i, rd; - char* reason; + int i; for(i=0; i < rrset->rr_count; ++i) { if(rrset->rrs[i].type == type && rrset->rrs[i].klass == klass && rrset->rrs[i].rdata_count == rdata_num && - rdatas_equal(rdatas, rrset->rrs[i].rdatas, rdata_num, type, - &rd, &reason)) + rdatas_equal(rdatas, rrset->rrs[i].rdatas, rdata_num, type)) { return i; } } - /* this is odd. Log why rr cannot be found. */ - if (!add) { - debug_find_rr_num(rrset, type, klass, rdatas, rdata_num); - } - return -1; -} - -#ifdef NSEC3 -/* see if nsec3 deletion triggers need action */ -static void -nsec3_delete_rr_trigger(namedb_type* db, rr_type* rr, zone_type* zone, - udb_ptr* udbz) -{ - /* the RR has not actually been deleted yet, so we can inspect it */ - if(!zone->nsec3_param) - return; - /* see if the domain was an NSEC3-domain in the chain, but no longer */ - if(rr->type == TYPE_NSEC3 && rr->owner->nsec3 && - rr->owner->nsec3->nsec3_node.key && - nsec3_rr_uses_params(rr, zone) && - nsec3_in_chain_count(rr->owner, zone) <= 1) { - domain_type* prev = nsec3_chain_find_prev(zone, rr->owner); - /* remove from prehash because no longer an NSEC3 domain */ - if(domain_is_prehash(db->domains, rr->owner)) - prehash_del(db->domains, rr->owner); - /* fixup the last in the zone */ - if(rr->owner == zone->nsec3_last) - zone->nsec3_last = prev; - /* unlink from the nsec3tree */ - zone_del_domain_in_hash_tree(zone->nsec3tree, - &rr->owner->nsec3->nsec3_node); - /* add previous NSEC3 to the prehash list */ - if(prev && prev != rr->owner) - prehash_add(db->domains, prev); - else nsec3_clear_precompile(db, zone); - /* this domain becomes ordinary data domain: done later */ - } - /* see if the rr was NSEC3PARAM that we were using */ - else if(rr->type == TYPE_NSEC3PARAM && rr == zone->nsec3_param) { - /* clear trees, wipe hashes, wipe precompile */ - nsec3_clear_precompile(db, zone); - /* pick up new nsec3param (from udb, or avoid deleted rr) */ - nsec3_find_zone_param(db, zone, udbz, rr); - /* if no more NSEC3, done */ - if(!zone->nsec3_param) - return; - nsec3_precompile_newparam(db, zone); - } -} -/* see if nsec3 prehash can be removed with new rrset content */ -static void -nsec3_rrsets_changed_remove_prehash(domain_type* domain, zone_type* zone) -{ - /* deletion of rrset already done, we can check if conditions apply */ - /* see if the domain is no longer precompiled */ - /* it has a hash_node, but no longer fulfills conditions */ - if(nsec3_domain_part_of_zone(domain, zone) && domain->nsec3 && - domain->nsec3->hash_node.key && - !nsec3_condition_hash(domain, zone)) { - /* remove precompile */ - domain->nsec3->nsec3_cover = NULL; - domain->nsec3->nsec3_wcard_child_cover = NULL; - domain->nsec3->nsec3_is_exact = 0; - /* remove it from the hash tree */ - zone_del_domain_in_hash_tree(zone->hashtree, - &domain->nsec3->hash_node); - zone_del_domain_in_hash_tree(zone->wchashtree, - &domain->nsec3->wchash_node); - } - if(domain != zone->apex && domain->nsec3 && - domain->nsec3->dshash_node.key && - (!domain->parent || nsec3_domain_part_of_zone(domain->parent, zone)) && - !nsec3_condition_dshash(domain, zone)) { - /* remove precompile */ - domain->nsec3->nsec3_ds_parent_cover = NULL; - domain->nsec3->nsec3_ds_parent_is_exact = 0; - /* remove it from the hash tree */ - zone_del_domain_in_hash_tree(zone->dshashtree, - &domain->nsec3->dshash_node); - } -} - -/* see if domain needs to get precompiled info */ -static void -nsec3_rrsets_changed_add_prehash(namedb_type* db, domain_type* domain, - zone_type* zone) -{ - if(!zone->nsec3_param) - return; - if((!domain->nsec3 || !domain->nsec3->hash_node.key) - && nsec3_condition_hash(domain, zone)) { - region_type* tmpregion = region_create(xalloc, free); - nsec3_precompile_domain(db, domain, zone, tmpregion); - region_destroy(tmpregion); - } - if((!domain->nsec3 || !domain->nsec3->dshash_node.key) - && nsec3_condition_dshash(domain, zone)) { - nsec3_precompile_domain_ds(db, domain, zone); - } -} - -/* see if nsec3 rrset-deletion triggers need action */ -static void -nsec3_delete_rrset_trigger(namedb_type* db, domain_type* domain, - zone_type* zone, uint16_t type) -{ - if(!zone->nsec3_param) - return; - nsec3_rrsets_changed_remove_prehash(domain, zone); - /* for type nsec3, or a delegation, the domain may have become a - * 'normal' domain with its remaining data now */ - if(type == TYPE_NSEC3 || type == TYPE_NS || type == TYPE_DS) - nsec3_rrsets_changed_add_prehash(db, domain, zone); - /* for type DNAME or a delegation, obscured data may be revealed */ - if(type == TYPE_NS || type == TYPE_DS || type == TYPE_DNAME) { - /* walk over subdomains and check them each */ - domain_type *d; - for(d=domain_next(domain); d && domain_is_subdomain(d, domain); - d=domain_next(d)) { - nsec3_rrsets_changed_add_prehash(db, d, zone); - } - } -} - -/* see if nsec3 addition triggers need action */ -static void -nsec3_add_rr_trigger(namedb_type* db, rr_type* rr, zone_type* zone, - udb_ptr* udbz) -{ - /* the RR has been added in full, also to UDB (and thus NSEC3PARAM - * in the udb has been adjusted) */ - if(zone->nsec3_param && rr->type == TYPE_NSEC3 && - (!rr->owner->nsec3 || !rr->owner->nsec3->nsec3_node.key) - && nsec3_rr_uses_params(rr, zone)) { - /* added NSEC3 into the chain */ - nsec3_precompile_nsec3rr(db, rr->owner, zone); - /* the domain has become an NSEC3-domain, if it was precompiled - * previously, remove that, neatly done in routine above */ - nsec3_rrsets_changed_remove_prehash(rr->owner, zone); - /* set this NSEC3 to prehash */ - prehash_add(db->domains, rr->owner); - } else if(!zone->nsec3_param && rr->type == TYPE_NSEC3PARAM) { - /* see if this means NSEC3 chain can be used */ - nsec3_find_zone_param(db, zone, udbz, NULL); - if(!zone->nsec3_param) - return; - nsec3_zone_trees_create(db->region, zone); - nsec3_precompile_newparam(db, zone); - } -} - -/* see if nsec3 rrset-addition triggers need action */ -static void -nsec3_add_rrset_trigger(namedb_type* db, domain_type* domain, zone_type* zone, - uint16_t type) -{ - /* the rrset has been added so we can inspect it */ - if(!zone->nsec3_param) - return; - /* because the rrset is added we can check conditions easily. - * check if domain needs to become precompiled now */ - nsec3_rrsets_changed_add_prehash(db, domain, zone); - /* if a delegation, it changes from normal name to unhashed referral */ - if(type == TYPE_NS || type == TYPE_DS) { - nsec3_rrsets_changed_remove_prehash(domain, zone); - } - /* if delegation or DNAME added, then some RRs may get obscured */ - if(type == TYPE_NS || type == TYPE_DS || type == TYPE_DNAME) { - /* walk over subdomains and check them each */ - domain_type *d; - for(d=domain_next(domain); d && domain_is_subdomain(d, domain); - d=domain_next(d)) { - nsec3_rrsets_changed_remove_prehash(d, zone); - } - } -} -#endif /* NSEC3 */ - -/* fixup usage lower for domain names in the rdata */ -static void -rr_lower_usage(namedb_type* db, rr_type* rr) -{ - unsigned i; - for(i=0; i<rr->rdata_count; i++) { - if(rdata_atom_is_domain(rr->type, i)) { - assert(rdata_atom_domain(rr->rdatas[i])->usage > 0); - rdata_atom_domain(rr->rdatas[i])->usage --; - if(rdata_atom_domain(rr->rdatas[i])->usage == 0) - domain_table_deldomain(db, - rdata_atom_domain(rr->rdatas[i])); - } - } -} - -static void -rrset_lower_usage(namedb_type* db, rrset_type* rrset) -{ - unsigned i; - for(i=0; i<rrset->rr_count; i++) - rr_lower_usage(db, &rrset->rrs[i]); + return -1; } -int +static int delete_RR(namedb_type* db, const dname_type* dname, uint16_t type, uint16_t klass, buffer_type* packet, size_t rdatalen, zone_type *zone, - region_type* temp_region, udb_ptr* udbz, int* softfail) + region_type* temp_region) { domain_type *domain; rrset_type *rrset; @@ -631,7 +383,6 @@ delete_RR(namedb_type* db, const dname_type* dname, log_msg(LOG_WARNING, "diff: domain %s does not exist", dname_to_string(dname,0)); buffer_skip(packet, rdatalen); - *softfail = 1; return 1; /* not fatal error */ } rrset = domain_find_rrset(domain, zone, type); @@ -639,7 +390,6 @@ delete_RR(namedb_type* db, const dname_type* dname, log_msg(LOG_WARNING, "diff: rrset %s does not exist", dname_to_string(dname,0)); buffer_skip(packet, rdatalen); - *softfail = 1; return 1; /* not fatal error */ } else { /* find the RR in the rrset */ @@ -658,37 +408,15 @@ delete_RR(namedb_type* db, const dname_type* dname, dname_to_string(dname,0)); return 0; } - rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num, 0); - if(rrnum == -1 && type == TYPE_SOA && domain == zone->apex - && rrset->rr_count != 0) - rrnum = 0; /* replace existing SOA if no match */ + rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num); if(rrnum == -1) { - log_msg(LOG_WARNING, "diff: RR <%s, %s> does not exist", - dname_to_string(dname,0), rrtype_to_string(type)); - *softfail = 1; + log_msg(LOG_WARNING, "diff: RR %s does not exist", + dname_to_string(dname,0)); return 1; /* not fatal error */ } - /* delete the normalized RR from the udb */ - if(db->udb) - udb_del_rr(db->udb, udbz, &rrset->rrs[rrnum]); -#ifdef NSEC3 - /* process triggers for RR deletions */ - nsec3_delete_rr_trigger(db, &rrset->rrs[rrnum], zone, udbz); -#endif - /* lower usage (possibly deleting other domains, and thus - * invalidating the current RR's domain pointers) */ - rr_lower_usage(db, &rrset->rrs[rrnum]); if(rrset->rr_count == 1) { /* delete entire rrset */ rrset_delete(db, domain, rrset); - /* check if domain is now nonexisting (or parents) */ - rrset_zero_nonexist_check(domain); -#ifdef NSEC3 - /* cleanup nsec3 */ - nsec3_delete_rrset_trigger(db, domain, zone, type); -#endif - /* see if the domain can be deleted (and inspect parents) */ - domain_table_deldomain(db, domain); } else { /* swap out the bad RR and decrease the count */ rr_type* rrs_orig = rrset->rrs; @@ -705,41 +433,16 @@ delete_RR(namedb_type* db, const dname_type* dname, } region_recycle(db->region, rrs_orig, sizeof(rr_type) * rrset->rr_count); -#ifdef NSEC3 - if(type == TYPE_NSEC3PARAM && zone->nsec3_param) { - /* fixup nsec3_param pointer to same RR */ - assert(zone->nsec3_param >= rrs_orig && - zone->nsec3_param <= - rrs_orig+rrset->rr_count); - /* last moved to rrnum, others at same index*/ - if(zone->nsec3_param == &rrs_orig[ - rrset->rr_count-1]) - zone->nsec3_param = &rrset->rrs[rrnum]; - else - zone->nsec3_param = - (void*)zone->nsec3_param - -(void*)rrs_orig + - (void*)rrset->rrs; - } -#endif /* NSEC3 */ rrset->rr_count --; -#ifdef NSEC3 - /* for type nsec3, the domain may have become a - * 'normal' domain with its remaining data now */ - if(type == TYPE_NSEC3) - nsec3_rrsets_changed_add_prehash(db, domain, - zone); -#endif /* NSEC3 */ } } return 1; } -int +static int add_RR(namedb_type* db, const dname_type* dname, uint16_t type, uint16_t klass, uint32_t ttl, - buffer_type* packet, size_t rdatalen, zone_type *zone, udb_ptr* udbz, - int* softfail) + buffer_type* packet, size_t rdatalen, zone_type *zone) { domain_type* domain; rrset_type* rrset; @@ -747,7 +450,6 @@ add_RR(namedb_type* db, const dname_type* dname, rr_type *rrs_old; ssize_t rdata_num; int rrnum; - int rrset_added = 0; domain = domain_table_find(db->domains, dname); if(!domain) { /* create the domain */ @@ -765,7 +467,6 @@ add_RR(namedb_type* db, const dname_type* dname, rrset->rrs = 0; rrset->rr_count = 0; domain_add_rrset(domain, rrset); - rrset_added = 1; } /* dnames in rdata are normalized, conform RFC 4035, @@ -778,12 +479,11 @@ add_RR(namedb_type* db, const dname_type* dname, dname_to_string(dname,0)); return 0; } - rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num, 1); + rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num); if(rrnum != -1) { - DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR <%s, %s> already exists", - dname_to_string(dname,0), rrtype_to_string(type))); + DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR %s already exists", + dname_to_string(dname,0))); /* ignore already existing RR: lenient accepting of messages */ - *softfail = 1; return 1; } @@ -809,150 +509,157 @@ add_RR(namedb_type* db, const dname_type* dname, /* see if it is a SOA */ if(domain == zone->apex) { - apex_rrset_checks(db, rrset, domain); -#ifdef NSEC3 - if(type == TYPE_NSEC3PARAM && zone->nsec3_param) { - /* the pointer just changed, fix it up to point - * to the same record */ - assert(zone->nsec3_param >= rrs_old && - zone->nsec3_param < rrs_old+rrset->rr_count); - /* in this order to make sure no overflow/underflow*/ - zone->nsec3_param = (void*)zone->nsec3_param - - (void*)rrs_old + (void*)rrset->rrs; + if(type == TYPE_SOA) { + uint32_t soa_minimum; + zone->soa_rrset = rrset; + zone->updated = 1; + /* BUG #103 tweaked SOA ttl value */ + if(zone->soa_nx_rrset == 0) { + zone->soa_nx_rrset = region_alloc(db->region, + sizeof(rrset_type)); + if(!zone->soa_nx_rrset) { + log_msg(LOG_ERR, "out of memory, %s:%d", + __FILE__, __LINE__); + exit(1); + } + zone->soa_nx_rrset->rr_count = 1; + zone->soa_nx_rrset->next = 0; + zone->soa_nx_rrset->zone = zone; + zone->soa_nx_rrset->rrs = region_alloc(db->region, + sizeof(rr_type)); + if(!zone->soa_nx_rrset->rrs) { + log_msg(LOG_ERR, "out of memory, %s:%d", + __FILE__, __LINE__); + exit(1); + } + } + memcpy(zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type)); + memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]), + rdata_atom_size(rrset->rrs->rdatas[6])); + if (rrset->rrs->ttl > ntohl(soa_minimum)) { + rrset->zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum); + } } -#endif /* NSEC3 */ - } - - /* write the just-normalized RR to the udb */ - if(db->udb) { - if(!udb_write_rr(db->udb, udbz, &rrset->rrs[rrset->rr_count - 1])) { - log_msg(LOG_ERR, "could not add RR to nsd.db, disk-space?"); - return 0; + if(type == TYPE_NS) { + zone->ns_rrset = rrset; } - } -#ifdef NSEC3 - if(rrset_added) { - domain_type* p = domain->parent; - nsec3_add_rrset_trigger(db, domain, zone, type); - /* go up and process (possibly created) empty nonterminals, - * until we hit the apex or root */ - while(p && p->rrsets == NULL && !p->is_apex) { - nsec3_rrsets_changed_add_prehash(db, p, zone); - p = p->parent; +#ifdef DNSSEC + if(type == TYPE_RRSIG) { + int i; + for (i = 0; i < rrset->rr_count; ++i) { + if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_SOA) { + zone->is_secure = 1; + break; + } + } } +#endif } - nsec3_add_rr_trigger(db, &rrset->rrs[rrset->rr_count - 1], zone, udbz); -#endif /* NSEC3 */ return 1; } static zone_type* -find_or_create_zone(namedb_type* db, const dname_type* zone_name, - nsd_options_t* opt, const char* zstr, const char* patname) +find_zone(namedb_type* db, const dname_type* zone_name, nsd_options_t* opt, + size_t child_count) { + domain_type *domain; zone_type* zone; - zone_options_t* zopt; - zone = namedb_find_zone(db, zone_name); - if(zone) { - return zone; - } - zopt = zone_options_find(opt, zone_name); - if(!zopt) { - /* if _implicit_ then insert as _part_of_config */ - if(strncmp(patname, PATTERN_IMPLICIT_MARKER, - strlen(PATTERN_IMPLICIT_MARKER)) == 0) { - zopt = zone_options_create(opt->region); - if(!zopt) return 0; - zopt->part_of_config = 1; - zopt->name = region_strdup(opt->region, zstr); - zopt->pattern = pattern_options_find(opt, patname); - if(!zopt->name || !zopt->pattern) return 0; - if(!nsd_options_insert_zone(opt, zopt)) { - log_msg(LOG_ERR, "bad domain name or duplicate zone '%s' " - "pattern %s", zstr, patname); - } - } else { - /* create zone : presumably already added to zonelist - * by xfrd, who wrote the AXFR or IXFR to disk, so we only - * need to add it to our config. - * This process does not need linesize and offset zonelist */ - zopt = zone_list_zone_insert(opt, zstr, patname, 0, 0); - if(!zopt) - return 0; + domain = domain_table_find(db->domains, zone_name); + if(!domain) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating domain %s", + dname_to_string(zone_name,0))); + /* create the zone and domain of apex (zone has config options) */ + domain = domain_table_insert(db->domains, zone_name); + } else { + /* O(1) if SOA exists */ + zone = domain_find_zone(domain); + /* if domain was empty (no rrsets, empty zone) search in zonelist */ + /* check apex to make sure we don't find a parent zone */ + if(!zone || zone->apex != domain) + zone = namedb_find_zone(db, domain); + if(zone) { + assert(zone->apex == domain); + return zone; } } - zone = namedb_zone_create(db, zone_name, zopt); + /* create the zone */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating zone_type %s", + dname_to_string(zone_name,0))); + zone = (zone_type *) region_alloc(db->region, sizeof(zone_type)); + if(!zone) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + zone->next = db->zones; + db->zones = zone; + db->zone_count++; + zone->apex = domain; + zone->soa_rrset = 0; + zone->soa_nx_rrset = 0; + zone->ns_rrset = 0; +#ifdef NSEC3 + zone->nsec3_soa_rr = NULL; + zone->nsec3_last = NULL; +#endif + zone->dirty = region_alloc(db->region, sizeof(uint8_t)*child_count); + if(!zone->dirty) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + memset(zone->dirty, 0, sizeof(uint8_t)*child_count); + zone->opts = zone_options_find(opt, domain_dname(zone->apex)); + if(!zone->opts) { + log_msg(LOG_ERR, "xfr: zone %s not in config.", + dname_to_string(zone_name,0)); + return 0; + } + zone->number = db->zone_count; + zone->is_secure = 0; + zone->updated = 1; + zone->is_ok = 0; return zone; } -void +static void delete_zone_rrs(namedb_type* db, zone_type* zone) { rrset_type *rrset; - domain_type *domain = zone->apex, *next; - int nonexist_check = 0; + domain_type *domain = zone->apex; /* go through entire tree below the zone apex (incl subzones) */ - while(domain && domain_is_subdomain(domain, zone->apex)) + while(domain && dname_is_subdomain( + domain_dname(domain), domain_dname(zone->apex))) { DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete zone visit %s", - domain_to_string(domain))); + dname_to_string(domain_dname(domain),0))); /* delete all rrsets of the zone */ while((rrset = domain_find_any_rrset(domain, zone))) { - /* lower usage can delete other domains */ - rrset_lower_usage(db, rrset); - /* rrset del does not delete our domain(yet) */ rrset_delete(db, domain, rrset); - /* no rrset_zero_nonexist_check, do that later */ - if(domain->rrsets == 0) - nonexist_check = 1; - } - /* the delete upcoming could delete parents, but nothing next - * or after the domain so store next ptr */ - next = domain_next(domain); - /* see if the domain can be deleted (and inspect parents) */ - domain_table_deldomain(db, domain); - domain = next; - } - - /* check if data deleteions have created nonexisting domain entries, - * but after deleting domains so the checks are faster */ - if(nonexist_check) { - DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: zero rrset check")); - domain = zone->apex; - while(domain && domain_is_subdomain(domain, zone->apex)) - { - /* the interesting domains should be existing==1 - * and rrsets==0, speeding up out processing of - * sub-zones, since we only spuriously check empty - * nonterminals */ - if(domain->is_existing) - rrset_zero_nonexist_check(domain); - domain = domain_next(domain); } + domain = domain_next(domain); } DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: recyclebin holds %lu bytes", (unsigned long) region_get_recycle_size(db->region))); #ifndef NDEBUG - if(nsd_debug_level >= 2) + if(nsd_debug_level >= 1) region_log_stats(db->region); #endif assert(zone->soa_rrset == 0); - /* keep zone->soa_nx_rrset alloced: it is reused */ + /* keep zone->soa_nx_rrset alloced */ assert(zone->ns_rrset == 0); assert(zone->is_secure == 0); + assert(zone->updated == 1); } -/* return value 0: syntaxerror,badIXFR, 1:OK, 2:done_and_skip_it */ static int -apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, - nsd_options_t* opt, uint32_t seq_nr, uint32_t seq_total, +apply_ixfr(namedb_type* db, FILE *in, const off_t* startpos, + const char* zone, uint32_t serialno, nsd_options_t* opt, + uint16_t id, uint32_t seq_nr, uint32_t seq_total, int* is_axfr, int* delete_mode, int* rr_count, - udb_ptr* udbz, struct zone** zone_res, const char* patname, int* bytes, - int* softfail) + size_t child_count) { - uint32_t msglen, checklen, pkttype; + uint32_t filelen, msglen, pkttype, timestamp[2]; int qcount, ancount, counter; buffer_type* packet; region_type* region; @@ -960,24 +667,35 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, uint16_t rrlen; const dname_type *dname_zone, *dname; zone_type* zone_db; - - /* note that errors could not really happen due to format of the - * packet since xfrd has checked all dnames and RRs before commit, - * this is why the errors are fatal (exit process), it must be - * something internal or a bad disk or something. */ - + char file_zone_name[3072]; + uint32_t file_serial, file_seq_nr; + uint16_t file_id; + off_t mempos; + + memmove(&mempos, startpos, sizeof(off_t)); + if(fseeko(in, mempos, SEEK_SET) == -1) { + log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno)); + return 0; + } /* read ixfr packet RRs and apply to in memory db */ - if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_XXFR) { + + if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_IXFR) { log_msg(LOG_ERR, "could not read type or wrong type"); return 0; } + if(!diff_read_32(in, ×tamp[0]) || + !diff_read_32(in, ×tamp[1])) { + log_msg(LOG_ERR, "could not read timestamp"); + return 0; + } - if(!diff_read_32(in, &msglen)) { + if(!diff_read_32(in, &filelen)) { log_msg(LOG_ERR, "could not read len"); return 0; } - if(msglen < QHEADERSZ) { + /* read header */ + if(filelen < QHEADERSZ + sizeof(uint32_t)*3 + sizeof(uint16_t)) { log_msg(LOG_ERR, "msg too short"); return 0; } @@ -987,7 +705,34 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, log_msg(LOG_ERR, "out of memory"); return 0; } + + if(!diff_read_str(in, file_zone_name, sizeof(file_zone_name)) || + !diff_read_32(in, &file_serial) || + !diff_read_16(in, &file_id) || + !diff_read_32(in, &file_seq_nr)) + { + log_msg(LOG_ERR, "could not part data"); + region_destroy(region); + return 0; + } + + if(strcmp(file_zone_name, zone) != 0 || serialno != file_serial || + id != file_id || seq_nr != file_seq_nr) { + log_msg(LOG_ERR, "internal error: reading part with changed id"); + region_destroy(region); + return 0; + } + msglen = filelen - sizeof(uint32_t)*3 - sizeof(uint16_t) + - strlen(file_zone_name); packet = buffer_create(region, QIOBUFSZ); + dname_zone = dname_parse(region, zone); + zone_db = find_zone(db, dname_zone, opt, child_count); + if(!zone_db) { + log_msg(LOG_ERR, "no zone exists"); + region_destroy(region); + return 0; + } + if(msglen > QIOBUFSZ) { log_msg(LOG_ERR, "msg too long"); region_destroy(region); @@ -1001,23 +746,6 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, } buffer_set_limit(packet, msglen); - /* see if check on data fails: checks that we are not reading - * random garbage */ - if(!diff_read_32(in, &checklen) || checklen != msglen) { - log_msg(LOG_ERR, "transfer part has incorrect checkvalue"); - return 0; - } - *bytes += msglen; - - dname_zone = dname_parse(region, zone); - zone_db = find_or_create_zone(db, dname_zone, opt, zone, patname); - if(!zone_db) { - log_msg(LOG_ERR, "could not create zone %s %s", zone, patname); - region_destroy(region); - return 0; - } - *zone_res = zone_db; - /* only answer section is really used, question, additional and authority section RRs are skipped */ qcount = QDCOUNT(packet); @@ -1073,8 +801,8 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, } if(buffer_read_u32(packet) != serialno) { buffer_skip(packet, -4); - log_msg(LOG_ERR, "SOA serial %u different from commit %u", - (unsigned)buffer_read_u32(packet), (unsigned)serialno); + log_msg(LOG_ERR, "SOA serial %d different from commit %d", + buffer_read_u32(packet), serialno); region_destroy(region); return 0; } @@ -1083,6 +811,7 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, *rr_count = 1; *is_axfr = 0; *delete_mode = 0; + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s start count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); } @@ -1118,16 +847,7 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, if(*rr_count == 1 && type != TYPE_SOA) { /* second RR: if not SOA: this is an AXFR; delete all zone contents */ -#ifdef NSEC3 - nsec3_hash_tree_clear(zone_db); -#endif delete_zone_rrs(db, zone_db); - if(db->udb) - udb_zone_clear(db->udb, udbz); -#ifdef NSEC3 - nsec3_clear_precompile(db, zone_db); - zone_db->nsec3_param = NULL; -#endif /* NSEC3 */ /* add everything else (incl end SOA) */ *delete_mode = 0; *is_axfr = 1; @@ -1149,30 +869,10 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, thisserial = buffer_read_u32(packet); if(thisserial == serialno) { /* AXFR */ -#ifdef NSEC3 - nsec3_hash_tree_clear(zone_db); -#endif delete_zone_rrs(db, zone_db); - if(db->udb) - udb_zone_clear(db->udb, udbz); -#ifdef NSEC3 - nsec3_clear_precompile(db, zone_db); - zone_db->nsec3_param = NULL; -#endif /* NSEC3 */ *delete_mode = 0; *is_axfr = 1; } - /* must have stuff in memory for a successful IXFR, - * the serial number of the SOA has been checked - * previously (by check_for_bad_serial) if it exists */ - if(!*is_axfr && !domain_find_rrset(zone_db->apex, - zone_db, TYPE_SOA)) { - log_msg(LOG_ERR, "%s SOA serial %u is not " - "in memory, skip IXFR", zone, serialno); - region_destroy(region); - /* break out and stop the IXFR, ignore it */ - return 2; - } buffer_set_position(packet, bufpos); } if(type == TYPE_SOA && !*is_axfr) { @@ -1199,7 +899,7 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, continue; /* do not delete final SOA RR for IXFR */ } if(!delete_RR(db, dname, type, klass, packet, - rrlen, zone_db, region, udbz, softfail)) { + rrlen, zone_db, region)) { region_destroy(region); return 0; } @@ -1208,7 +908,7 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno, { /* add this rr */ if(!add_RR(db, dname, type, klass, ttl, packet, - rrlen, zone_db, udbz, softfail)) { + rrlen, zone_db)) { region_destroy(region); return 0; } @@ -1228,7 +928,7 @@ check_for_bad_serial(namedb_type* db, const char* zone_str, uint32_t old_serial) zone_type* zone = 0; domain = domain_table_find(db->domains, zone_name); if(domain) - zone = domain_find_zone(db, domain); + zone = domain_find_zone(domain); if(zone && zone->apex == domain && zone->soa_rrset && old_serial) { uint32_t memserial; @@ -1243,796 +943,548 @@ check_for_bad_serial(namedb_type* db, const char* zone_str, uint32_t old_serial) return 0; } +/* for multiple tcp packets use a data structure that has + * a rbtree (zone_names) with for each zone: + * has a rbtree by sequence number + * with inside a serial_number and ID (for checking only) + * and contains a off_t to the IXFR packet in the file. + * so when you get a commit for a zone, get zone obj, find sequence, + * then check if you have all sequence numbers available. Apply all packets. + */ +struct diff_read_data { + /* rbtree of struct diff_zone*/ + rbtree_t* zones; + /* region for allocation */ + region_type* region; +}; +struct diff_zone { + /* key is dname of zone */ + rbnode_t node; + /* rbtree of struct diff_xfrpart */ + rbtree_t* parts; +}; +struct diff_xfrpart { + /* key is sequence number */ + rbnode_t node; + uint32_t seq_nr; + uint32_t new_serial; + uint16_t id; + off_t file_pos; +}; + +static struct diff_read_data* +diff_read_data_create() +{ + region_type* region = region_create(xalloc, free); + struct diff_read_data* data = (struct diff_read_data*) + region_alloc(region, sizeof(struct diff_read_data)); + if(!data) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + data->region = region; + data->zones = rbtree_create(region, + (int (*)(const void *, const void *)) dname_compare); + return data; +} + +static struct diff_zone* +diff_read_find_zone(struct diff_read_data* data, const char* name) +{ + const dname_type* dname = dname_parse(data->region, name); + struct diff_zone* zp = (struct diff_zone*) + rbtree_search(data->zones, dname); + return zp; +} + +static int intcompf(const void* a, const void* b) +{ + if(*(uint32_t*)a < *(uint32_t*)b) + return -1; + if(*(uint32_t*)a > *(uint32_t*)b) + return +1; + return 0; +} + +static struct diff_zone* +diff_read_insert_zone(struct diff_read_data* data, const char* name) +{ + const dname_type* dname = dname_parse(data->region, name); + struct diff_zone* zp = region_alloc(data->region, + sizeof(struct diff_zone)); + if(!zp) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + zp->node = *RBTREE_NULL; + zp->node.key = dname; + zp->parts = rbtree_create(data->region, intcompf); + rbtree_insert(data->zones, (rbnode_t*)zp); + return zp; +} + +static struct diff_xfrpart* +diff_read_find_part(struct diff_zone* zp, uint32_t seq_nr) +{ + struct diff_xfrpart* xp = (struct diff_xfrpart*) + rbtree_search(zp->parts, &seq_nr); + return xp; +} + +static struct diff_xfrpart* +diff_read_insert_part(struct diff_read_data* data, + struct diff_zone* zp, uint32_t seq_nr) +{ + struct diff_xfrpart* xp = region_alloc(data->region, + sizeof(struct diff_xfrpart)); + if(!xp) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + xp->node = *RBTREE_NULL; + xp->node.key = &xp->seq_nr; + xp->seq_nr = seq_nr; + rbtree_insert(zp->parts, (rbnode_t*)xp); + return xp; +} + +/* mark commit as rollback and close inputfile, fatal exits */ +static void +mark_and_exit(nsd_options_t* opt, FILE* f, off_t commitpos, const char* desc) +{ + const char* filename = opt->difffile; + fclose(f); + if(!(f = fopen(filename, "r+"))) { + log_msg(LOG_ERR, "mark xfr, failed to re-open difffile %s: %s", + filename, strerror(errno)); + } else if(fseeko(f, commitpos, SEEK_SET) == -1) { + log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno)); + fclose(f); + } else { + uint8_t c = 0; + fwrite(&c, sizeof(c), 1, f); + fclose(f); + log_msg(LOG_ERR, "marked xfr as failed: %s", desc); + log_msg(LOG_ERR, "marked xfr so that next reload can succeed"); + } + exit(1); +} + static int -apply_ixfr_for_zone(nsd_type* nsd, zone_type* zonedb, FILE* in, - nsd_options_t* opt, udb_base* taskudb, udb_ptr* last_task, - uint32_t xfrfilenr) +read_sure_part(namedb_type* db, FILE *in, nsd_options_t* opt, + struct diff_read_data* data, struct diff_log** log, + size_t child_count) { char zone_buf[3072]; char log_buf[5120]; - char patname_buf[2048]; - - uint32_t old_serial, new_serial, num_parts, type; - uint64_t time_end_0, time_start_0; - uint32_t time_end_1, time_start_1; + uint32_t old_serial, new_serial, num_parts; + uint16_t id; uint8_t committed; + struct diff_zone *zp; uint32_t i; - int num_bytes = 0; + int have_all_parts = 1; + struct diff_log* thislog = 0; + off_t commitpos; /* read zone name and serial */ - if(!diff_read_32(in, &type)) { - log_msg(LOG_ERR, "diff file too short"); + if(!diff_read_str(in, zone_buf, sizeof(zone_buf)) || + !diff_read_32(in, &old_serial) || + !diff_read_32(in, &new_serial) || + !diff_read_16(in, &id) || + !diff_read_32(in, &num_parts)) { + log_msg(LOG_ERR, "diff file bad commit part"); return 0; } - if(type != DIFF_PART_XFRF) { - log_msg(LOG_ERR, "xfr file has wrong format"); + commitpos = ftello(in); /* position of commit byte */ + if(commitpos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno)); return 0; - } - /* committed and num_parts are first because they need to be - * updated once the rest is written. The log buf is not certain - * until its done, so at end of file. The patname is in case a - * new zone is created, we know what the options-pattern is */ if(!diff_read_8(in, &committed) || - !diff_read_32(in, &num_parts) || - !diff_read_64(in, &time_end_0) || - !diff_read_32(in, &time_end_1) || - !diff_read_32(in, &old_serial) || - !diff_read_32(in, &new_serial) || - !diff_read_64(in, &time_start_0) || - !diff_read_32(in, &time_start_1) || - !diff_read_str(in, zone_buf, sizeof(zone_buf)) || - !diff_read_str(in, patname_buf, sizeof(patname_buf))) { + !diff_read_str(in, log_buf, sizeof(log_buf)) ) + { log_msg(LOG_ERR, "diff file bad commit part"); return 0; } - /* has been read in completely */ - if(strcmp(zone_buf, dname_to_string(zonedb->apex->dname,0)) != 0) { - log_msg(LOG_ERR, "file %s does not match task %s", - zone_buf, dname_to_string(zonedb->apex->dname,0)); - return 0; - } - if(!committed) { - log_msg(LOG_ERR, "diff file %s was not committed", zone_buf); - return 0; + if(log) { + thislog = (struct diff_log*)region_alloc(db->region, sizeof(struct diff_log)); + if(!thislog) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + thislog->zone_name = region_strdup(db->region, zone_buf); + thislog->comment = region_strdup(db->region, log_buf); + thislog->error = 0; + thislog->next = *log; + *log = thislog; } - if(num_parts == 0) { - log_msg(LOG_ERR, "diff file %s was not completed", zone_buf); - return 0; + + /* has been read in completely */ + zp = diff_read_find_zone(data, zone_buf); + if(!zp) { + log_msg(LOG_ERR, "diff file commit without IXFR"); + if(thislog) + thislog->error = "error no IXFR parts"; + return 1; } - if(check_for_bad_serial(nsd->db, zone_buf, old_serial)) { + if(committed && check_for_bad_serial(db, zone_buf, old_serial)) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "skipping diff file commit with bad serial")); + zp->parts->root = RBTREE_NULL; + zp->parts->count = 0; + if(thislog) + thislog->error = "error bad serial"; return 1; } + for(i=0; i<num_parts; i++) { + struct diff_xfrpart *xp = diff_read_find_part(zp, i); + if(!xp || xp->id != id || xp->new_serial != new_serial) { + have_all_parts = 0; + } + } + if(!have_all_parts) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, + "skipping diff file commit without all parts")); + if(thislog) + thislog->error = "error missing parts"; + } - if(committed) + if(committed && have_all_parts) { - int is_axfr=0, delete_mode=0, rr_count=0, softfail=0; - const dname_type* apex = zonedb->apex->dname; - udb_ptr z; - - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "processing xfr: %s", zone_buf)); - if(nsd->db->udb) { - if(udb_base_get_userflags(nsd->db->udb) != 0) { - log_msg(LOG_ERR, "database corrupted, cannot update"); - xfrd_unlink_xfrfile(nsd, xfrfilenr); - exit(1); - } - /* all parts were checked by xfrd before commit */ - if(!udb_zone_search(nsd->db->udb, &z, dname_name(apex), - apex->name_size)) { - /* create it */ - if(!udb_zone_create(nsd->db->udb, &z, dname_name(apex), - apex->name_size)) { - /* out of disk space perhaps */ - log_msg(LOG_ERR, "could not udb_create_zone " - "%s, disk space full?", log_buf); - return 0; - } - } - /* set the udb dirty until we are finished applying changes */ - udb_base_set_userflags(nsd->db->udb, 1); + int is_axfr=0, delete_mode=0, rr_count=0; + off_t resume_pos; + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "processing xfr: %s", log_buf)); + resume_pos = ftello(in); + if(resume_pos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno)); + return 0; } - /* read and apply all of the parts */ for(i=0; i<num_parts; i++) { - int ret; + struct diff_xfrpart *xp = diff_read_find_part(zp, i); DEBUG(DEBUG_XFRD,2, (LOG_INFO, "processing xfr: apply part %d", (int)i)); - ret = apply_ixfr(nsd->db, in, zone_buf, new_serial, opt, - i, num_parts, &is_axfr, &delete_mode, - &rr_count, (nsd->db->udb?&z:NULL), &zonedb, - patname_buf, &num_bytes, &softfail); - if(ret == 0) { - log_msg(LOG_ERR, "bad ixfr packet part %d in diff file for %s", (int)i, zone_buf); - xfrd_unlink_xfrfile(nsd, xfrfilenr); - /* the udb is still dirty, it is bad */ - exit(1); - } else if(ret == 2) { - break; + if(!apply_ixfr(db, in, &xp->file_pos, zone_buf, new_serial, opt, + id, xp->seq_nr, num_parts, &is_axfr, &delete_mode, + &rr_count, child_count)) { + log_msg(LOG_ERR, "bad ixfr packet part %d in %s", (int)i, + opt->difffile); + mark_and_exit(opt, in, commitpos, log_buf); } } - if(nsd->db->udb) - udb_base_set_userflags(nsd->db->udb, 0); - /* read the final log_str: but do not fail on it */ - if(!diff_read_str(in, log_buf, sizeof(log_buf))) { - log_msg(LOG_ERR, "could not read log for transfer %s", - zone_buf); - snprintf(log_buf, sizeof(log_buf), "error reading log"); - } -#ifdef NSEC3 - if(zonedb) prehash_zone(nsd->db, zonedb); -#endif /* NSEC3 */ - zonedb->is_changed = 1; - if(nsd->db->udb) { - ZONE(&z)->is_changed = 1; - ZONE(&z)->mtime = time_end_0; - udb_zone_set_log_str(nsd->db->udb, &z, log_buf); - udb_zone_set_file_str(nsd->db->udb, &z, NULL); - udb_ptr_unlink(&z, nsd->db->udb); - } else { - zonedb->mtime = time_end_0; - if(zonedb->logstr) - region_recycle(nsd->db->region, zonedb->logstr, - strlen(zonedb->logstr)+1); - zonedb->logstr = region_strdup(nsd->db->region, log_buf); - if(zonedb->filename) - region_recycle(nsd->db->region, zonedb->filename, - strlen(zonedb->filename)+1); - zonedb->filename = NULL; - } - if(softfail && taskudb && !is_axfr) { - log_msg(LOG_ERR, "Failed to apply IXFR cleanly " - "(deletes nonexistent RRs, adds existing RRs). " - "Zone %s contents is different from master, " - "starting AXFR. Transfer %s", zone_buf, log_buf); - /* add/del failures in IXFR, get an AXFR */ - task_new_soainfo(taskudb, last_task, zonedb, 1); - } else { - if(taskudb) - task_new_soainfo(taskudb, last_task, zonedb, 0); - } - - if(1 <= verbosity) { - double elapsed = (double)(time_end_0 - time_start_0)+ - (double)((double)time_end_1 - -(double)time_start_1) / 1000000.0; - VERBOSITY(2, (LOG_INFO, "zone %s %s of %d bytes in %g seconds", - zone_buf, log_buf, num_bytes, elapsed)); + if(fseeko(in, resume_pos, SEEK_SET) == -1) { + log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno)); + return 0; } } else { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "skipping xfr: %s", log_buf)); } - return 1; -} -struct udb_base* task_file_create(const char* file) -{ - return udb_base_create_new(file, &namedb_walkfunc, NULL); + /* clean out the parts for the zone after the commit/rollback */ + zp->parts->root = RBTREE_NULL; + zp->parts->count = 0; + return 1; } static int -task_create_new_elem(struct udb_base* udb, udb_ptr* last, udb_ptr* e, - size_t sz, const dname_type* zname) -{ - if(!udb_ptr_alloc_space(e, udb, udb_chunk_type_task, sz)) { +store_ixfr_data(FILE *in, uint32_t len, struct diff_read_data* data, off_t* startpos) +{ + char zone_name[3072]; + struct diff_zone* zp; + struct diff_xfrpart* xp; + uint32_t new_serial, seq; + uint16_t id; + if(!diff_read_str(in, zone_name, sizeof(zone_name)) || + !diff_read_32(in, &new_serial) || + !diff_read_16(in, &id) || + !diff_read_32(in, &seq)) { + log_msg(LOG_INFO, "could not read ixfr store info: file format error"); return 0; } - if(udb_ptr_is_null(last)) { - udb_base_set_userdata(udb, e->data); - } else { - udb_rptr_set_ptr(&TASKLIST(last)->next, udb, e); + len -= sizeof(uint32_t)*3 + sizeof(uint16_t) + strlen(zone_name); + if(fseeko(in, len, SEEK_CUR) == -1) + log_msg(LOG_INFO, "fseek failed: %s", strerror(errno)); + /* store the info */ + zp = diff_read_find_zone(data, zone_name); + if(!zp) + zp = diff_read_insert_zone(data, zone_name); + xp = diff_read_find_part(zp, seq); + if(xp) { + log_msg(LOG_INFO, "discarding partial xfr part: %s %d", zone_name, seq); + /* overwrite with newer value (which probably relates to next commit) */ } - udb_ptr_set_ptr(last, udb, e); - - /* fill in tasklist item */ - udb_rel_ptr_init(&TASKLIST(e)->next); - TASKLIST(e)->size = sz; - TASKLIST(e)->oldserial = 0; - TASKLIST(e)->newserial = 0; - TASKLIST(e)->yesno = 0; - - if(zname) { - memmove(TASKLIST(e)->zname, zname, dname_total_size(zname)); + else { + xp = diff_read_insert_part(data, zp, seq); } + xp->new_serial = new_serial; + xp->id = id; + memmove(&xp->file_pos, startpos, sizeof(off_t)); return 1; } -void task_new_soainfo(struct udb_base* udb, udb_ptr* last, struct zone* z, - int gone) +static int +read_process_part(namedb_type* db, FILE *in, uint32_t type, + nsd_options_t* opt, struct diff_read_data* data, + struct diff_log** log, size_t child_count, off_t* startpos) { - /* calculate size */ - udb_ptr e; - size_t sz; - const dname_type* apex, *ns, *em; - if(!z || !z->apex || !domain_dname(z->apex)) - return; /* safety check */ - - DEBUG(DEBUG_IPC,1, (LOG_INFO, "nsd: add soa info for zone %s", - domain_to_string(z->apex))); - apex = domain_dname(z->apex); - sz = sizeof(struct task_list_d) + dname_total_size(apex); - if(z->soa_rrset && !gone) { - ns = domain_dname(rdata_atom_domain( - z->soa_rrset->rrs[0].rdatas[0])); - em = domain_dname(rdata_atom_domain( - z->soa_rrset->rrs[0].rdatas[1])); - sz += sizeof(uint32_t)*6 + sizeof(uint8_t)*2 - + ns->name_size + em->name_size; - } else { - ns = 0; - em = 0; - } + uint32_t len, len2; - /* create new task_list item */ - if(!task_create_new_elem(udb, last, &e, sz, apex)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add SOAINFO"); - return; + /* read length */ + if(!diff_read_32(in, &len)) + return 1; + /* read content */ + if(type == DIFF_PART_IXFR) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "part IXFR len %d", len)); + if(!store_ixfr_data(in, len, data, startpos)) + return 0; } - TASKLIST(&e)->task_type = task_soa_info; - - if(z->soa_rrset && !gone) { - uint32_t ttl = htonl(z->soa_rrset->rrs[0].ttl); - uint8_t* p = (uint8_t*)TASKLIST(&e)->zname; - p += dname_total_size(apex); - memmove(p, &ttl, sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, &ns->name_size, sizeof(uint8_t)); - p += sizeof(uint8_t); - memmove(p, dname_name(ns), ns->name_size); - p += ns->name_size; - memmove(p, &em->name_size, sizeof(uint8_t)); - p += sizeof(uint8_t); - memmove(p, dname_name(em), em->name_size); - p += em->name_size; - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[2]), - sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[3]), - sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[4]), - sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[5]), - sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[6]), - sizeof(uint32_t)); + else if(type == DIFF_PART_SURE) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "part SURE len %d", len)); + if(!read_sure_part(db, in, opt, data, log, child_count)) + return 0; + } else { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "unknown part %x len %d", type, len)); + return 0; } - udb_ptr_unlink(&e, udb); -} - -void task_process_sync(struct udb_base* taskudb) -{ - /* need to sync before other process uses the mmap? */ - DEBUG(DEBUG_IPC,1, (LOG_INFO, "task procsync %s size %d", - taskudb->fname, (int)taskudb->base_size)); - (void)taskudb; -} - -void task_remap(struct udb_base* taskudb) -{ - DEBUG(DEBUG_IPC,1, (LOG_INFO, "task remap %s size %d", - taskudb->fname, (int)taskudb->glob_data->fsize)); - udb_base_remap_process(taskudb); + /* read length */ + if(!diff_read_32(in, &len2)) + return 1; /* short read is OK */ + /* verify length */ + if(len != len2) + return 0; /* bad data is wrong */ + return 1; } -void task_clear(struct udb_base* taskudb) +/* + * Finds smallest offset in data structs + * returns 0 if no offsets in the data structs. + */ +static int +find_smallest_offset(struct diff_read_data* data, off_t* offset) { - udb_ptr t, n; - udb_ptr_new(&t, taskudb, udb_base_get_userdata(taskudb)); - udb_base_set_userdata(taskudb, 0); - udb_ptr_init(&n, taskudb); - while(!udb_ptr_is_null(&t)) { - udb_ptr_set_rptr(&n, taskudb, &TASKLIST(&t)->next); - udb_rptr_zero(&TASKLIST(&t)->next, taskudb); - udb_ptr_free_space(&t, taskudb, TASKLIST(&t)->size); - udb_ptr_set_ptr(&t, taskudb, &n); - } - udb_ptr_unlink(&t, taskudb); - udb_ptr_unlink(&n, taskudb); -} + int found_any = 0; + struct diff_zone* dz; + struct diff_xfrpart* dx; + off_t mem_offset, mem_fpos; -void task_new_expire(struct udb_base* udb, udb_ptr* last, - const struct dname* z, int expired) -{ - udb_ptr e; - if(!z) return; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add expire info for zone %s", - dname_to_string(z,NULL))); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d)+ - dname_total_size(z), z)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add expire"); - return; - } - TASKLIST(&e)->task_type = task_expire; - TASKLIST(&e)->yesno = expired; - udb_ptr_unlink(&e, udb); -} + if(!data || !data->zones) + return 0; + RBTREE_FOR(dz, struct diff_zone*, data->zones) + { + if(!dz->parts) + continue; + RBTREE_FOR(dx, struct diff_xfrpart*, dz->parts) + { + memmove(&mem_fpos, &dx->file_pos, sizeof(off_t)); -void task_new_check_zonefiles(udb_base* udb, udb_ptr* last, - const dname_type* zone) -{ - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task checkzonefiles")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) + - (zone?dname_total_size(zone):0), zone)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add check_zones"); - return; - } - TASKLIST(&e)->task_type = task_check_zonefiles; - TASKLIST(&e)->yesno = (zone!=NULL); - udb_ptr_unlink(&e, udb); -} + if(found_any) { + memmove(&mem_offset, offset, sizeof(off_t)); -void task_new_write_zonefiles(udb_base* udb, udb_ptr* last, - const dname_type* zone) -{ - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task writezonefiles")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) + - (zone?dname_total_size(zone):0), zone)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add writezones"); - return; + if(mem_fpos < mem_offset) + memmove(offset, &mem_fpos, sizeof(off_t)); + } else { + found_any = 1; + memmove(offset, &mem_fpos, sizeof(off_t)); + } + } } - TASKLIST(&e)->task_type = task_write_zonefiles; - TASKLIST(&e)->yesno = (zone!=NULL); - udb_ptr_unlink(&e, udb); -} -void task_new_set_verbosity(udb_base* udb, udb_ptr* last, int v) -{ - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task set_verbosity")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d), - NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add set_v"); - return; - } - TASKLIST(&e)->task_type = task_set_verbosity; - TASKLIST(&e)->yesno = v; - udb_ptr_unlink(&e, udb); + return found_any; } -#ifdef BIND8_STATS -void* task_new_stat_info(udb_base* udb, udb_ptr* last, struct nsdst* stat, +int +diff_read_file(namedb_type* db, nsd_options_t* opt, struct diff_log** log, size_t child_count) { - void* p; - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task stat_info")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d)+ - sizeof(*stat) + sizeof(stc_t)*child_count, NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add stati"); - return NULL; - } - TASKLIST(&e)->task_type = task_stat_info; - p = TASKLIST(&e)->zname; - memcpy(p, stat, sizeof(*stat)); - udb_ptr_unlink(&e, udb); - return p + sizeof(*stat); -} -#endif /* BIND8_STATS */ + const char* filename = opt->difffile; + FILE *df; + uint32_t type, timestamp[2], curr_timestamp[2]; + struct diff_read_data* data = diff_read_data_create(); + off_t startpos; -void -task_new_add_zone(udb_base* udb, udb_ptr* last, const char* zone, - const char* pattern) -{ - size_t zlen = strlen(zone); - size_t plen = strlen(pattern); - void *p; - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task addzone %s %s", zone, pattern)); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d)+ - zlen + 1 + plen + 1, NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add addz"); - return; + df = fopen(filename, "r"); + if(!df) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "could not open file %s for reading: %s", + filename, strerror(errno))); + region_destroy(data->region); + return 1; } - TASKLIST(&e)->task_type = task_add_zone; - p = TASKLIST(&e)->zname; - memcpy(p, zone, zlen+1); - memmove(p+zlen+1, pattern, plen+1); - udb_ptr_unlink(&e, udb); -} -void -task_new_del_zone(udb_base* udb, udb_ptr* last, const dname_type* dname) -{ - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task delzone %s", dname_to_string(dname, 0))); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) - +dname_total_size(dname), dname)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add delz"); - return; - } - TASKLIST(&e)->task_type = task_del_zone; - udb_ptr_unlink(&e, udb); -} + /* check timestamp */ + curr_timestamp[0] = (uint32_t) db->diff_timestamp.tv_sec; + curr_timestamp[1] = (uint32_t) db->diff_timestamp.tv_usec; -void task_new_add_key(udb_base* udb, udb_ptr* last, key_options_t* key) -{ - char* p; - udb_ptr e; - assert(key->name && key->algorithm && key->secret); - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task addkey")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) - +strlen(key->name)+1+strlen(key->algorithm)+1+ - strlen(key->secret)+1, NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add addk"); - return; + if(!diff_read_32(df, &type)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "difffile %s is empty", + filename)); + db->diff_skip = 0; + db->diff_pos = 0; } - TASKLIST(&e)->task_type = task_add_key; - p = (char*)TASKLIST(&e)->zname; - memmove(p, key->name, strlen(key->name)+1); - p+=strlen(key->name)+1; - memmove(p, key->algorithm, strlen(key->algorithm)+1); - p+=strlen(key->algorithm)+1; - memmove(p, key->secret, strlen(key->secret)+1); - udb_ptr_unlink(&e, udb); -} - -void task_new_del_key(udb_base* udb, udb_ptr* last, const char* name) -{ - char* p; - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task delkey")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) - +strlen(name)+1, NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add delk"); - return; + else if (!diff_read_32(df, ×tamp[0]) || + !diff_read_32(df, ×tamp[1])) { + log_msg(LOG_ERR, "difffile %s bad first part: no timestamp", + filename); + region_destroy(data->region); + return 0; } - TASKLIST(&e)->task_type = task_del_key; - p = (char*)TASKLIST(&e)->zname; - memmove(p, name, strlen(name)+1); - udb_ptr_unlink(&e, udb); -} - -void task_new_add_pattern(udb_base* udb, udb_ptr* last, pattern_options_t* p) -{ - region_type* temp; - buffer_type* buffer; - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task addpattern %s", p->pname)); - temp = region_create(xalloc, free); - buffer = buffer_create(temp, 4096); - pattern_options_marshal(buffer, p); - buffer_flip(buffer); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) - + buffer_limit(buffer), NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add addp"); - region_destroy(temp); - return; + else if (curr_timestamp[0] != timestamp[0] || + curr_timestamp[1] != timestamp[1]) { + /* new timestamp, no skipping */ + db->diff_timestamp.tv_sec = (time_t) timestamp[0]; + db->diff_timestamp.tv_usec = (suseconds_t) timestamp[1]; + + if (db->diff_skip) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "new timestamp on " + "difffile %s, restoring diff_skip and diff_pos " + "[old timestamp: %u.%u; new timestamp: %u.%u]", + filename, curr_timestamp[0], curr_timestamp[1], + timestamp[0], timestamp[1])); + db->diff_skip = 0; + db->diff_pos = 0; + } } - TASKLIST(&e)->task_type = task_add_pattern; - TASKLIST(&e)->yesno = buffer_limit(buffer); - memmove(TASKLIST(&e)->zname, buffer_begin(buffer), - buffer_limit(buffer)); - udb_ptr_unlink(&e, udb); - region_destroy(temp); -} -void task_new_del_pattern(udb_base* udb, udb_ptr* last, const char* name) -{ - char* p; - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task delpattern %s", name)); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) - +strlen(name)+1, NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add delp"); - return; + /* Always seek, to diff_pos or to beginning of the file. */ + if (fseeko(df, 0, SEEK_SET)==-1) { + log_msg(LOG_INFO, "could not fseeko file %s: %s.", filename, + strerror(errno)); + region_destroy(data->region); + return 0; } - TASKLIST(&e)->task_type = task_del_pattern; - p = (char*)TASKLIST(&e)->zname; - memmove(p, name, strlen(name)+1); - udb_ptr_unlink(&e, udb); -} - -void task_new_opt_change(udb_base* udb, udb_ptr* last, nsd_options_t* opt) -{ - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task opt_change")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d), - NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add o_c"); - return; + if(db->diff_skip) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "skip diff file")); + if(fseeko(df, db->diff_pos, SEEK_SET)==-1) { + log_msg(LOG_INFO, "could not fseeko file %s: %s. " + "Reread from start.", filename, + strerror(errno)); + } } - TASKLIST(&e)->task_type = task_opt_change; -#ifdef RATELIMIT - TASKLIST(&e)->oldserial = opt->rrl_ratelimit; - TASKLIST(&e)->newserial = opt->rrl_whitelist_ratelimit; - TASKLIST(&e)->yesno = (uint64_t) opt->rrl_slip; -#else - (void)opt; -#endif - udb_ptr_unlink(&e, udb); -} -int -task_new_apply_xfr(udb_base* udb, udb_ptr* last, const dname_type* dname, - uint32_t old_serial, uint32_t new_serial, uint64_t filenumber) -{ - udb_ptr e; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task apply_xfr")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) - +dname_total_size(dname), dname)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add applyxfr"); + startpos = ftello(df); + if(startpos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno)); + region_destroy(data->region); return 0; } - TASKLIST(&e)->oldserial = old_serial; - TASKLIST(&e)->newserial = new_serial; - TASKLIST(&e)->yesno = filenumber; - TASKLIST(&e)->task_type = task_apply_xfr; - udb_ptr_unlink(&e, udb); - return 1; -} - -void -task_process_expire(namedb_type* db, struct task_list_d* task) -{ - uint8_t ok; - zone_type* z = namedb_find_zone(db, task->zname); - assert(task->task_type == task_expire); - if(!z) { - DEBUG(DEBUG_IPC, 1, (LOG_WARNING, "zone %s %s but not in zonetree", - dname_to_string(task->zname, NULL), - task->yesno?"expired":"unexpired")); - return; - } - DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: expire task zone %s %s", - dname_to_string(task->zname,0), - task->yesno?"expired":"unexpired")); - /* find zone, set expire flag */ - ok = !task->yesno; - /* only update zone->is_ok if needed to minimize copy-on-write - * of memory pages shared after fork() */ - if(ok && !z->is_ok) - z->is_ok = 1; - else if(!ok && z->is_ok) - z->is_ok = 0; -} -static void -task_process_set_verbosity(struct task_list_d* task) -{ - DEBUG(DEBUG_IPC,1, (LOG_INFO, "verbosity task %d", (int)task->yesno)); - verbosity = task->yesno; -} + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "start of diff file read at pos %u", + (uint32_t) db->diff_pos)); + while(diff_read_32(df, &type)) + { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "iter loop")); + + /* read timestamp */ + if(!diff_read_32(df, ×tamp[0]) || + !diff_read_32(df, ×tamp[1])) { + log_msg(LOG_INFO, "could not read timestamp: %s.", + strerror(errno)); + region_destroy(data->region); + return 0; + } -static void -task_process_checkzones(struct nsd* nsd, udb_base* udb, udb_ptr* last_task, - struct task_list_d* task) -{ - /* on SIGHUP check if zone-text-files changed and if so, - * reread. When from xfrd-reload, no need to fstat the files */ - if(task->yesno) { - zone_options_t* zo = zone_options_find(nsd->options, - task->zname); - if(zo) - namedb_check_zonefile(nsd, udb, last_task, zo); - } else { - /* check all zones */ - namedb_check_zonefiles(nsd, nsd->options, udb, last_task); + if(!read_process_part(db, df, type, opt, data, log, + child_count, &startpos)) + { + log_msg(LOG_INFO, "error processing diff file"); + region_destroy(data->region); + return 0; + } + startpos = ftello(df); + if(startpos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno)); + region_destroy(data->region); + return 0; + } } -} + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "end of diff file read")); -static void -task_process_writezones(struct nsd* nsd, struct task_list_d* task) -{ - if(task->yesno) { - zone_options_t* zo = zone_options_find(nsd->options, - task->zname); - if(zo) - namedb_write_zonefile(nsd, zo); + if(find_smallest_offset(data, &db->diff_pos)) { + /* can skip to the first unused element */ + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "next time skip diff file")); + db->diff_skip = 1; } else { - namedb_write_zonefiles(nsd, nsd->options); + /* all processed, can skip to here next time */ + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "next time skip diff file")); + db->diff_skip = 1; + db->diff_pos = ftello(df); + if(db->diff_pos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", + strerror(errno)); + db->diff_skip = 0; + } } -} -static void -task_process_add_zone(struct nsd* nsd, udb_base* udb, udb_ptr* last_task, - struct task_list_d* task) -{ - zone_type* z; - const dname_type* zdname; - const char* zname = (const char*)task->zname; - const char* pname = zname + strlen(zname)+1; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "addzone task %s %s", zname, pname)); - zdname = dname_parse(nsd->db->region, zname); - if(!zdname) { - log_msg(LOG_ERR, "can not parse zone name %s", zname); - return; - } - /* create zone */ - z = find_or_create_zone(nsd->db, zdname, nsd->options, zname, pname); - if(!z) { - region_recycle(nsd->db->region, (void*)zdname, - dname_total_size(zdname)); - log_msg(LOG_ERR, "can not add zone %s %s", zname, pname); - return; - } - /* if zone is empty, attempt to read the zonefile from disk (if any) */ - if(!z->soa_rrset && z->opts->pattern->zonefile) { - namedb_read_zonefile(nsd, z, udb, last_task); - } + region_destroy(data->region); + fclose(df); + return 1; } -static void -task_process_del_zone(struct nsd* nsd, struct task_list_d* task) +static int diff_broken(FILE *df, off_t* break_pos) { - zone_type* zone; - zone_options_t* zopt; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "delzone task %s", dname_to_string( - task->zname, NULL))); - zone = namedb_find_zone(nsd->db, task->zname); - if(!zone) - return; + uint32_t type, len, len2; + *break_pos = ftello(df); -#ifdef NSEC3 - nsec3_hash_tree_clear(zone); -#endif - delete_zone_rrs(nsd->db, zone); - if(nsd->db->udb) { - udb_ptr udbz; - if(udb_zone_search(nsd->db->udb, &udbz, dname_name(task->zname), - task->zname->name_size)) { - udb_zone_delete(nsd->db->udb, &udbz); - udb_ptr_unlink(&udbz, nsd->db->udb); + /* try to read and validate parts of the file */ + while(diff_read_32(df, &type)) /* cannot read type is no error, normal EOF */ + { + /* check type */ + if(type != DIFF_PART_IXFR && type != DIFF_PART_SURE) + return 1; + /* check length */ + if(!diff_read_32(df, &len)) + return 1; /* EOF inside the part is error */ + if(fseeko(df, len, SEEK_CUR) == -1) + { + log_msg(LOG_INFO, "fseeko failed: %s", strerror(errno)); + return 1; } + /* fseek clears EOF flag, but try reading length value, + if EOF, the part is truncated */ + if(!diff_read_32(df, &len2)) + return 1; + if(len != len2) + return 1; /* bad part, lengths must agree */ + /* this part is ok */ + *break_pos = ftello(df); } -#ifdef NSEC3 - nsec3_clear_precompile(nsd->db, zone); - zone->nsec3_param = NULL; -#endif /* NSEC3 */ - - /* remove from zonetree, apex, soa */ - zopt = zone->opts; - namedb_zone_delete(nsd->db, zone); - /* remove from options (zone_list already edited by xfrd) */ - zone_options_delete(nsd->options, zopt); -} - -static void -task_process_add_key(struct nsd* nsd, struct task_list_d* task) -{ - key_options_t key; - key.name = (char*)task->zname; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "addkey task %s", key.name)); - key.algorithm = key.name + strlen(key.name)+1; - key.secret = key.algorithm + strlen(key.algorithm)+1; - key_options_add_modify(nsd->options, &key); - memset(key.secret, 0xdd, strlen(key.secret)); /* wipe secret */ -} - -static void -task_process_del_key(struct nsd* nsd, struct task_list_d* task) -{ - char* name = (char*)task->zname; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "delkey task %s", name)); - /* this is reload and nothing is using the TSIG key right now */ - key_options_remove(nsd->options, name); -} - -static void -task_process_add_pattern(struct nsd* nsd, struct task_list_d* task) -{ - region_type* temp = region_create(xalloc, free); - buffer_type buffer; - pattern_options_t *pat; - buffer_create_from(&buffer, task->zname, task->yesno); - pat = pattern_options_unmarshal(temp, &buffer); - DEBUG(DEBUG_IPC,1, (LOG_INFO, "addpattern task %s", pat->pname)); - pattern_options_add_modify(nsd->options, pat); - region_destroy(temp); -} - -static void -task_process_del_pattern(struct nsd* nsd, struct task_list_d* task) -{ - char* name = (char*)task->zname; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "delpattern task %s", name)); - pattern_options_remove(nsd->options, name); + return 0; } -static void -task_process_opt_change(struct nsd* nsd, struct task_list_d* task) +void diff_snip_garbage(namedb_type* db, nsd_options_t* opt) { - DEBUG(DEBUG_IPC,1, (LOG_INFO, "optchange task")); -#ifdef RATELIMIT - nsd->options->rrl_ratelimit = task->oldserial; - nsd->options->rrl_whitelist_ratelimit = task->newserial; - nsd->options->rrl_slip = task->yesno; - rrl_set_limit(nsd->options->rrl_ratelimit, nsd->options->rrl_whitelist_ratelimit, - nsd->options->rrl_slip); -#else - (void)nsd; (void)task; -#endif -} + off_t break_pos; + const char* filename = opt->difffile; + FILE *df; -static void -task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr *last_task, - udb_ptr* task) -{ - /* we have to use an udb_ptr task here, because the apply_xfr procedure - * appends soa_info which may remap and change the pointer. */ - zone_type* zone; - FILE* df; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "applyxfr task %s", dname_to_string( - TASKLIST(task)->zname, NULL))); - zone = namedb_find_zone(nsd->db, TASKLIST(task)->zname); - if(!zone) { - /* assume the zone has been deleted and a zone transfer was - * still waiting to be processed */ - xfrd_unlink_xfrfile(nsd, TASKLIST(task)->yesno); - return; - } - /* apply the XFR */ - /* oldserial, newserial, yesno is filenumber */ - df = xfrd_open_xfrfile(nsd, TASKLIST(task)->yesno, "r"); + /* open file here and keep open, so it cannot change under our nose */ + df = fopen(filename, "r+"); if(!df) { - /* could not open file to update */ - /* there is no reply to xfrd failed-update, - * because xfrd has a scan for apply-failures. */ - xfrd_unlink_xfrfile(nsd, TASKLIST(task)->yesno); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "could not open file %s for garbage collecting: %s", + filename, strerror(errno))); return; } - /* read and apply zone transfer */ - if(!apply_ixfr_for_zone(nsd, zone, df, nsd->options, udb, - last_task, TASKLIST(task)->yesno)) { - /* there is no reply to xfrd failed-update, - * because xfrd has a scan for apply-failures. */ + /* and skip into file, since nsd does not read anything before the pos */ + if(db->diff_skip) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "garbage collect skip diff file")); + if(fseeko(df, db->diff_pos, SEEK_SET)==-1) { + log_msg(LOG_INFO, "could not fseeko file %s: %s.", + filename, strerror(errno)); + fclose(df); + return; + } + } + + /* detect break point */ + if(diff_broken(df, &break_pos)) + { + /* snip off at break_pos */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "snipping off trailing partial part of %s", + filename)); + if(ftruncate(fileno(df), break_pos) == -1) + log_msg(LOG_ERR, "ftruncate %s failed: %s", + filename, strerror(errno)); } fclose(df); - xfrd_unlink_xfrfile(nsd, TASKLIST(task)->yesno); -} - - -void task_process_in_reload(struct nsd* nsd, udb_base* udb, udb_ptr *last_task, - udb_ptr* task) -{ - switch(TASKLIST(task)->task_type) { - case task_expire: - task_process_expire(nsd->db, TASKLIST(task)); - break; - case task_check_zonefiles: - task_process_checkzones(nsd, udb, last_task, TASKLIST(task)); - break; - case task_write_zonefiles: - task_process_writezones(nsd, TASKLIST(task)); - break; - case task_set_verbosity: - task_process_set_verbosity(TASKLIST(task)); - break; - case task_add_zone: - task_process_add_zone(nsd, udb, last_task, TASKLIST(task)); - break; - case task_del_zone: - task_process_del_zone(nsd, TASKLIST(task)); - break; - case task_add_key: - task_process_add_key(nsd, TASKLIST(task)); - break; - case task_del_key: - task_process_del_key(nsd, TASKLIST(task)); - break; - case task_add_pattern: - task_process_add_pattern(nsd, TASKLIST(task)); - break; - case task_del_pattern: - task_process_del_pattern(nsd, TASKLIST(task)); - break; - case task_opt_change: - task_process_opt_change(nsd, TASKLIST(task)); - break; - case task_apply_xfr: - task_process_apply_xfr(nsd, udb, last_task, task); - break; - default: - log_msg(LOG_WARNING, "unhandled task in reload type %d", - (int)TASKLIST(task)->task_type); - break; - } - udb_ptr_free_space(task, udb, TASKLIST(task)->size); } |