#include <config.h>
#include <isc/buffer.h>
#include <isc/util.h>
#include <dns/db.h>
#include <dns/message.h>
#include <dns/ncache.h>
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
static inline isc_result_t
copy_rdataset(dns_rdataset_t *rdataset, isc_buffer_t *buffer) {
isc_result_t result;
unsigned int count;
isc_region_t ar, r;
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_buffer_availableregion(buffer, &ar);
if (ar.length < 2)
return (ISC_R_NOSPACE);
count = dns_rdataset_count(rdataset);
INSIST(count <= 65535);
isc_buffer_putuint16(buffer, (isc_uint16_t)count);
result = dns_rdataset_first(rdataset);
while (result == ISC_R_SUCCESS) {
dns_rdataset_current(rdataset, &rdata);
dns_rdata_toregion(&rdata, &r);
INSIST(r.length <= 65535);
isc_buffer_availableregion(buffer, &ar);
if (ar.length < 2)
return (ISC_R_NOSPACE);
isc_buffer_putuint16(buffer, (isc_uint16_t)r.length);
result = isc_buffer_copyregion(buffer, &r);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdata_reset(&rdata);
result = dns_rdataset_next(rdataset);
}
if (result != ISC_R_NOMORE)
return (result);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
dns_rdataset_t *addedrdataset)
{
isc_result_t result;
isc_buffer_t buffer;
isc_region_t r;
dns_rdataset_t *rdataset;
dns_rdatatype_t type;
dns_name_t *name;
dns_ttl_t ttl;
dns_trust_t trust;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdataset_t ncrdataset;
dns_rdatalist_t ncrdatalist;
unsigned char data[4096];
REQUIRE(message != NULL);
ttl = maxttl;
trust = 0xffff;
isc_buffer_init(&buffer, data, sizeof(data));
result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
while (result == ISC_R_SUCCESS) {
name = NULL;
dns_message_currentname(message, DNS_SECTION_AUTHORITY,
&name);
if ((name->attributes & DNS_NAMEATTR_NCACHE) != 0) {
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
if ((rdataset->attributes &
DNS_RDATASETATTR_NCACHE) == 0)
continue;
type = rdataset->type;
if (type == dns_rdatatype_sig)
type = rdataset->covers;
if (type == dns_rdatatype_soa ||
type == dns_rdatatype_nxt) {
if (ttl > rdataset->ttl)
ttl = rdataset->ttl;
if (trust > rdataset->trust)
trust = rdataset->trust;
dns_name_toregion(name, &r);
result = isc_buffer_copyregion(&buffer,
&r);
if (result != ISC_R_SUCCESS)
return (result);
isc_buffer_availableregion(&buffer,
&r);
if (r.length < 2)
return (ISC_R_NOSPACE);
isc_buffer_putuint16(&buffer,
rdataset->type);
result = copy_rdataset(rdataset,
&buffer);
if (result != ISC_R_SUCCESS)
return (result);
}
}
}
result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
}
if (result != ISC_R_NOMORE)
return (result);
if (trust == 0xffff) {
dns_name_toregion(dns_rootname, &r);
result = isc_buffer_copyregion(&buffer, &r);
if (result != ISC_R_SUCCESS)
return (result);
isc_buffer_availableregion(&buffer, &r);
if (r.length < 4)
return (ISC_R_NOSPACE);
isc_buffer_putuint16(&buffer, 0);
isc_buffer_putuint16(&buffer, 0);
ttl = 0;
if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 &&
message->counts[DNS_SECTION_ANSWER] == 0) {
trust = dns_trust_authauthority;
} else
trust = dns_trust_additional;
}
INSIST(trust != 0xffff);
isc_buffer_usedregion(&buffer, &r);
rdata.data = r.base;
rdata.length = r.length;
rdata.rdclass = dns_db_class(cache);
rdata.type = 0;
rdata.flags = 0;
ncrdatalist.rdclass = rdata.rdclass;
ncrdatalist.type = 0;
ncrdatalist.covers = covers;
ncrdatalist.ttl = ttl;
ISC_LIST_INIT(ncrdatalist.rdata);
ISC_LINK_INIT(&ncrdatalist, link);
ISC_LIST_APPEND(ncrdatalist.rdata, &rdata, link);
dns_rdataset_init(&ncrdataset);
dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset);
ncrdataset.trust = trust;
return (dns_db_addrdataset(cache, node, NULL, now, &ncrdataset,
0, addedrdataset));
}
isc_result_t
dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
isc_buffer_t *target, unsigned int *countp)
{
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_result_t result;
isc_region_t remaining, tavailable;
isc_buffer_t source, savedbuffer, rdlen;
dns_name_t name;
dns_rdatatype_t type;
unsigned int i, rcount, count;
REQUIRE(rdataset != NULL);
REQUIRE(rdataset->type == 0);
result = dns_rdataset_first(rdataset);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_current(rdataset, &rdata);
INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
isc_buffer_init(&source, rdata.data, rdata.length);
isc_buffer_add(&source, rdata.length);
savedbuffer = *target;
count = 0;
do {
dns_name_init(&name, NULL);
isc_buffer_remainingregion(&source, &remaining);
dns_name_fromregion(&name, &remaining);
INSIST(remaining.length >= name.length);
isc_buffer_forward(&source, name.length);
remaining.length -= name.length;
INSIST(remaining.length >= 4);
type = isc_buffer_getuint16(&source);
rcount = isc_buffer_getuint16(&source);
for (i = 0; i < rcount; i++) {
isc_buffer_remainingregion(&source, &remaining);
INSIST(remaining.length >= 2);
dns_rdata_reset(&rdata);
rdata.length = isc_buffer_getuint16(&source);
isc_buffer_remainingregion(&source, &remaining);
rdata.data = remaining.base;
rdata.type = type;
rdata.rdclass = rdataset->rdclass;
INSIST(remaining.length >= rdata.length);
isc_buffer_forward(&source, rdata.length);
dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
result = dns_name_towire(&name, cctx, target);
if (result != ISC_R_SUCCESS)
goto rollback;
isc_buffer_availableregion(target, &tavailable);
if (tavailable.length < 10) {
result = ISC_R_NOSPACE;
goto rollback;
}
isc_buffer_putuint16(target, type);
isc_buffer_putuint16(target, rdataset->rdclass);
isc_buffer_putuint32(target, rdataset->ttl);
rdlen = *target;
isc_buffer_add(target, 2);
result = dns_rdata_towire(&rdata, cctx, target);
if (result != ISC_R_SUCCESS)
goto rollback;
INSIST((target->used >= rdlen.used + 2) &&
(target->used - rdlen.used - 2 < 65536));
isc_buffer_putuint16(&rdlen,
(isc_uint16_t)(target->used -
rdlen.used - 2));
count++;
}
isc_buffer_remainingregion(&source, &remaining);
} while (remaining.length > 0);
*countp = count;
return (ISC_R_SUCCESS);
rollback:
INSIST(savedbuffer.used < 65536);
dns_compress_rollback(cctx, (isc_uint16_t)savedbuffer.used);
*countp = 0;
*target = savedbuffer;
return (result);
}