diff --git a/CHANGES b/CHANGES index 02b0a65562..5fd606305c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +4718. [func] Avoid seaching for a owner name compression pointer + more than once when writing out a RRset. [RT #45802] + 4717. [bug] Treat replies with QCOUNT=0 as truncated if TC=1, FORMERR if TC=0, and log the error correctly. [RT #45836] diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h index 5b12fb6978..9bb859aeaa 100644 --- a/lib/dns/include/dns/name.h +++ b/lib/dns/include/dns/name.h @@ -739,6 +739,9 @@ dns_name_fromwire(dns_name_t *name, isc_buffer_t *source, isc_result_t dns_name_towire(const dns_name_t *name, dns_compress_t *cctx, isc_buffer_t *target); +isc_result_t +dns_name_towire2(const dns_name_t *name, dns_compress_t *cctx, + isc_buffer_t *target, isc_uint16_t *comp_offsetp); /*%< * Convert 'name' into wire format, compressing it as specified by the * compression context 'cctx', and storing the result in 'target'. diff --git a/lib/dns/name.c b/lib/dns/name.c index f56263840a..0b8c6ddf51 100644 --- a/lib/dns/name.c +++ b/lib/dns/name.c @@ -1987,6 +1987,13 @@ dns_name_fromwire(dns_name_t *name, isc_buffer_t *source, isc_result_t dns_name_towire(const dns_name_t *name, dns_compress_t *cctx, isc_buffer_t *target) +{ + return (dns_name_towire2(name, cctx, target, NULL)); +} + +isc_result_t +dns_name_towire2(const dns_name_t *name, dns_compress_t *cctx, + isc_buffer_t *target, isc_uint16_t *comp_offsetp) { unsigned int methods; isc_uint16_t offset; @@ -2005,6 +2012,23 @@ dns_name_towire(const dns_name_t *name, dns_compress_t *cctx, REQUIRE(cctx != NULL); REQUIRE(ISC_BUFFER_VALID(target)); + /* + * If this exact name was already rendered before, and the + * offset of the previously rendered name is passed to us, write + * a compression pointer directly. + */ + methods = dns_compress_getmethods(cctx); + if (comp_offsetp != NULL && *comp_offsetp < 0x4000 && + (name->attributes & DNS_NAMEATTR_NOCOMPRESS) == 0 && + (methods & DNS_COMPRESS_GLOBAL14) != 0) { + if (ISC_UNLIKELY(target->length - target->used < 2)) + return (ISC_R_NOSPACE); + offset = *comp_offsetp; + offset |= 0xc000; + isc_buffer_putuint16(target, offset); + return (ISC_R_SUCCESS); + } + /* * If 'name' doesn't have an offsets table, make a clone which * has one. @@ -2022,8 +2046,6 @@ dns_name_towire(const dns_name_t *name, dns_compress_t *cctx, offset = target->used; /*XXX*/ - methods = dns_compress_getmethods(cctx); - if ((name->attributes & DNS_NAMEATTR_NOCOMPRESS) == 0 && (methods & DNS_COMPRESS_GLOBAL14) != 0) gf = dns_compress_findglobal(cctx, name, &gp, &go); @@ -2034,7 +2056,7 @@ dns_name_towire(const dns_name_t *name, dns_compress_t *cctx, * If the offset is too high for 14 bit global compression, we're * out of luck. */ - if (gf && go >= 0x4000) + if (gf && ISC_UNLIKELY(go >= 0x4000)) gf = ISC_FALSE; /* @@ -2044,25 +2066,32 @@ dns_name_towire(const dns_name_t *name, dns_compress_t *cctx, gf = ISC_FALSE; if (gf) { - if (target->length - target->used < gp.length) + if (ISC_UNLIKELY(target->length - target->used < gp.length)) return (ISC_R_NOSPACE); (void)memmove((unsigned char *)target->base + target->used, gp.ndata, (size_t)gp.length); isc_buffer_add(target, gp.length); - go |= 0xc000; - if (target->length - target->used < 2) + if (ISC_UNLIKELY(target->length - target->used < 2)) return (ISC_R_NOSPACE); - isc_buffer_putuint16(target, go); - if (gp.length != 0) + isc_buffer_putuint16(target, go | 0xc000); + if (gp.length != 0) { dns_compress_add(cctx, name, &gp, offset); + if (comp_offsetp != NULL) + *comp_offsetp = offset; + } else if (comp_offsetp != NULL) { + *comp_offsetp = go; + } } else { - if (target->length - target->used < name->length) + if (ISC_UNLIKELY(target->length - target->used < name->length)) return (ISC_R_NOSPACE); (void)memmove((unsigned char *)target->base + target->used, name->ndata, (size_t)name->length); isc_buffer_add(target, name->length); dns_compress_add(cctx, name, name, offset); + if (comp_offsetp != NULL) + *comp_offsetp = offset; } + return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index 6355832e61..b20eadf4e6 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -324,6 +324,7 @@ towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name, struct towire_sort *out = out_fixed; dns_fixedname_t fixed; dns_name_t *name; + isc_uint16_t offset; UNUSED(state); @@ -464,6 +465,7 @@ towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name, name = dns_fixedname_name(&fixed); dns_name_copy(owner_name, name, NULL); dns_rdataset_getownercase(rdataset, name); + offset = 0xffff; name->attributes |= owner_name->attributes & DNS_NAMEATTR_NOCOMPRESS; @@ -475,7 +477,7 @@ towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name, rrbuffer = *target; dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14); - result = dns_name_towire(name, cctx, target); + result = dns_name_towire2(name, cctx, target, &offset); if (result != ISC_R_SUCCESS) goto rollback; headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t);