From a1e63e81188ca4f5b00674e84269272bfc500486 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Mon, 29 Jan 2018 14:33:08 +0000 Subject: [PATCH] auth zone work, ixfr apply procedure. git-svn-id: file:///svn/unbound/trunk@4461 be551aaa-1e26-0410-a405-d3ace91eadb9 --- services/authzone.c | 510 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 489 insertions(+), 21 deletions(-) diff --git a/services/authzone.c b/services/authzone.c index 6f11c53a0..264292d03 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -635,6 +635,40 @@ domain_remove_rrset(struct auth_data* node, uint16_t rr_type) } } +/** find an rr index in the rrset. returns true if found */ +static int +az_rrset_find_rr(struct packed_rrset_data* d, uint8_t* rdata, size_t len, + size_t* index) +{ + size_t i; + for(i=0; icount; i++) { + if(d->rr_len[i] != len) + continue; + if(memcmp(d->rr_data[i], rdata, len) == 0) { + *index = i; + return 1; + } + } + return 0; +} + +/** find an rrsig index in the rrset. returns true if found */ +static int +az_rrset_find_rrsig(struct packed_rrset_data* d, uint8_t* rdata, size_t len, + size_t* index) +{ + size_t i; + for(i=d->count; icount + d->rrsig_count; i++) { + if(d->rr_len[i] != len) + continue; + if(memcmp(d->rr_data[i], rdata, len) == 0) { + *index = i; + return 1; + } + } + return 0; +} + /** see if rdata is duplicate */ static int rdata_duplicate(struct packed_rrset_data* d, uint8_t* rdata, size_t len) @@ -662,6 +696,68 @@ rrsig_rdata_get_type_covered(uint8_t* rdata, size_t rdatalen) return sldns_read_uint16(rdata+2); } +/** remove RR from existing RRset. Also sig, if it is a signature. + * reallocates the packed rrset for a new one, false on alloc failure */ +static int +rrset_remove_rr(struct auth_rrset* rrset, size_t index) +{ + struct packed_rrset_data* d, *old = rrset->data; + size_t i; + if(index >= old->count + old->rrsig_count) + return 0; /* index out of bounds */ + d = (struct packed_rrset_data*)calloc(1, packed_rrset_sizeof(old) - ( + sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t) + + old->rr_len[index])); + if(!d) { + log_err("malloc failure"); + return 0; + } + d->ttl = old->ttl; + d->count = old->count; + d->rrsig_count = old->rrsig_count; + if(index < d->count) d->count--; + else d->rrsig_count--; + d->trust = old->trust; + d->security = old->security; + + /* set rr_len, needed for ptr_fixup */ + d->rr_len = (size_t*)((uint8_t*)d + + sizeof(struct packed_rrset_data)); + if(index > 0) + memmove(d->rr_len, old->rr_len, (index)*sizeof(size_t)); + if(index+1 < old->count+old->rrsig_count) + memmove(&d->rr_len[index], &old->rr_len[index+1], + (old->count+old->rrsig_count - (index+1))*sizeof(size_t)); + packed_rrset_ptr_fixup(d); + + /* move over ttls */ + if(index > 0) + memmove(d->rr_ttl, old->rr_ttl, (index)*sizeof(time_t)); + if(index+1 < old->count+old->rrsig_count) + memmove(&d->rr_ttl[index], &old->rr_ttl[index+1], + (old->count+old->rrsig_count - (index+1))*sizeof(time_t)); + + /* move over rr_data */ + for(i=0; icount+d->rrsig_count; i++) { + int oldi; + if(i < index) oldi = i; + else oldi = i+1; + memmove(d->rr_data[i], old->rr_data[oldi], d->rr_len[i]); + } + + /* recalc ttl (lowest of remaining RR ttls) */ + if(d->count + d->rrsig_count > 0) + d->ttl = d->rr_ttl[0]; + for(i=0; icount+d->rrsig_count; i++) { + if(d->rr_ttl[i] < d->ttl) + d->ttl = d->rr_ttl[i]; + } + + free(rrset->data); + rrset->data = d; + return 1; +} + /** add RR to existing RRset. If insert_sig is true, add to rrsigs. * This reallocates the packed rrset for a new one */ static int @@ -938,11 +1034,38 @@ rrset_moveover_rrsigs(struct auth_data* node, uint16_t rr_type, return 1; } +/** copy the rrsigs from the rrset to the rrsig rrset, because the rrset + * is going to be deleted. reallocates the RRSIG rrset data. */ +static int +rrsigs_copy_from_rrset_to_rrsigset(struct auth_rrset* rrset, + struct auth_rrset* rrsigset) +{ + size_t i; + if(rrset->data->rrsig_count == 0) + return 1; + + /* move them over one by one, because there might be duplicates, + * duplicates are ignored */ + for(i=rrset->data->count; + idata->count+rrset->data->rrsig_count; i++) { + uint8_t* rdata = rrset->data->rr_data[i]; + size_t rdatalen = rrset->data->rr_len[i]; + time_t rr_ttl = rrset->data->rr_ttl[i]; + + if(rdata_duplicate(rrsigset->data, rdata, rdatalen)) { + continue; + } + if(!rrset_add_rr(rrsigset, rr_ttl, rdata, rdatalen, 0)) + return 0; + } + return 1; +} + /** Add rr to node, ignores duplicate RRs, * rdata points to buffer with rdatalen octets, starts with 2bytelength. */ static int az_domain_add_rr(struct auth_data* node, uint16_t rr_type, uint32_t rr_ttl, - uint8_t* rdata, size_t rdatalen) + uint8_t* rdata, size_t rdatalen, int* duplicate) { struct auth_rrset* rrset; /* packed rrsets have their rrsigs along with them, sort them out */ @@ -951,14 +1074,18 @@ az_domain_add_rr(struct auth_data* node, uint16_t rr_type, uint32_t rr_ttl, if((rrset=az_domain_rrset(node, ctype))!= NULL) { /* a node of the correct type exists, add the RRSIG * to the rrset of the covered data type */ - if(rdata_duplicate(rrset->data, rdata, rdatalen)) + if(rdata_duplicate(rrset->data, rdata, rdatalen)) { + if(duplicate) *duplicate = 1; return 1; + } if(!rrset_add_rr(rrset, rr_ttl, rdata, rdatalen, 1)) return 0; } else if((rrset=az_domain_rrset(node, rr_type))!= NULL) { /* add RRSIG to rrset of type RRSIG */ - if(rdata_duplicate(rrset->data, rdata, rdatalen)) + if(rdata_duplicate(rrset->data, rdata, rdatalen)) { + if(duplicate) *duplicate = 1; return 1; + } if(!rrset_add_rr(rrset, rr_ttl, rdata, rdatalen, 0)) return 0; } else { @@ -971,8 +1098,10 @@ az_domain_add_rr(struct auth_data* node, uint16_t rr_type, uint32_t rr_ttl, /* normal RR type */ if((rrset=az_domain_rrset(node, rr_type))!= NULL) { /* add data to existing node with data type */ - if(rdata_duplicate(rrset->data, rdata, rdatalen)) + if(rdata_duplicate(rrset->data, rdata, rdatalen)) { + if(duplicate) *duplicate = 1; return 1; + } if(!rrset_add_rr(rrset, rr_ttl, rdata, rdatalen, 0)) return 0; } else { @@ -999,7 +1128,7 @@ az_domain_add_rr(struct auth_data* node, uint16_t rr_type, uint32_t rr_ttl, /** insert RR into zone, ignore duplicates */ static int az_insert_rr(struct auth_zone* z, uint8_t* rr, size_t rr_len, - size_t dname_len) + size_t dname_len, int* duplicate) { struct auth_data* node; uint8_t* dname = rr; @@ -1019,13 +1148,273 @@ az_insert_rr(struct auth_zone* z, uint8_t* rr, size_t rr_len, log_err("cannot create domain"); return 0; } - if(!az_domain_add_rr(node, rr_type, rr_ttl, rdata, rdatalen)) { + if(!az_domain_add_rr(node, rr_type, rr_ttl, rdata, rdatalen, + duplicate)) { log_err("cannot add RR to domain"); return 0; } return 1; } +/** Remove rr from node, ignores nonexisting RRs, + * rdata points to buffer with rdatalen octets, starts with 2bytelength. */ +static int +az_domain_remove_rr(struct auth_data* node, uint16_t rr_type, + uint8_t* rdata, size_t rdatalen, int* nonexist) +{ + struct auth_rrset* rrset; + size_t index = 0; + + /* find the plain RR of the given type */ + if((rrset=az_domain_rrset(node, rr_type))!= NULL) { + if(az_rrset_find_rr(rrset->data, rdata, rdatalen, &index)) { + if(rrset->data->count == 1 && + rrset->data->rrsig_count == 0) { + /* last RR, delete the rrset */ + domain_remove_rrset(node, rr_type); + } else if(rrset->data->count == 1 && + rrset->data->rrsig_count != 0) { + /* move RRSIGs to the RRSIG rrset, or + * this one becomes that RRset */ + struct auth_rrset* rrsigset = az_domain_rrset( + node, LDNS_RR_TYPE_RRSIG); + if(rrsigset) { + /* move left over rrsigs to the + * existing rrset of type RRSIG */ + rrsigs_copy_from_rrset_to_rrsigset( + rrset, rrsigset); + /* and then delete the rrset */ + domain_remove_rrset(node, rr_type); + } else { + /* no rrset of type RRSIG, this + * set is now of that type, + * just remove the rr */ + if(!rrset_remove_rr(rrset, index)) + return 0; + rrset->type = LDNS_RR_TYPE_RRSIG; + rrset->data->count = rrset->data->rrsig_count; + rrset->data->rrsig_count = 0; + } + } else { + /* remove the RR from the rrset */ + if(!rrset_remove_rr(rrset, index)) + return 0; + } + return 1; + } + /* rr not found in rrset */ + } + + /* is it a type RRSIG, look under the covered type */ + if(rr_type == LDNS_RR_TYPE_RRSIG) { + uint16_t ctype = rrsig_rdata_get_type_covered(rdata, rdatalen); + if((rrset=az_domain_rrset(node, ctype))!= NULL) { + if(az_rrset_find_rrsig(rrset->data, rdata, rdatalen, + &index)) { + /* rrsig should have d->count > 0, be + * over some rr of that type */ + /* remove the rrsig from the rrsigs list of the + * rrset */ + if(!rrset_remove_rr(rrset, index)) + return 0; + return 1; + } + } + /* also RRSIG not found */ + } + + /* nothing found to delete */ + if(nonexist) *nonexist = 1; + return 1; +} + +/** remove RR from zone, ignore if it does not exist, false on alloc failure*/ +static int +az_remove_rr(struct auth_zone* z, uint8_t* rr, size_t rr_len, + size_t dname_len, int* nonexist) +{ + struct auth_data* node; + uint8_t* dname = rr; + uint16_t rr_type = sldns_wirerr_get_type(rr, rr_len, dname_len); + uint16_t rr_class = sldns_wirerr_get_class(rr, rr_len, dname_len); + size_t rdatalen = ((size_t)sldns_wirerr_get_rdatalen(rr, rr_len, + dname_len))+2; + /* rdata points to rdata prefixed with uint16 rdatalength */ + uint8_t* rdata = sldns_wirerr_get_rdatawl(rr, rr_len, dname_len); + + if(rr_class != z->dclass) { + log_err("wrong class for RR"); + /* really also a nonexisting entry, because no records + * of that class in the zone, but return an error because + * getting records of the wrong class is a failure of the + * zone transfer */ + return 0; + } + node = az_find_name(z, dname, dname_len); + if(!node) { + /* node with that name does not exist */ + /* nonexisting entry, because no such name */ + *nonexist = 1; + return 1; + } + if(!az_domain_remove_rr(node, rr_type, rdata, rdatalen, nonexist)) { + /* alloc failure or so */ + return 0; + } + /* remove the node, if necessary */ + /* an rrsets==NULL entry is not kept around for empty nonterminals, + * and also parent nodes are not kept around, so we just delete it */ + if(node->rrsets == NULL) { + rbtree_delete(&z->data, node); + auth_data_delete(node); + } + return 1; +} + +/** decompress an RR into the buffer where it'll be an uncompressed RR + * with uncompressed dname and uncompressed rdata (dnames) */ +static int +decompress_rr_into_buffer(struct sldns_buffer* buf, uint8_t* pkt, + size_t pktlen, uint8_t* dname, uint16_t rr_type, uint16_t rr_class, + uint32_t rr_ttl, uint8_t* rr_data, uint16_t rr_rdlen) +{ + sldns_buffer pktbuf; + size_t dname_len = 0; + size_t rdlenpos; + size_t rdlen; + uint8_t* rd; + const sldns_rr_descriptor* desc; + sldns_buffer_init_frm_data(&pktbuf, pkt, pktlen); + sldns_buffer_clear(buf); + + /* decompress dname */ + sldns_buffer_set_position(&pktbuf, + dname - sldns_buffer_current(&pktbuf)); + dname_len = pkt_dname_len(&pktbuf); + if(dname_len == 0) return 0; /* parse fail on dname */ + if(!sldns_buffer_available(buf, dname_len)) return 0; + dname_pkt_copy(&pktbuf, sldns_buffer_current(buf), dname); + sldns_buffer_skip(buf, dname_len); + + /* type, class, ttl and rdatalength fields */ + if(!sldns_buffer_available(buf, 10)) return 0; + sldns_buffer_write_u16(buf, rr_type); + sldns_buffer_write_u16(buf, rr_class); + sldns_buffer_write_u32(buf, rr_ttl); + rdlenpos = sldns_buffer_position(buf); + sldns_buffer_write_u16(buf, 0); /* rd length position */ + + /* decompress rdata */ + desc = sldns_rr_descript(rr_type); + rd = rr_data; + rdlen = rr_rdlen; + if(rdlen > 0 && desc && desc->_dname_count > 0) { + int count = (int)desc->_dname_count; + int rdf = 0; + size_t len; /* how much rdata to plain copy */ + size_t uncompressed_len, compressed_len; + size_t oldpos; + /* decompress dnames. */ + while(rdlen > 0 && count) { + switch(desc->_wireformat[rdf]) { + case LDNS_RDF_TYPE_DNAME: + sldns_buffer_set_position(&pktbuf, + rd - sldns_buffer_current(&pktbuf)); + oldpos = sldns_buffer_position(&pktbuf); + /* moves pktbuf to right after the + * compressed dname, and returns uncompressed + * dname length */ + uncompressed_len = pkt_dname_len(&pktbuf); + if(!uncompressed_len) + return 0; /* parse error in dname */ + if(!sldns_buffer_available(buf, + uncompressed_len)) + /* dname too long for buffer */ + return 0; + dname_pkt_copy(&pktbuf, + sldns_buffer_current(buf), rd); + sldns_buffer_skip(buf, uncompressed_len); + compressed_len = sldns_buffer_position( + &pktbuf) - oldpos; + rd += compressed_len; + rdlen -= compressed_len; + count--; + len = 0; + break; + case LDNS_RDF_TYPE_STR: + len = rd[0] + 1; + break; + default: + len = get_rdf_size(desc->_wireformat[rdf]); + break; + } + if(len) { + if(!sldns_buffer_available(buf, len)) + return 0; /* too long for buffer */ + sldns_buffer_write(buf, rd, len); + rd += len; + rdlen -= len; + } + rdf++; + } + } + /* copy remaining data */ + if(rdlen > 0) { + if(!sldns_buffer_available(buf, rdlen)) return 0; + sldns_buffer_write(buf, rd, rdlen); + } + sldns_buffer_flip(buf); + + /* fixup rdlength */ + sldns_buffer_write_u16_at(buf, rdlenpos, + sldns_buffer_position(buf)-rdlenpos-2); + return 1; +} + +/** insert RR into zone, from packet, decompress RR, + * if duplicate is nonNULL set the flag but otherwise ignore duplicates */ +static int +az_insert_rr_decompress(struct auth_zone* z, uint8_t* pkt, size_t pktlen, + struct sldns_buffer* scratch_buffer, uint8_t* dname, uint16_t rr_type, + uint16_t rr_class, uint32_t rr_ttl, uint8_t* rr_data, + uint16_t rr_rdlen, int* duplicate) +{ + uint8_t* rr; + size_t rr_len; + size_t dname_len; + if(!decompress_rr_into_buffer(scratch_buffer, pkt, pktlen, dname, + rr_type, rr_class, rr_ttl, rr_data, rr_rdlen)) { + log_err("could not decompress RR"); + return 0; + } + rr = sldns_buffer_begin(scratch_buffer); + rr_len = sldns_buffer_limit(scratch_buffer); + dname_len = dname_valid(rr, rr_len); + return az_insert_rr(z, rr, rr_len, dname_len, duplicate); +} + +/** remove RR from zone, from packet, decompress RR, + * if nonexist is nonNULL set the flag but otherwise ignore nonexisting entries*/ +static int +az_remove_rr_decompress(struct auth_zone* z, uint8_t* pkt, size_t pktlen, + struct sldns_buffer* scratch_buffer, uint8_t* dname, uint16_t rr_type, + uint16_t rr_class, uint32_t rr_ttl, uint8_t* rr_data, + uint16_t rr_rdlen, int* nonexist) +{ + uint8_t* rr; + size_t rr_len; + size_t dname_len; + if(!decompress_rr_into_buffer(scratch_buffer, pkt, pktlen, dname, + rr_type, rr_class, rr_ttl, rr_data, rr_rdlen)) { + log_err("could not decompress RR"); + return 0; + } + rr = sldns_buffer_begin(scratch_buffer); + rr_len = sldns_buffer_limit(scratch_buffer); + dname_len = dname_valid(rr, rr_len); + return az_remove_rr(z, rr, rr_len, dname_len, nonexist); +} + /** * Parse zonefile * @param z: zone to read in. @@ -1099,7 +1488,7 @@ az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen, continue; } /* insert wirerr in rrbuf */ - if(!az_insert_rr(z, rr, rr_len, dname_len)) { + if(!az_insert_rr(z, rr, rr_len, dname_len, NULL)) { char buf[17]; sldns_wire2str_type_buf(sldns_wirerr_get_type(rr, rr_len, dname_len), buf, sizeof(buf)); @@ -3294,7 +3683,7 @@ static int chunk_rrlist_get_current(struct auth_chunk* rr_chunk, int rr_num, size_t rr_pos, uint8_t** rr_dname, uint16_t* rr_type, uint16_t* rr_class, uint32_t* rr_ttl, uint16_t* rr_rdlen, - uint8_t** rr_rdata, uint8_t** rr_pkt, size_t* rr_nextpos) + uint8_t** rr_rdata, size_t* rr_nextpos) { sldns_buffer pkt; /* integrity checks on position */ @@ -3315,7 +3704,6 @@ chunk_rrlist_get_current(struct auth_chunk* rr_chunk, int rr_num, *rr_rdlen = sldns_buffer_read_u16(&pkt); if(sldns_buffer_remaining(&pkt) < (*rr_rdlen)) return 0; *rr_rdata = sldns_buffer_current(&pkt); - *rr_pkt = sldns_buffer_begin(&pkt); sldns_buffer_skip(&pkt, (ssize_t)(*rr_rdlen)); *rr_nextpos = sldns_buffer_position(&pkt); return 1; @@ -3323,12 +3711,13 @@ chunk_rrlist_get_current(struct auth_chunk* rr_chunk, int rr_num, /** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */ static int -apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z) +apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, + struct sldns_buffer* scratch_buffer) { struct auth_chunk* rr_chunk; int rr_num; size_t rr_pos; - uint8_t* rr_dname, *rr_rdata, *rr_pkt; + uint8_t* rr_dname, *rr_rdata; uint16_t rr_type, rr_class, rr_rdlen; uint32_t rr_ttl; size_t rr_nextpos; @@ -3343,7 +3732,7 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z) while(!chunk_rrlist_end(rr_chunk, rr_num)) { if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos, &rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen, - &rr_rdata, &rr_pkt, &rr_nextpos)) { + &rr_rdata, &rr_nextpos)) { /* failed to parse RR */ return 0; } @@ -3382,7 +3771,8 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z) * SOA 3 followed by add * SOA 3 end */ /* ended by SOA record */ - return 1; + xfr->serial = transfer_serial; + break; } } /* twiddle add/del mode */ @@ -3398,11 +3788,32 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z) * that we serve something fairly nice during the refetch */ if(delmode) { /* delete this RR */ - (void)z; - /* TODO */ + int nonexist = 0; + if(!az_remove_rr_decompress(z, rr_chunk->data, + rr_chunk->len, scratch_buffer, rr_dname, + rr_type, rr_class, rr_ttl, rr_rdata, rr_rdlen, + &nonexist)) { + /* failed, malloc error or so */ + return 0; + } + if(nonexist) { + /* it was removal of a nonexisting RR */ + softfail = 1; + } } else { + int duplicate = 0; /* add this RR */ - /* TODO */ + if(!az_insert_rr_decompress(z, rr_chunk->data, + rr_chunk->len, scratch_buffer, rr_dname, + rr_type, rr_class, rr_ttl, rr_rdata, rr_rdlen, + &duplicate)) { + /* failed, malloc error or so */ + return 0; + } + if(duplicate) { + /* it was a duplicate */ + softfail = 1; + } } rr_counter++; @@ -3414,10 +3825,59 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z) /** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */ static int -apply_axfr(struct auth_xfer* xfr, struct auth_zone* z) +apply_axfr(struct auth_xfer* xfr, struct auth_zone* z, + struct sldns_buffer* scratch_buffer) { - /* TODO */ - (void)xfr; (void)z; + struct auth_chunk* rr_chunk; + int rr_num; + size_t rr_pos; + uint8_t* rr_dname, *rr_rdata; + uint16_t rr_type, rr_class, rr_rdlen; + uint32_t rr_ttl; + uint32_t serial = 0; + size_t rr_nextpos; + size_t rr_counter = 0; + + /* clear the data tree */ + traverse_postorder(&z->data, auth_data_del, NULL); + rbtree_init(&z->data, &auth_data_cmp); + xfr->have_zone = 0; + xfr->serial = 0; + + /* insert all RRs in to the zone */ + /* insert the SOA only once, skip the last one */ + /* start RR iterator over chunklist of packets */ + chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos); + while(!chunk_rrlist_end(rr_chunk, rr_num)) { + if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos, + &rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen, + &rr_rdata, &rr_nextpos)) { + /* failed to parse RR */ + return 0; + } + if(rr_type == LDNS_RR_TYPE_SOA) { + if(rr_counter != 0) { + /* end of the axfr */ + break; + } + if(rr_rdlen < 22) return 0; /* bad SOA rdlen */ + serial = sldns_read_uint32(rr_rdata+rr_rdlen-20); + } + + /* add this RR */ + if(!az_insert_rr_decompress(z, rr_chunk->data, rr_chunk->len, + scratch_buffer, rr_dname, rr_type, rr_class, rr_ttl, + rr_rdata, rr_rdlen, NULL)) { + /* failed, malloc error or so */ + return 0; + } + + rr_counter++; + chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos); + } + + xfr->serial = serial; + xfr->have_zone = 1; return 1; } @@ -3494,7 +3954,7 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env, /* apply data */ if(xfr->task_transfer->on_ixfr && !xfr->task_transfer->on_ixfr_is_axfr) { - if(!apply_ixfr(xfr, z)) { + if(!apply_ixfr(xfr, z, env->scratch_buffer)) { lock_rw_unlock(&z->lock); verbose(VERB_ALGO, "xfr from %s: could not store IXFR" " data", xfr->task_transfer->master->host); @@ -3502,13 +3962,21 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env, return 0; } } else { - if(!apply_axfr(xfr, z)) { + if(!apply_axfr(xfr, z, env->scratch_buffer)) { lock_rw_unlock(&z->lock); verbose(VERB_ALGO, "xfr from %s: could not store AXFR" " data", xfr->task_transfer->master->host); return 0; } } + if(!xfr_find_soa(z, xfr)) { + lock_rw_unlock(&z->lock); + verbose(VERB_ALGO, "xfr from %s: no SOA in zone after update" + " (or malformed RR)", xfr->task_transfer->master->host); + return 0; + } + xfr->zone_expired = 0; + z->zone_expired = 0; /* unlock */ lock_rw_unlock(&z->lock);