/* * Copyright (C) 1999 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The format of an ncache rdata is a sequence of one or more records of * the following format: * * owner name * type * rdata count * rdata length These two occur 'rdata count' * rdata times. * */ 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; /* * Copy the rdataset count to the buffer. */ isc_buffer_available(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_available(buffer, &ar); if (ar.length < 2) return (ISC_R_NOSPACE); /* * Copy the rdata length to the buffer. */ isc_buffer_putuint16(buffer, (isc_uint16_t)r.length); /* * Copy the rdata to the buffer. */ result = isc_buffer_copyregion(buffer, &r); if (result != ISC_R_SUCCESS) return (result); 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_rdataset_t *addedrdataset) { isc_result_t result; isc_buffer_t buffer; isc_region_t r; dns_rdataset_t *rdataset; dns_rdata_t rdata; dns_rdataset_t ncrdataset; dns_rdatalist_t ncrdatalist; dns_rdatatype_t type; dns_name_t *name; dns_ttl_t ttl; char *data[4096]; dns_trust_t trust; /* * Convert the authority data from 'message' into a negative cache * rdataset, and store it in 'cache' at 'node'. */ REQUIRE(message != NULL); /* * We assume that all data in the authority section has been * validated by the caller. */ /* * First, build an ncache rdata in buffer. */ ttl = 0xffffffff; trust = 0xffff; isc_buffer_init(&buffer, data, sizeof data, ISC_BUFFERTYPE_BINARY); 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; /* * Copy the owner name to the buffer. */ dns_name_toregion(name, &r); result = isc_buffer_copyregion(&buffer, &r); if (result != ISC_R_SUCCESS) return (result); /* * Copy the type to the buffer. */ isc_buffer_available(&buffer, &r); if (r.length < 2) return (ISC_R_NOSPACE); isc_buffer_putuint16(&buffer, type); /* * Copy the rdataset into the buffer. */ 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); /* * Now, turn 'buffer' into an ncache rdataset and add it to * the cache. */ INSIST(trust != 0xffff); dns_rdata_init(&rdata); isc_buffer_used(&buffer, &r); rdata.data = r.base; rdata.length = r.length; rdata.rdclass = dns_db_class(cache); rdata.type = 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; result = dns_db_addrdataset(cache, node, NULL, now, &ncrdataset, ISC_FALSE, addedrdataset); return (result); } 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; isc_result_t result; isc_region_t remaining, tremaining; isc_buffer_t source, savedbuffer, rdlen; dns_name_t name; dns_rdatatype_t type; unsigned int i, rcount, count; /* * Convert the negative caching rdataset 'rdataset' to wire format, * compressing names as specified in 'cctx', and storing the result in * 'target'. */ 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_BUFFERTYPE_BINARY); isc_buffer_add(&source, rdata.length); if (dns_compress_getedns(cctx) >= 1) dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL); else dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14); savedbuffer = *target; count = 0; do { dns_name_init(&name, NULL); isc_buffer_remaining(&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++) { /* * Get the length of this rdata and set up an * rdata structure for it. */ isc_buffer_remaining(&source, &remaining); INSIST(remaining.length >= 2); dns_rdata_init(&rdata); rdata.length = isc_buffer_getuint16(&source); isc_buffer_remaining(&source, &remaining); rdata.data = remaining.base; rdata.type = type; rdata.rdclass = rdataset->rdclass; INSIST(remaining.length >= rdata.length); isc_buffer_forward(&source, rdata.length); /* * Write the name. */ result = dns_name_towire(&name, cctx, target); if (result != DNS_R_SUCCESS) goto rollback; /* * See if we have space for type, class, ttl, and * rdata length. Write the type, class, and ttl. */ isc_buffer_remaining(target, &tremaining); if (tremaining.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); /* * Save space for rdata length. */ rdlen = *target; isc_buffer_add(target, 2); /* * Write the rdata. */ result = dns_compress_localinit(cctx, &name, target); if (result != DNS_R_SUCCESS) goto rollback; result = dns_rdata_towire(&rdata, cctx, target); dns_compress_localinvalidate(cctx); if (result != DNS_R_SUCCESS) goto rollback; /* * Set the rdata length field to the compressed * length. */ 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_remaining(&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); }