mirror of https://github.com/getdnsapi/getdns.git
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.
This commit is contained in:
parent
291e001881
commit
7b12e21ad0
|
@ -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);
|
||||
|
|
102
src/rr-dict.c
102
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));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue