#include "config.h"
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <inttypes.h>
#include "difffile.h"
#include "xfrd-disk.h"
#include "util.h"
#include "packet.h"
#include "rdata.h"
#include "udb.h"
#include "nsec3.h"
#include "nsd.h"
#include "rrl.h"
#include "ixfr.h"
#include "zonec.h"
#include "xfrd-catalog-zones.h"
static int
write_64(FILE *out, uint64_t val)
{
return write_data(out, &val, sizeof(val));
}
static int
write_32(FILE *out, uint32_t val)
{
val = htonl(val);
return write_data(out, &val, sizeof(val));
}
static int
write_8(FILE *out, uint8_t val)
{
return write_data(out, &val, sizeof(val));
}
static int
write_str(FILE *out, const char* str)
{
uint32_t len = strlen(str);
if(!write_32(out, len))
return 0;
return write_data(out, str, len);
}
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)
{
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));
return;
}
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) ||
!write_32(df, 0) ||
!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;
}
}
if(!write_32(df, DIFF_PART_XXFR) ||
!write_32(df, len) ||
!write_data(df, data, len) ||
!write_32(df, len))
{
log_msg(LOG_ERR, "could not write transfer %s file %lld: %s",
zone, (long long)filenumber, strerror(errno));
}
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)
{
struct timeval tv;
FILE* df;
if (gettimeofday(&tv, NULL) != 0) {
log_msg(LOG_ERR, "could not set timestamp for %s: %s",
zone, strerror(errno));
}
df = xfrd_open_xfrfile(nsd, filenumber, "r+");
if(!df) {
log_msg(LOG_ERR, "could not open transfer %s file %lld: %s",
zone, (long long)filenumber, strerror(errno));
return;
}
if(!write_32(df, DIFF_PART_XFRF) ||
!write_8(df, commit) ||
!write_32(df, num_parts) ||
!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))
{
log_msg(LOG_ERR, "could not write transfer %s file %lld: %s",
zone, (long long)filenumber, strerror(errno));
fclose(df);
return;
}
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;
}
fflush(df);
fclose(df);
}
void
diff_update_commit(
const char* zone, uint8_t commit, struct nsd* nsd, uint64_t filenumber)
{
FILE *df;
assert(zone != NULL);
assert(nsd != NULL);
assert(commit == DIFF_NOT_COMMITTED ||
commit == DIFF_COMMITTED ||
commit == DIFF_CORRUPT ||
commit == DIFF_INCONSISTENT ||
commit == DIFF_VERIFIED);
df = xfrd_open_xfrfile(nsd, filenumber, "r+");
if(!df) {
log_msg(LOG_ERR, "could not open transfer %s file %lld: %s",
zone, (long long)filenumber, strerror(errno));
return;
}
if(!write_32(df, DIFF_PART_XFRF) || !write_8(df, commit)) {
log_msg(LOG_ERR, "could not write transfer %s file %lld: %s",
zone, (long long)filenumber, strerror(errno));
fclose(df);
return;
}
fflush(df);
fclose(df);
}
int
diff_read_64(FILE *in, uint64_t* result)
{
if (fread(result, sizeof(*result), 1, in) == 1) {
return 1;
} else {
return 0;
}
}
int
diff_read_32(FILE *in, uint32_t* result)
{
if (fread(result, sizeof(*result), 1, in) == 1) {
*result = ntohl(*result);
return 1;
} else {
return 0;
}
}
int
diff_read_8(FILE *in, uint8_t* result)
{
if (fread(result, sizeof(*result), 1, in) == 1) {
return 1;
} else {
return 0;
}
}
int
diff_read_str(FILE* in, char* buf, size_t len)
{
uint32_t disklen;
if(!diff_read_32(in, &disklen))
return 0;
if(disklen >= len)
return 0;
if(fread(buf, disklen, 1, in) != 1)
return 0;
buf[disklen] = 0;
return 1;
}
static void
add_rr_to_recyclebin(namedb_type* db, rr_type* rr)
{
region_recycle(db->region, rr, sizeof(rr_type) + rr->rdlength);
}
static int
has_data_below(domain_type* top)
{
domain_type* d = top;
assert(d != NULL);
d = domain_next(d);
while(d != NULL && domain_is_subdomain(d, top)) {
if(d->is_existing)
return 1;
d = domain_next(d);
}
return 0;
}
static domain_type*
rrset_zero_nonexist_check(domain_type* domain, domain_type* ce)
{
if(domain->rrsets == 0) {
if(!has_data_below(domain)) {
domain_type* p = domain;
while(p != NULL && p->rrsets == 0) {
if(p == ce || has_data_below(p))
return p;
p->is_existing = 0;
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;
}
static void
rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset)
{
int i;
rrset_type** pp = &domain->rrsets;
while(*pp && *pp != rrset) {
pp = &( (*pp)->next );
}
if(!*pp) {
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))));
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;
}
}
}
for (i = 0; i < rrset->rr_count; ++i)
add_rr_to_recyclebin(db, rrset->rrs[i]);
#ifndef PACKED_STRUCTS
region_recycle(db->region, rrset->rrs, sizeof(rr_type*) * rrset->rr_count);
rrset->rr_count = 0;
#endif
region_recycle(db->region, rrset, sizeof(rrset_type)
#ifdef PACKED_STRUCTS
+ rrset->rr_count*sizeof(rr_type*)
#endif
);
}
static int
rdatas_equal(const rr_type *rr1, const rr_type *rr2, uint16_t type,
int* rdnum, char** reason)
{
size_t offset = 0;
const nsd_type_descriptor_type *descriptor;
if(rr1->rdlength != rr2->rdlength) {
*rdnum = 0;
*reason = "rr length different";
return 0;
}
assert(rr1->rdlength == rr2->rdlength);
if (type == TYPE_SOA) {
offset = 2 * sizeof(void*);
if(rr1->rdlength != offset + 20) {
*rdnum = 0;
*reason = "invalid SOA length";
return 0;
}
assert(rr1->rdlength == offset + 20);
if (memcmp(rr1->rdata + offset, rr2->rdata + offset, 4) == 0)
return 1;
*rdnum = 2;
*reason = "rdata data";
return 0;
}
if (memcmp(rr1->rdata, rr2->rdata, rr1->rdlength) == 0)
return 1;
descriptor = nsd_type_descriptor(type);
for (size_t i=0; i < descriptor->rdata.length && offset < rr1->rdlength; i++) {
uint16_t field_len1, field_len2;
struct domain *domain1, *domain2;
if(offset == rr1->rdlength &&
descriptor->rdata.fields[i].is_optional)
break;
if(!lookup_rdata_field_entry(descriptor, i, rr1, offset,
&field_len1, &domain1)) {
*rdnum = i;
*reason = "malformed field rr1";
return 0;
}
if(!lookup_rdata_field_entry(descriptor, i, rr2, offset,
&field_len2, &domain2)) {
*rdnum = i;
*reason = "malformed field rr2";
return 0;
}
if(domain1 && domain2) {
const struct dname *dname1, *dname2;
dname1 = domain_dname(domain1);
dname2 = domain_dname(domain2);
if (dname_compare(dname1, dname2) != 0) {
*rdnum = i;
*reason = "dname data";
return 0;
}
offset += field_len1;
continue;
} else if(descriptor->rdata.fields[i].length ==
RDATA_LITERAL_DNAME) {
uint8_t length1, length2;
const uint8_t *name1 = rr1->rdata + offset;
const uint8_t *name2 = rr2->rdata + offset;
if(field_len1 != field_len2) {
*rdnum = i;
*reason = "literal dname field len";
return 0;
}
length1 = buf_dname_length(name1, rr1->rdlength - offset);
length2 = buf_dname_length(name2, rr2->rdlength - offset);
if (!length1 || !length2 || length1 != length2) {
*rdnum = i;
*reason = "literal dname len";
return 0;
}
if (!dname_equal_nocase((uint8_t*)name1, (uint8_t*)name2, length1)) {
*rdnum = i;
*reason = "literal dname data";
return 0;
}
offset += field_len1;
continue;
}
if(field_len1 != field_len2) {
*rdnum = i;
*reason = "rdata len";
return 0;
}
if(memcmp(rr1->rdata+offset, rr2->rdata+offset, field_len1) != 0) {
*rdnum = i;
*reason = "rdata data";
return 0;
}
offset += field_len1;
}
return 1;
}
static void
debug_find_rr_num(rrset_type* rrset, uint16_t type, uint16_t klass,
rr_type *rr)
{
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));
continue;
}
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);
continue;
}
if (rrset->rrs[i]->rdlength != rr->rdlength) {
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) rr->rdlength, i,
(unsigned) rrset->rrs[i]->rdlength);
}
if (!rdatas_equal(rr, rrset->rrs[i], 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,
rr_type *rr, 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]->rdlength == rr->rdlength &&
rdatas_equal(rrset->rrs[i], rr, type,
&rd, &reason))
{
return i;
}
}
if (!add) {
debug_find_rr_num(rrset, type, klass, rr);
}
return -1;
}
#ifdef NSEC3
static void
nsec3_delete_rr_trigger(namedb_type* db, rr_type* rr, zone_type* zone)
{
if(!zone->nsec3_param)
return;
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);
if(domain_is_prehash(db->domains, rr->owner))
prehash_del(db->domains, rr->owner);
if(rr->owner == zone->nsec3_last)
zone->nsec3_last = prev;
zone_del_domain_in_hash_tree(zone->nsec3tree,
&rr->owner->nsec3->nsec3_node);
if(prev && prev != rr->owner)
prehash_add(db->domains, prev);
else nsec3_clear_precompile(db, zone);
}
else if(rr->type == TYPE_NSEC3PARAM && rr == zone->nsec3_param) {
nsec3_clear_precompile(db, zone);
nsec3_find_zone_param(db, zone, rr, 0);
if(!zone->nsec3_param)
return;
nsec3_precompile_newparam(db, zone);
}
}
static void
nsec3_rrsets_changed_remove_prehash(domain_type* domain, zone_type* zone)
{
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)) {
domain->nsec3->nsec3_cover = NULL;
domain->nsec3->nsec3_wcard_child_cover = NULL;
domain->nsec3->nsec3_is_exact = 0;
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)) {
domain->nsec3->nsec3_ds_parent_cover = NULL;
domain->nsec3->nsec3_ds_parent_is_exact = 0;
zone_del_domain_in_hash_tree(zone->dshashtree,
&domain->nsec3->ds_parent_hash->node);
}
}
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_wc
|| !domain->nsec3->hash_wc->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->ds_parent_hash
|| !domain->nsec3->ds_parent_hash->node.key)
&& nsec3_condition_dshash(domain, zone)) {
nsec3_precompile_domain_ds(db, domain, zone);
}
}
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);
if(type == TYPE_NSEC3 || type == TYPE_NS || type == TYPE_DS)
nsec3_rrsets_changed_add_prehash(db, domain, zone);
if(type == TYPE_NS || type == TYPE_DS || type == TYPE_DNAME) {
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);
}
}
}
static void
nsec3_add_rr_trigger(namedb_type* db, rr_type* rr, zone_type* zone)
{
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) {
nsec3_precompile_newparam(db, zone);
}
nsec3_precompile_nsec3rr(db, rr->owner, zone);
nsec3_rrsets_changed_remove_prehash(rr->owner, zone);
prehash_add(db->domains, rr->owner);
} else if(!zone->nsec3_param && rr->type == TYPE_NSEC3PARAM) {
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);
}
}
static void
nsec3_add_rrset_trigger(namedb_type* db, domain_type* domain, zone_type* zone,
uint16_t type)
{
if(!zone->nsec3_param)
return;
nsec3_rrsets_changed_add_prehash(db, domain, zone);
if(type == TYPE_NS || type == TYPE_DS) {
nsec3_rrsets_changed_remove_prehash(domain, zone);
}
if(type == TYPE_NS || type == TYPE_DS || type == TYPE_DNAME) {
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
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]);
}
int
delete_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,
region_type* temp_region, int* softfail, struct ixfr_store* ixfr_store)
{
domain_type *domain;
rrset_type *rrset;
#ifdef PACKED_STRUCTS
rrset_type* rrset_prev;
#endif
const nsd_type_descriptor_type *descriptor = nsd_type_descriptor(type);
domain = domain_table_find(db->domains, dname);
if(!domain) {
log_msg(LOG_WARNING, "diff: domain %s does not exist",
dname_to_string(dname,0));
buffer_skip(packet, rdatalen);
*softfail = 1;
return 1;
}
#ifndef PACKED_STRUCTS
rrset = domain_find_rrset(domain, zone, type);
#else
rrset = domain_find_rrset_and_prev(domain, zone, type, &rrset_prev);
#endif
if(!rrset) {
log_msg(LOG_WARNING, "diff: rrset %s does not exist",
dname_to_string(dname,0));
buffer_skip(packet, rdatalen);
*softfail = 1;
return 1;
} else {
domain_table_type *temptable;
int32_t rrnum, code;
struct rr *rr;
temptable = domain_table_create(temp_region);
code = descriptor->read_rdata(temptable, rdatalen, packet, &rr);
if(code < 0) {
log_msg(LOG_ERR, "diff: could not read rdata for "
"%s %s %s", dname_to_string(dname,0),
rrtype_to_string(type),
read_rdata_fail_str(code));
}
rr->owner = domain;
rr->type = type;
rr->klass = klass;
rr->ttl = ttl;
if(ixfr_store)
ixfr_store_delrr(ixfr_store, rr);
rrnum = find_rr_num(rrset, type, klass, rr, 0);
if(rrnum == -1 && type == TYPE_SOA && domain == zone->apex
&& rrset->rr_count != 0)
rrnum = 0;
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;
return 1;
}
#ifdef NSEC3
nsec3_delete_rr_trigger(db, rrset->rrs[rrnum], zone);
#endif
rr_lower_usage(db, rrset->rrs[rrnum]);
if(rrset->rr_count == 1) {
rrset_delete(db, domain, rrset);
rrset_zero_nonexist_check(domain, NULL);
#ifdef NSEC3
nsec3_delete_rrset_trigger(db, domain, zone, type);
#endif
domain_table_deldomain(db, domain);
} else {
#ifndef PACKED_STRUCTS
rr_type** rrs_orig = rrset->rrs;
#else
rrset_type* rrset_orig = rrset;
#endif
add_rr_to_recyclebin(db, rrset->rrs[rrnum]);
if(rrnum < rrset->rr_count-1)
rrset->rrs[rrnum] = rrset->rrs[rrset->rr_count-1];
rrset->rrs[rrset->rr_count-1] = NULL;
#ifndef PACKED_STRUCTS
rrset->rrs = region_alloc_array_init(db->region, rrs_orig,
(rrset->rr_count-1), sizeof(rr_type*));
if(!rrset->rrs) {
log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
exit(1);
}
region_recycle(db->region, rrs_orig,
sizeof(rr_type*) * rrset->rr_count);
#else
rrset = region_alloc_init(db->region, rrset_orig,
sizeof(rrset_type) +
(rrset_orig->rr_count-1)*sizeof(rr_type*));
if(!rrset) {
log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
exit(1);
}
if(rrset_prev)
rrset_prev->next = rrset;
else domain->rrsets = rrset;
region_recycle(db->region, rrset_orig,
sizeof(rrset_type) +
rrset_orig->rr_count*sizeof(rr_type*));
if(domain == zone->apex) {
if(type == TYPE_SOA) {
zone->soa_rrset = rrset;
} else if(type == TYPE_NS) {
zone->ns_rrset = rrset;
}
}
#endif
rrset->rr_count --;
#ifdef NSEC3
if(type == TYPE_NSEC3)
nsec3_rrsets_changed_add_prehash(db, domain,
zone);
#endif
}
}
return 1;
}
static void
commit_RRset(namedb_type* db, zone_type* zone, struct collect_rrs* collect_rrs)
{
struct rrset *rrset;
if(!collect_rrs->domain || collect_rrs->rr_count == 0)
return;
if (!collect_rrs->rrset) {
#ifdef NSEC3
domain_type* p;
#endif
rrset = region_alloc(db->region, sizeof(*rrset)
#ifdef PACKED_STRUCTS
+ sizeof(rr_type*) * collect_rrs->rr_count
#endif
);
if(!rrset) {
log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
exit(1);
}
rrset->zone = zone;
rrset->rr_count = collect_rrs->rr_count;
#ifndef PACKED_STRUCTS
rrset->rrs = region_alloc(db->region,
sizeof(rr_type*) * collect_rrs->rr_count);
#endif
memcpy(rrset->rrs, collect_rrs->rrs, collect_rrs->rr_count * sizeof(rr_type*));
domain_add_rrset(collect_rrs->domain, rrset);
#ifdef NSEC3
p = collect_rrs->domain->parent;
nsec3_add_rrset_trigger(db, collect_rrs->domain, zone, collect_rrs->type);
while(p && p->rrsets == NULL && !p->is_apex) {
nsec3_rrsets_changed_add_prehash(db, p, zone);
p = p->parent;
}
#endif
} else {
#ifndef PACKED_STRUCTS
struct rr **rrs;
#else
struct rrset *rrset_orig;
#endif
rrset = collect_rrs->rrset;
#ifndef PACKED_STRUCTS
rrs = rrset->rrs;
rrset->rrs = region_alloc_array(
db->region, rrset->rr_count + collect_rrs->rr_count, sizeof(*rrs));
if(!rrset->rrs) {
log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
exit(1);
}
if(rrs)
memcpy(rrset->rrs, rrs, rrset->rr_count * sizeof(*rrs));
region_recycle(db->region, rrs, rrset->rr_count * sizeof(*rrs));
#else
rrset_orig = rrset;
rrset = region_alloc(db->region,
sizeof(rrset_type) +
(rrset_orig->rr_count+collect_rrs->rr_count)*sizeof(rr_type*));
if(!rrset) {
log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
exit(1);
}
memcpy(rrset, rrset_orig,
sizeof(rrset_type) +
rrset_orig->rr_count*sizeof(rr_type*));
if(collect_rrs->rrset_prev)
collect_rrs->rrset_prev->next = rrset;
else collect_rrs->domain->rrsets = rrset;
region_recycle(db->region, rrset_orig,
sizeof(rrset_type) +
rrset_orig->rr_count*sizeof(rr_type*));
#endif
memcpy(rrset->rrs + rrset->rr_count, collect_rrs->rrs, collect_rrs->rr_count * sizeof(rr_type*));
rrset->rr_count += collect_rrs->rr_count;
}
if (collect_rrs->domain == zone->apex)
apex_rrset_checks(db, rrset, collect_rrs->domain);
collect_rrs->domain = NULL;
collect_rrs->type = -1;
collect_rrs->rrset = NULL;
#ifdef PACKED_STRUCTS
collect_rrs->rrset_prev = NULL;
#endif
#ifdef NSEC3
for (int i = 0; i < collect_rrs->rr_count; i++) {
nsec3_add_rr_trigger(db, collect_rrs->rrs[i], zone);
}
#endif
collect_rrs->rr_count = 0;
}
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,
int* softfail, struct ixfr_store* ixfr_store,
struct collect_rrs* collect_rrs)
{
domain_type* domain;
rr_type *rr;
int32_t code;
const nsd_type_descriptor_type *descriptor;
if (collect_rrs == NULL) {
struct collect_rrs collect_rrs2;
collect_rrs2.domain = NULL;
collect_rrs2.type = -1;
collect_rrs2.rrset = NULL;
#ifdef PACKED_STRUCTS
collect_rrs2.rrset_prev = NULL;
#endif
collect_rrs2.rr_count = 0;
if (!add_RR(db, dname, type, klass, ttl, packet, rdatalen,
zone, softfail, ixfr_store, &collect_rrs2)) {
return 0;
}
commit_RRset(db, zone, &collect_rrs2);
return 1;
}
domain = domain_table_insert(db->domains, dname);
assert(domain);
if (domain != collect_rrs->domain || type != collect_rrs->type
|| collect_rrs->rr_count >= (int)(sizeof(collect_rrs->rrs) / sizeof(*collect_rrs->rrs))){
commit_RRset(db, zone, collect_rrs);
collect_rrs->domain = domain;
collect_rrs->type = type;
collect_rrs->rrset = NULL;
#ifdef PACKED_STRUCTS
collect_rrs->rrset_prev = NULL;
#endif
}
descriptor = nsd_type_descriptor(type);
code = descriptor->read_rdata(db->domains, rdatalen, packet, &rr);
if(code < 0) {
log_msg(LOG_ERR, "diff: could not read rdata for %s %s %s",
dname_to_string(dname,0), rrtype_to_string(type),
read_rdata_fail_str(code));
return 0;
}
rr->owner = domain;
rr->type = type;
rr->klass = klass;
rr->ttl = ttl;
if (ixfr_store) {
ixfr_store_addrr(ixfr_store, rr);
}
if (collect_rrs->rr_count == 0) {
#ifndef PACKED_STRUCTS
collect_rrs->rrset = domain_find_rrset(collect_rrs->domain, zone, collect_rrs->type);
#else
collect_rrs->rrset = domain_find_rrset_and_prev(collect_rrs->domain, zone, collect_rrs->type, &collect_rrs->rrset_prev);
#endif
}
if (collect_rrs->rrset) {
for (int i = 0; i < collect_rrs->rrset->rr_count; i++) {
if (!equal_rr_rdata(descriptor, rr, collect_rrs->rrset->rrs[i]))
continue;
rr_lower_usage(db, rr);
region_recycle(db->region, rr, sizeof(*rr) + rr->rdlength);
DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR <%s, %s> already exists",
dname_to_string(dname,0), rrtype_to_string(type)));
*softfail = 1;
return 1;
}
}
for (int i = 0; i < collect_rrs->rr_count; i++) {
if (!equal_rr_rdata(descriptor, rr, collect_rrs->rrs[i]))
continue;
rr_lower_usage(db, rr);
region_recycle(db->region, rr, sizeof(*rr) + rr->rdlength);
DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR <%s, %s> already exists",
dname_to_string(dname,0), rrtype_to_string(type)));
*softfail = 1;
return 1;
}
if(collect_rrs->rr_count + ( collect_rrs->rrset
? collect_rrs->rrset->rr_count : 0) >= 65535) {
log_msg(LOG_ERR, "diff: too many RRs at %s",
dname_to_string(dname,0));
return 0;
}
collect_rrs->rrs[collect_rrs->rr_count++] = rr;
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(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 {
zopt = zone_list_zone_insert(opt, zstr, patname);
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;
while(domain && domain_is_subdomain(domain, zone->apex))
{
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete zone visit %s",
domain_to_string(domain)));
while((rrset = domain_find_any_rrset(domain, zone))) {
rrset_lower_usage(db, rrset);
rrset_delete(db, domain, rrset);
if(domain->rrsets == 0)
nonexist_check = 1;
}
next = domain_next(domain);
domain_table_deldomain(db, domain);
domain = next;
}
if(nonexist_check) {
domain_type* ce = NULL;
DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: zero rrset check"));
domain = zone->apex;
while(domain && domain_is_subdomain(domain, zone->apex))
{
if(domain->is_existing)
ce = rrset_zero_nonexist_check(domain, ce);
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)
region_log_stats(db->region);
#endif
assert(zone->soa_rrset == 0);
assert(zone->ns_rrset == 0);
assert(zone->is_secure == 0);
}
static int
apply_ixfr(nsd_type* nsd, FILE *in, uint32_t serialno,
uint32_t seq_nr, uint32_t seq_total,
int* is_axfr, int* delete_mode, int* rr_count,
struct zone* zone, uint64_t* bytes,
int* softfail, struct ixfr_store* ixfr_store)
{
uint32_t msglen, checklen, pkttype;
int qcount, ancount;
buffer_type* packet;
region_type* region;
struct collect_rrs collect_rrs;
if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_XXFR) {
log_msg(LOG_ERR, "could not read type or wrong type");
return 0;
}
if(!diff_read_32(in, &msglen)) {
log_msg(LOG_ERR, "could not read len");
return 0;
}
if(msglen < QHEADERSZ) {
log_msg(LOG_ERR, "msg too short");
return 0;
}
region = region_create(xalloc, free);
if(!region) {
log_msg(LOG_ERR, "out of memory");
return 0;
}
packet = buffer_create(region, QIOBUFSZ);
if(msglen > QIOBUFSZ) {
log_msg(LOG_ERR, "msg too long");
region_destroy(region);
return 0;
}
buffer_clear(packet);
if(fread(buffer_begin(packet), msglen, 1, in) != 1) {
log_msg(LOG_ERR, "short fread: %s", strerror(errno));
region_destroy(region);
return 0;
}
buffer_set_limit(packet, msglen);
if(!diff_read_32(in, &checklen) || checklen != msglen) {
log_msg(LOG_ERR, "transfer part has incorrect checkvalue");
return 0;
}
*bytes += msglen;
qcount = QDCOUNT(packet);
ancount = ANCOUNT(packet);
buffer_skip(packet, QHEADERSZ);
if(qcount > 64 || ancount > 65530) {
log_msg(LOG_ERR, "RR count impossibly high");
region_destroy(region);
return 0;
}
for(int i=0; i < qcount; ++i) {
if(!packet_skip_rr(packet, 1)) {
log_msg(LOG_ERR, "bad RR in question section");
region_destroy(region);
return 0;
}
}
collect_rrs.domain = NULL;
collect_rrs.type = -1;
collect_rrs.rrset = NULL;
#ifdef PACKED_STRUCTS
collect_rrs.rrset_prev = NULL;
#endif
collect_rrs.rr_count = 0;
DEBUG(DEBUG_XFRD, 2, (LOG_INFO, "diff: started packet %d/%d for zone %s",
(int)seq_nr, (int)seq_total, domain_to_string(zone->apex)));
for(int i=0; i < ancount; ++i, ++(*rr_count)) {
const dname_type *owner;
uint16_t type, klass, rrlen;
uint32_t ttl;
owner = dname_make_from_packet(region, packet, 1, 1);
if(!owner) {
log_msg(LOG_ERR, "bad xfr RR dname %d", *rr_count);
region_destroy(region);
return 0;
}
if(!buffer_available(packet, 10)) {
log_msg(LOG_ERR, "bad xfr RR format %d", *rr_count);
region_destroy(region);
return 0;
}
type = buffer_read_u16(packet);
klass = buffer_read_u16(packet);
ttl = buffer_read_u32(packet);
rrlen = buffer_read_u16(packet);
if(!buffer_available(packet, rrlen)) {
log_msg(LOG_ERR, "bad xfr RR rdata %d, len %d have %d",
*rr_count, rrlen, (int)buffer_remaining(packet));
region_destroy(region);
return 0;
}
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parsed count %d, ax %d, delmode %d",
domain_to_string(zone->apex), *rr_count, *is_axfr, *delete_mode));
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);
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);
if(ixfr_store)
ixfr_store_add_newsoa(ixfr_store, ttl, packet, rrlen);
continue;
} else if (*rr_count == 1) {
assert(!*is_axfr);
assert(!*delete_mode);
if (serial == serialno)
goto axfr;
*delete_mode = 1;
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);
return 2;
}
if(ixfr_store)
ixfr_store_add_oldsoa(ixfr_store, ttl, packet, rrlen);
} else if (!*is_axfr) {
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);
}
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;
} 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));
}
}
if(type == TYPE_TSIG || type == TYPE_OPT) {
buffer_skip(packet, rrlen);
continue;
}
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfr %s RR dname is %s type %s",
*delete_mode?"del":"add",
dname_to_string(owner, 0), rrtype_to_string(type)));
if(*delete_mode) {
assert(!*is_axfr);
commit_RRset(nsd->db, zone, &collect_rrs);
if(!delete_RR(nsd->db, owner, type, klass, ttl, packet,
rrlen, zone, region, softfail, ixfr_store)) {
region_destroy(region);
return 0;
}
} else {
if(!(add_RR(nsd->db, owner, type, klass, ttl, packet,
rrlen, zone, softfail, ixfr_store, &collect_rrs))) {
for (int i = 0; i < collect_rrs.rr_count; i++) {
rr_lower_usage( nsd->db, collect_rrs.rrs[i]);
region_recycle( nsd->db->region, collect_rrs.rrs[i]
, sizeof(*collect_rrs.rrs[i])
+ collect_rrs.rrs[i]->rdlength);
}
region_destroy(region);
return 0;
}
}
}
commit_RRset(nsd->db, zone, &collect_rrs);
region_destroy(region);
return 1;
}
static int
check_for_bad_serial(namedb_type* db, const char* zone_str, uint32_t old_serial)
{
domain_type* domain;
region_type* region = region_create(xalloc, free);
const dname_type* zone_name = dname_parse(region, zone_str);
zone_type* zone = 0;
domain = domain_table_find(db->domains, zone_name);
if(domain)
zone = domain_find_zone(db, domain);
if(zone && zone->apex == domain && zone->soa_rrset && old_serial)
{
uint32_t memserial = 0;
retrieve_soa_rdata_serial(zone->soa_rrset->rrs[0], &memserial);
if(old_serial != memserial) {
region_destroy(region);
return 1;
}
}
region_destroy(region);
return 0;
}
int
apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in,
struct nsd_options* ATTR_UNUSED(opt), udb_base* taskudb, uint32_t xfrfilenr)
{
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;
uint8_t committed;
uint32_t i;
uint64_t num_bytes = 0;
assert(zone);
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;
}
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;
}
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 -2;
}
if(!zone->is_skipped)
{
int is_axfr=0, delete_mode=0, rr_count=0, softfail=0;
struct ixfr_store* ixfr_store = NULL, ixfr_store_mem;
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "processing xfr: %s", zone_buf));
if(zone_is_ixfr_enabled(zone))
ixfr_store = ixfr_store_start(zone, &ixfr_store_mem);
for(i=0; i<num_parts; i++) {
int ret;
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "processing xfr: apply part %d", (int)i));
ret = apply_ixfr(nsd, in, new_serial,
i, num_parts, &is_axfr, &delete_mode,
&rr_count, zone,
&num_bytes, &softfail, ixfr_store);
if(ret == 0) {
log_msg(LOG_ERR, "bad ixfr packet part %d in diff file for %s", (int)i, zone_buf);
diff_update_commit(
zone_buf, DIFF_CORRUPT, nsd, xfrfilenr);
return -1;
} else if(ret == 2) {
break;
}
}
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
prehash_zone(nsd->db, zone);
#endif
zone->is_changed = 1;
zone->is_updated = 1;
zone->is_checked = (committed == DIFF_VERIFIED);
zone->mtime.tv_sec = time_end_0;
zone->mtime.tv_nsec = time_end_1*1000;
if(zone->logstr)
region_recycle(nsd->db->region, zone->logstr,
strlen(zone->logstr)+1);
zone->logstr = region_strdup(nsd->db->region, log_buf);
namedb_zone_free_filenames(nsd->db, zone);
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 primary, "
"starting AXFR. Transfer %s", zone_buf, log_buf);
diff_update_commit(
zone_buf, DIFF_INCONSISTENT, nsd, xfrfilenr);
return -1;
}
if(ixfr_store)
ixfr_store_finish(ixfr_store, nsd, log_buf);
if(1 <= verbosity) {
double elapsed = (double)(time_end_0 - time_start_0)+
(double)((double)time_end_1
-(double)time_start_1) / 1000000.0;
VERBOSITY(1, (LOG_INFO, "zone %s %s of %"PRIu64" bytes in %g seconds",
zone_buf, log_buf, num_bytes, elapsed));
}
}
else {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "skipping xfr: %s", zone_buf));
}
return 1;
}
static void udb_task_walk_chunk(void* base, void* d, uint64_t s, udb_walk_relptr_cb* cb, void *arg)
{
struct task_list_d* p = (struct task_list_d*)d;
assert(s >= p->size);
(void)s;
(*cb)(base, &p->next, arg);
}
void udb_walkfunc(void* base, void* warg, uint8_t t, void* d, uint64_t s,
udb_walk_relptr_cb* cb, void *arg)
{
(void)warg;
switch(t) {
case udb_chunk_type_task:
udb_task_walk_chunk(base, d, s, cb, arg);
break;
default:
break;
}
}
struct udb_base* task_file_create(const char* file)
{
return udb_base_create_new(file, &udb_walkfunc, NULL);
}
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)) {
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);
}
udb_ptr_set_ptr(last, udb, e);
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));
}
return 1;
}
void task_new_soainfo(struct udb_base* udb, udb_ptr* last, struct zone* z,
enum soainfo_hint hint)
{
udb_ptr e;
size_t sz;
const dname_type* apex, *ns, *em;
if(!z || !z->apex || !domain_dname(z->apex))
return;
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_domain_ref(z->soa_rrset->rrs[0]));
em = domain_dname(rdata_domain_ref_offset(
z->soa_rrset->rrs[0], sizeof(void*)));
sz += sizeof(uint32_t)*6 + sizeof(uint8_t)*2
+ ns->name_size + em->name_size;
} else {
ns = NULL;
em = NULL;
}
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, z->soa_rrset->rrs[0]->rdata + sizeof(void*)*2,
sizeof(uint32_t)*5);
}
udb_ptr_unlink(&e, udb);
}
void task_process_sync(struct udb_base* taskudb)
{
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);
}
void task_clear(struct udb_base* taskudb)
{
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);
}
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);
}
void task_new_check_zonefiles(udb_base* udb, udb_ptr* last,
const dname_type* zone)
{
udb_ptr e;
xfrd_check_catalog_consumer_zonefiles(zone);
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);
}
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;
}
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);
}
void
task_new_add_zone(udb_base* udb, udb_ptr* last, const char* zone,
const char* pattern, unsigned zonestatid)
{
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;
}
TASKLIST(&e)->task_type = task_add_zone;
TASKLIST(&e)->yesno = zonestatid;
p = TASKLIST(&e)->zname;
memcpy(p, zone, zlen+1);
memmove((char*)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);
}
void task_new_add_key(udb_base* udb, udb_ptr* last, struct key_options* 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;
}
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;
}
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_cookies(udb_base* udb, udb_ptr* last, int answer_cookie,
size_t cookie_count, void* cookie_secrets) {
udb_ptr e;
char* p;
size_t const secrets_size = sizeof(cookie_secrets_type);
DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task cookies"));
if(!task_create_new_elem(udb, last, &e,
sizeof(struct task_list_d) + secrets_size, NULL)) {
log_msg(LOG_ERR, "tasklist: out of space, cannot add cookies");
return;
}
TASKLIST(&e)->task_type = task_cookies;
TASKLIST(&e)->newserial = (uint32_t) answer_cookie;
TASKLIST(&e)->yesno = (uint64_t) cookie_count;
p = (char*)TASKLIST(&e)->zname;
memmove(p, cookie_secrets, secrets_size);
udb_ptr_unlink(&e, udb);
}
void task_new_add_pattern(udb_base* udb, udb_ptr* last,
struct pattern_options* 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;
}
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;
}
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, struct nsd_options* 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;
}
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);
}
void task_new_zonestat_inc(udb_base* udb, udb_ptr* last, unsigned sz)
{
udb_ptr e;
DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task zonestat_inc"));
if(sz == 0)
return;
if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d),
NULL)) {
log_msg(LOG_ERR, "tasklist: out of space, cannot add z_i");
return;
}
TASKLIST(&e)->task_type = task_zonestat_inc;
TASKLIST(&e)->oldserial = (uint32_t)sz;
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");
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"));
ok = !task->yesno;
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;
}
static void
task_process_checkzones(struct nsd* nsd, udb_base* taskudb, udb_ptr* last_task,
struct task_list_d* task)
{
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 {
namedb_check_zonefiles(nsd, nsd->options, taskudb, last_task);
}
}
static void
task_process_writezones(struct nsd* nsd, struct task_list_d* task)
{
if(task->yesno) {
struct zone_options* zo = zone_options_find(nsd->options,
task->zname);
if(zo)
namedb_write_zonefile(nsd, zo);
} else {
namedb_write_zonefiles(nsd, nsd->options);
}
}
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;
}
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;
}
region_recycle(nsd->db->region, (void*)zdname,
dname_total_size(zdname));
z->zonestatid = (unsigned)task->yesno;
if(!z->soa_rrset && z->opts->pattern->zonefile) {
namedb_read_zonefile(nsd, z, udb, last_task);
}
}
static void
task_process_del_zone(struct nsd* nsd, struct task_list_d* task)
{
zone_type* zone;
struct zone_options* 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;
#ifdef NSEC3
nsec3_clear_precompile(nsd->db, zone);
zone->nsec3_param = NULL;
#endif
delete_zone_rrs(nsd->db, zone);
zopt = zone->opts;
namedb_zone_delete(nsd->db, zone);
zone_options_delete(nsd->options, zopt);
}
static void
task_process_add_key(struct nsd* nsd, struct task_list_d* task)
{
struct key_options 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));
}
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));
key_options_remove(nsd->options, name);
}
static void
task_process_cookies(struct nsd* nsd, struct task_list_d* task) {
DEBUG(DEBUG_IPC, 1, (LOG_INFO, "cookies task answer: %s, count: %d",
task->newserial ? "yes" : "no", (int)task->yesno));
nsd->do_answer_cookie = (int) task->newserial;
nsd->cookie_count = (size_t) task->yesno;
memmove(nsd->cookie_secrets, task->zname, sizeof(nsd->cookie_secrets));
explicit_bzero(task->zname, sizeof(nsd->cookie_secrets));
}
static void
task_process_add_pattern(struct nsd* nsd, struct task_list_d* task)
{
region_type* temp = region_create(xalloc, free);
buffer_type buffer;
struct pattern_options *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);
}
static void
task_process_opt_change(struct nsd* nsd, struct task_list_d* task)
{
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
}
#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;
task_new_zonestat_inc(udb, last_task, nsd->zonestatdesired);
}
#endif
void
task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr* task)
{
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) {
udb_ptr_free_space(task, udb, TASKLIST(task)->size);
return;
}
df = xfrd_open_xfrfile(nsd, TASKLIST(task)->yesno, "r");
if(!df) {
zone->is_skipped = 1;
udb_ptr_free_space(task, udb, TASKLIST(task)->size);
return;
}
switch(apply_ixfr_for_zone(nsd, zone, df, nsd->options, udb,
TASKLIST(task)->yesno)) {
case 1:
break;
case 0:
zone->is_skipped = 1;
break;
case -1:
exit(1);
break;
default:break;
}
fclose(df);
udb_ptr_free_space(task, udb, TASKLIST(task)->size);
}
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_cookies:
task_process_cookies(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);
}