if (gettimeofday(&tv, NULL) != 0) {
log_msg(LOG_ERR, "could not set timestamp for %s: %s",
zone, strerror(errno));
}
/* 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. */
/* this routine determines if below a domain there exist names with
* data (is_existing) or no names below the domain have data.
*/
static int
has_data_below(domain_type* top)
{
domain_type* d = 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)) {
if(d->is_existing)
return 1;
d = domain_next(d);
}
return 0;
}
/** check if domain with 0 rrsets has become empty (nonexist) */
static domain_type*
rrset_zero_nonexist_check(domain_type* domain, domain_type* ce)
{
/* 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(p == ce || has_data_below(p))
return p;
p->is_existing = 0;
/* fixup wildcard child of parent */
if(p->parent &&
p->parent->wildcard_child_closest_match == p)
p->parent->wildcard_child_closest_match = domain_previous_existing_child(p);
p = p->parent;
}
}
}
return NULL;
}
/** remove rrset. Adjusts zone params. Does not remove domain */
static void
rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset)
{
int i;
/* find previous */
rrset_type** pp = &domain->rrsets;
while(*pp && *pp != rrset) {
pp = &( (*pp)->next );
}
if(!*pp) {
/* rrset does not exist for domain */
return;
}
*pp = rrset->next;
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete rrset of %s type %s",
domain_to_string(domain),
rrtype_to_string(rrset_rrtype(rrset))));
/* is this a SOA rrset ? */
if(rrset->zone->soa_rrset == rrset) {
rrset->zone->soa_rrset = 0;
}
if(rrset->zone->ns_rrset == rrset) {
rrset->zone->ns_rrset = 0;
}
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) {
rrset->zone->is_secure = 0;
break;
}
}
}
/* 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));
}
static int
rdatas_equal(rdata_atom_type *a, rdata_atom_type *b, int num, uint16_t type,
int* rdnum, char** reason)
{
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++)
{
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";
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";
return 0;
}
/* check data */
if(memcmp(a[k].data+1, b[k].data+1, a[k].data[0])!=0) {
*rdnum = k;
*reason = "rdata data";
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(domain_dname(rrset->rrs[i].owner),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(domain_dname(rrset->rrs[i].owner),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(domain_dname(rrset->rrs[i].owner),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(domain_dname(rrset->rrs[i].owner),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)
{
int i, rd;
char* reason;
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))
{
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)
{
/* 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, rr, 0);
/* 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_wc &&
domain->nsec3->hash_wc->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_wc->hash.node);
zone_del_domain_in_hash_tree(zone->wchashtree,
&domain->nsec3->hash_wc->wc.node);
}
if(domain != zone->apex && domain->nsec3 &&
domain->nsec3->ds_parent_hash &&
domain->nsec3->ds_parent_hash->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->ds_parent_hash->node);
}
}
/* 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)
{
/* 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)) {
if(!zone->nsec3_last) {
/* all nsec3s have previously been deleted, but
* we have nsec3 parameters, set it up again from
* being cleared. */
nsec3_precompile_newparam(db, 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, NULL, 0);
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 */
/* 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;
}
#endif /* NSEC3 */
}
#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;
}
}
nsec3_add_rr_trigger(db, &rrset->rrs[rrset->rr_count - 1], zone);
#endif /* NSEC3 */
return 1;
}
static zone_type*
find_or_create_zone(namedb_type* db, const dname_type* zone_name,
struct nsd_options* opt, const char* zstr, const char* patname)
{
zone_type* zone;
struct zone_options* 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;
}
}
zone = namedb_zone_create(db, zone_name, zopt);
return zone;
}
void
delete_zone_rrs(namedb_type* db, zone_type* zone)
{
rrset_type *rrset;
domain_type *domain = zone->apex, *next;
int nonexist_check = 0;
/* go through entire tree below the zone apex (incl subzones) */
while(domain && domain_is_subdomain(domain, zone->apex))
{
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete zone visit %s",
domain_to_string(domain)));
/* 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 deletions have created nonexisting domain entries,
* but after deleting domains so the checks are faster */
if(nonexist_check) {
domain_type* ce = NULL; /* for speeding up has_data_below */
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)
ce = rrset_zero_nonexist_check(domain, ce);
domain = domain_next(domain);
}
}
/* 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. */
/* read ixfr packet RRs and apply to in memory db */
if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_XXFR) {
log_msg(LOG_ERR, "could not read type or wrong type");
return 0;
}
/* 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;
/* only answer section is really used, question, additional and
authority section RRs are skipped */
qcount = QDCOUNT(packet);
ancount = ANCOUNT(packet);
buffer_skip(packet, QHEADERSZ);
/* qcount should be 0 or 1 really, ancount limited by 64k packet */
if(qcount > 64 || ancount > 65530) {
log_msg(LOG_ERR, "RR count impossibly high");
region_destroy(region);
return 0;
}
if (type == TYPE_SOA) {
size_t position;
uint32_t serial;
position = buffer_position(packet);
if (!packet_skip_dname(packet) ||
!packet_skip_dname(packet) ||
buffer_remaining(packet) < sizeof(uint32_t) * 5)
{
log_msg(LOG_ERR, "bad xfr SOA RR formerr.");
region_destroy(region);
return 0;
}
serial = buffer_read_u32(packet);
buffer_set_position(packet, position);
/* first RR: check if SOA and correct zone & serialno */
if (*rr_count == 0) {
assert(!*is_axfr);
assert(!*delete_mode);
if (klass != CLASS_IN) {
log_msg(LOG_ERR, "first RR not SOA IN");
region_destroy(region);
return 0;
}
if(dname_compare(domain_dname(zone->apex), owner) != 0) {
log_msg(LOG_ERR, "SOA dname not equal to zone %s",
domain_to_string(zone->apex));
region_destroy(region);
return 0;
}
if(serial != serialno) {
log_msg(LOG_ERR, "SOA serial %u different from commit %u",
(unsigned)serial, (unsigned)serialno);
region_destroy(region);
return 0;
}
buffer_skip(packet, rrlen);
continue;
} else if (*rr_count == 1) {
assert(!*is_axfr);
assert(!*delete_mode);
/* if the serial no of the SOA equals the serialno, then AXFR */
if (serial == serialno)
goto axfr;
*delete_mode = 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(!domain_find_rrset(zone->apex, zone, TYPE_SOA)) {
log_msg(LOG_ERR, "%s SOA serial %u is not "
"in memory, skip IXFR", domain_to_string(zone->apex), serialno);
region_destroy(region);
/* break out and stop the IXFR, ignore it */
return 2;
}
if(ixfr_store)
ixfr_store_add_oldsoa(ixfr_store, ttl, packet, rrlen);
} else if (!*is_axfr) {
/* do not delete final SOA RR for IXFR */
if (i == ancount - 1 && seq_nr == seq_total - 1) {
if (ixfr_store) {
ixfr_store_add_newsoa(ixfr_store, ttl, packet, rrlen);
}
*delete_mode = 0;
buffer_skip(packet, rrlen);
continue;
} else
*delete_mode = !*delete_mode;
if (ixfr_store && *delete_mode) {
ixfr_store_add_newsoa(ixfr_store, ttl, packet, rrlen);
ixfr_store_finish(ixfr_store, nsd, NULL);
ixfr_store_start(zone, ixfr_store);
ixfr_store_add_oldsoa(ixfr_store, ttl, packet, rrlen);
}
/* switch from delete-part to add-part and back again,
just before soa - so it gets deleted and added too */
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s IXFRswapdel count %d, ax %d, delmode %d",
domain_to_string(zone->apex), *rr_count, *is_axfr, *delete_mode));
}
} else {
if (*rr_count == 0) {
log_msg(LOG_ERR, "first RR not SOA IN");
region_destroy(region);
return 0;
/* second RR: if not SOA: this is an AXFR; delete all zone contents */
} else if (*rr_count == 1) {
axfr:
*is_axfr = 1;
#ifdef NSEC3
nsec3_clear_precompile(nsd->db, zone);
zone->nsec3_param = NULL;
#endif
delete_zone_rrs(nsd->db, zone);
if(ixfr_store) {
ixfr_store_cancel(ixfr_store);
ixfr_store_delixfrs(zone);
}
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s sawAXFR count %d, ax %d, delmode %d",
domain_to_string(zone->apex), *rr_count, *is_axfr, *delete_mode));
}
}
/* read zone name and serial */
if(!diff_read_32(in, &type)) {
log_msg(LOG_ERR, "diff file too short");
return 0;
}
if(type != DIFF_PART_XFRF) {
log_msg(LOG_ERR, "xfr file has wrong format");
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))) {
log_msg(LOG_ERR, "diff file bad commit part");
return 0;
}
/* has been read in completely */
if(strcmp(zone_buf, domain_to_string(zone->apex)) != 0) {
log_msg(LOG_ERR, "file %s does not match task %s",
zone_buf, domain_to_string(zone->apex));
return 0;
}
switch(committed) {
case DIFF_NOT_COMMITTED:
log_msg(LOG_ERR, "diff file %s was not committed", zone_buf);
return 0;
case DIFF_CORRUPT:
log_msg(LOG_ERR, "diff file %s was corrupt", zone_buf);
return 0;
case DIFF_INCONSISTENT:
log_msg(LOG_ERR, "diff file %s was inconsistent", zone_buf);
return 0;
case DIFF_VERIFIED:
log_msg(LOG_INFO, "diff file %s already verified", zone_buf);
break;
default:
break;
}
if(num_parts == 0) {
log_msg(LOG_ERR, "diff file %s was not completed", zone_buf);
return 0;
}
if(check_for_bad_serial(nsd->db, zone_buf, old_serial)) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR,
"skipping diff file commit with bad serial"));
return 1;
}
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 && hint == soainfo_ok) {
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;
}
/* 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;
}
TASKLIST(&e)->task_type = task_soa_info;
TASKLIST(&e)->yesno = (uint64_t)hint;
if(z->soa_rrset && hint == soainfo_ok) {
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));
}
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;
}
static void
task_process_checkzones(struct nsd* nsd, udb_base* taskudb, 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) {
struct zone_options* zo = zone_options_find(nsd->options,
task->zname);
if(zo)
namedb_check_zonefile(nsd, taskudb, last_task, zo);
} else {
/* check all zones */
namedb_check_zonefiles(nsd, nsd->options, taskudb, last_task);
}
}
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);
}
#ifdef USE_ZONE_STATS
static void
task_process_zonestat_inc(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
struct task_list_d* task)
{
DEBUG(DEBUG_IPC,1, (LOG_INFO, "zonestat_inc task %u", (unsigned)task->oldserial));
nsd->zonestatdesired = (unsigned)task->oldserial;
/* send echo to xfrd to increment on its end */
task_new_zonestat_inc(udb, last_task, nsd->zonestatdesired);
}
#endif
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 */
return;
}
/* apply the XFR */
/* oldserial, newserial, yesno is filenumber */
df = xfrd_open_xfrfile(nsd, TASKLIST(task)->yesno, "r");
if(!df) {
/* could not open file to update */
/* soainfo_gone will be communicated from server_reload, unless
preceding updates have been applied */
zone->is_skipped = 1;
return;
}
/* read and apply zone transfer */
if(!apply_ixfr_for_zone(nsd, zone, df, nsd->options, udb,
last_task, TASKLIST(task)->yesno)) {
/* soainfo_gone will be communicated from server_reload, unless
preceding updates have been applied */
zone->is_skipped = 1;
}
fclose(df);
}
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;
#ifdef USE_ZONE_STATS
case task_zonestat_inc:
task_process_zonestat_inc(nsd, udb, last_task, TASKLIST(task));
break;
#endif
case task_apply_xfr:
task_process_apply_xfr(nsd, udb, last_task, task);
break;
case task_add_cookie_secret:
task_process_add_cookie_secret(nsd, TASKLIST(task));
break;
case task_drop_cookie_secret:
task_process_drop_cookie_secret(nsd, TASKLIST(task));
break;
case task_activate_cookie_secret:
task_process_activate_cookie_secret(nsd, TASKLIST(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);
}