From 7b12e21ad0eeaeec50ebc79ef0ed00c45037217f Mon Sep 17 00:00:00 2001 From: Andrzej Mialkowski Date: Sun, 9 May 2021 14:45:16 -0700 Subject: [PATCH 1/2] Implement name compression during response packet generation. Compression is required by certain clients like UDP to fit response in packet size limit. While generating packet small cache stores recently used names (currently 4 entries) and uses relative references to previous instances of the same name. Each reused instance is just two bytes of relative reference (0xC000 + offset). Cache is currently performing lookup for query name, responses and CNAMEs. --- src/convert.c | 5 ++- src/rr-dict.c | 102 +++++++++++++++++++++++++++++++++++--------------- src/rr-dict.h | 21 ++++++++++- 3 files changed, 93 insertions(+), 35 deletions(-) diff --git a/src/convert.c b/src/convert.c index 8dc44ddf..6dbd429a 100644 --- a/src/convert.c +++ b/src/convert.c @@ -823,6 +823,7 @@ _getdns_reply_dict2wire( getdns_list *section; getdns_dict *rr_dict; getdns_bindata *qname; + name_cache_t name_cache = {0}; int remove_dnssec; pkt_start = gldns_buffer_position(buf); @@ -852,7 +853,7 @@ _getdns_reply_dict2wire( if (!getdns_dict_get_bindata(reply, "/question/qname", &qname) && !getdns_dict_get_int(reply, "/question/qtype", &qtype)) { (void)getdns_dict_get_int(reply, "/question/qclass", &qclass); - gldns_buffer_write(buf, qname->data, qname->size); + _getdns_rr_buffer_write_cached_name(buf, qname, &name_cache); gldns_buffer_write_u16(buf, (uint16_t)qtype); gldns_buffer_write_u16(buf, (uint16_t)qclass); gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_QDCOUNT_OFF, 1); @@ -875,7 +876,7 @@ _getdns_reply_dict2wire( !getdns_dict_get_int(rr_dict, "type", &rr_type) && rr_type == GETDNS_RRTYPE_RRSIG) continue; - if (!_getdns_rr_dict2wire(rr_dict, buf)) + if (!_getdns_rr_dict2wire_cache(rr_dict, buf, &name_cache)) n++; } gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_ANCOUNT_OFF, n); diff --git a/src/rr-dict.c b/src/rr-dict.c index fc675830..cf666b48 100644 --- a/src/rr-dict.c +++ b/src/rr-dict.c @@ -1293,8 +1293,39 @@ write_rdata_field(gldns_buffer *buf, uint8_t *rdata_start, return r != GETDNS_RETURN_NO_SUCH_LIST_ITEM ? r : GETDNS_RETURN_GOOD; } +void +_getdns_rr_buffer_write_cached_name(gldns_buffer *buf, getdns_bindata *name, name_cache_t *name_cache) +{ + size_t name_size = name->size; + uint8_t *name_data = name->data; + if((NULL != name_cache) && (name_size > 2)) { + unsigned count = name_cache->count; + name_cache_entry_t *entry_ptr = &name_cache->entry[(count < NAME_CACHE_ENTRIES)?count:NAME_CACHE_ENTRIES]; + name_cache_entry_t *table_start = &name_cache->entry[0]; + /* Search backward if name is already in cache */ + while(entry_ptr-- > table_start) { + if((entry_ptr->name->size == name_size) && + !memcmp(entry_ptr->name->data, name_data, name_size)) { + gldns_buffer_write_u16(buf, (uint16_t)(0xc000 | entry_ptr->name_offset)); + return; + } + } + unsigned name_offset = gldns_buffer_position(buf); + if (name_offset < 0xc000) { + /* Cache name */ + entry_ptr = &name_cache->entry[count % NAME_CACHE_ENTRIES]; + entry_ptr->name = name; + entry_ptr->name_offset = name_offset; + name_cache->count = count + 1; + } + } + gldns_buffer_write(buf, name_data, name_size); + return; +} + getdns_return_t -_getdns_rr_dict2wire(const getdns_dict *rr_dict, gldns_buffer *buf) +_getdns_rr_dict2wire_cache(const getdns_dict *rr_dict, gldns_buffer *buf, + name_cache_t *name_cache) { getdns_return_t r = GETDNS_RETURN_GOOD; getdns_bindata root = { 1, (void *)"" }; @@ -1325,7 +1356,7 @@ _getdns_rr_dict2wire(const getdns_dict *rr_dict, gldns_buffer *buf) } else return r; } - gldns_buffer_write(buf, name->data, name->size); + _getdns_rr_buffer_write_cached_name(buf, name, name_cache); gldns_buffer_write_u16(buf, (uint16_t)rr_type); (void) getdns_dict_get_int(rr_dict, "class", &rr_class); @@ -1378,42 +1409,51 @@ _getdns_rr_dict2wire(const getdns_dict *rr_dict, gldns_buffer *buf) gldns_buffer_skip(buf, 2); rdata_start = gldns_buffer_current(buf); - for ( rd_def = rr_def->rdata - , n_rdata_fields = rr_def->n_rdata_fields - ; n_rdata_fields ; n_rdata_fields-- , rd_def++ ) { + /* Special case CNAME payload */ + if((rr_type == GETDNS_RRTYPE_CNAME) && (n_rdata_fields == 1) && + (rd_def->type & GETDNS_RDF_BINDATA) && !(rd_def->type & GETDNS_RDF_REPEAT) && + (GETDNS_RETURN_GOOD == (r = getdns_dict_get_bindata(rdata, rd_def->name, &rdata_raw)))) { - if (rd_def->type == GETDNS_RDF_REPEAT) - break; + _getdns_rr_buffer_write_cached_name(buf, rdata_raw, name_cache); + } else { - if ((r = write_rdata_field(buf, - rdata_start, rd_def, rdata))) - break; - } - if (n_rdata_fields == 0 || r) { - /* pass */; + for ( rd_def = rr_def->rdata + , n_rdata_fields = rr_def->n_rdata_fields + ; n_rdata_fields ; n_rdata_fields-- , rd_def++ ) { - } else if ((r = getdns_dict_get_list( - rdata, rd_def->name, &list))) { - /* pass */; - - } else for ( i = 0 - ; r == GETDNS_RETURN_GOOD - ; i++) { - - if ((r = getdns_list_get_dict(list, i, &rdata))) { - if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM) - r = GETDNS_RETURN_GOOD; - break; - } - for ( rep_rd_def = rd_def + 1 - , rep_n_rdata_fields = n_rdata_fields - 1 - ; rep_n_rdata_fields - ; rep_n_rdata_fields--, rep_rd_def++ ) { + if (rd_def->type == GETDNS_RDF_REPEAT) + break; if ((r = write_rdata_field(buf, - rdata_start, rep_rd_def, rdata))) + rdata_start, rd_def, rdata))) break; } + if (n_rdata_fields == 0 || r) { + /* pass */; + + } else if ((r = getdns_dict_get_list( + rdata, rd_def->name, &list))) { + /* pass */; + + } else for ( i = 0 + ; r == GETDNS_RETURN_GOOD + ; i++) { + + if ((r = getdns_list_get_dict(list, i, &rdata))) { + if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM) + r = GETDNS_RETURN_GOOD; + break; + } + for ( rep_rd_def = rd_def + 1 + , rep_n_rdata_fields = n_rdata_fields - 1 + ; rep_n_rdata_fields + ; rep_n_rdata_fields--, rep_rd_def++ ) { + + if ((r = write_rdata_field(buf, + rdata_start, rep_rd_def, rdata))) + break; + } + } } gldns_buffer_write_u16_at(buf, rdata_size_mark, (uint16_t)(gldns_buffer_position(buf)-rdata_size_mark-2)); diff --git a/src/rr-dict.h b/src/rr-dict.h index e19e0387..f0788021 100644 --- a/src/rr-dict.h +++ b/src/rr-dict.h @@ -143,8 +143,25 @@ typedef struct _getdns_rr_def { const _getdns_rr_def *_getdns_rr_def_lookup(uint16_t rr_type); -getdns_return_t _getdns_rr_dict2wire( - const getdns_dict *rr_dict, gldns_buffer *buf); +#define NAME_CACHE_ENTRIES 4 + +typedef struct __name_cache_entry { + getdns_bindata *name; + unsigned name_offset; +} name_cache_entry_t; + +typedef struct __name_cache { + unsigned count; + name_cache_entry_t entry[NAME_CACHE_ENTRIES]; +} name_cache_t; + +void _getdns_rr_buffer_write_cached_name( + gldns_buffer *buf, getdns_bindata *name, name_cache_t *name_cache); + +getdns_return_t _getdns_rr_dict2wire_cache( + const getdns_dict *rr_dict, gldns_buffer *buf, name_cache_t *name_cache); + +#define _getdns_rr_dict2wire(d, b) _getdns_rr_dict2wire_cache((d),(b), NULL) const char *_getdns_rr_type_name(int rr_type); From 8b558afde07f733a73333c69d152a13376148494 Mon Sep 17 00:00:00 2001 From: Andrzej Mialkowski Date: Sat, 15 May 2021 13:20:30 -0700 Subject: [PATCH 2/2] Fixes #505. Disable packet size checking. --- src/test/tpkg/265-supported-rrs.tpkg/265-supported-rrs.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/tpkg/265-supported-rrs.tpkg/265-supported-rrs.test b/src/test/tpkg/265-supported-rrs.tpkg/265-supported-rrs.test index 30636390..d97b013a 100644 --- a/src/test/tpkg/265-supported-rrs.tpkg/265-supported-rrs.test +++ b/src/test/tpkg/265-supported-rrs.tpkg/265-supported-rrs.test @@ -11,5 +11,5 @@ elif ! ( "./${TPKG_NAME}" "${TPKG_NAME}.net-dns.org" | tee out ) then exit 1 else - diff out "${TPKG_NAME}.good" + diff out "${TPKG_NAME}.good" --ignore-matching-lines=";; MSG SIZE rcvd: " fi