Skip to content

Commit

Permalink
Implement name compression during response packet generation. Compres…
Browse files Browse the repository at this point in the history
…sion 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.
  • Loading branch information
amialkow committed May 9, 2021
1 parent 291e001 commit 7b12e21
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 34 deletions.
5 changes: 3 additions & 2 deletions src/convert.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
100 changes: 70 additions & 30 deletions src/rr-dict.c
Expand Up @@ -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 *)"" };
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1378,41 +1409,50 @@ _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 (rd_def->type == GETDNS_RDF_REPEAT)
break;

if ((r = getdns_list_get_dict(list, i, &rdata))) {
if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM)
r = GETDNS_RETURN_GOOD;
break;
if ((r = write_rdata_field(buf,
rdata_start, rd_def, rdata)))
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 (n_rdata_fields == 0 || r) {
/* pass */;

if ((r = write_rdata_field(buf,
rdata_start, rep_rd_def, rdata)))
} 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,
Expand Down
21 changes: 19 additions & 2 deletions src/rr-dict.h
Expand Up @@ -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);

Expand Down

0 comments on commit 7b12e21

Please sign in to comment.