Commit ab3b6e9a authored by Mark Karpilovskij's avatar Mark Karpilovskij Committed by Daniel Salzman

libknot/packet: compression algorithm improved

The previous compression algorithm does not compress everything it can
in cases where there are domain names in the data section of a RR, e.g.
when the answer contains

bar.example.com  CNAME  foo.example.com
foo.example.com  A      192.0.0.2

and foo.example.com was not encountered previously, then the second RR
leaves the 'foo' part uncompressed.

The improvement resolves some of these cases by comparing the owner of a new RRset to be written
with the last written dname instead of QNAME. Also, the effectivity was slightly increased
according to benchmarks.
parent abd21e95
......@@ -533,7 +533,7 @@ bool knot_dname_is_sub(const knot_dname_t *sub, const knot_dname_t *domain)
int common = knot_dname_align(&sub, sub_l, &domain, domain_l, NULL);
/* Compare common suffix. */
while(common > 0) {
while (common > 0) {
/* Compare label. */
if (!label_is_equal(sub, domain))
return false;
......@@ -689,7 +689,10 @@ int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2)
_public_
bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2)
{
while(*d1 != '\0' || *d2 != '\0') {
assert(d1);
assert(d2);
while (*d1 != '\0' || *d2 != '\0') {
if (label_is_equal(d1, d2)) {
d1 = knot_wire_next_label(d1, NULL);
d2 = knot_wire_next_label(d2, NULL);
......@@ -701,6 +704,28 @@ bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2)
return true;
}
/*----------------------------------------------------------------------------*/
_public_
bool knot_dname_label_is_equal(const uint8_t *label1, const uint8_t *label2)
{
assert(label1);
assert(label2);
/* Check that they have the same length */
if (*label1 != *label2) {
return false;
}
uint8_t len = *label1;
for (uint8_t i = 1; i <= len; i++) {
if (knot_tolower(label1[i]) != knot_tolower(label2[i])) {
return false;
}
}
return true;
}
/*----------------------------------------------------------------------------*/
_public_
knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2)
......
......@@ -281,7 +281,7 @@ knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned label
void knot_dname_free(knot_dname_t **name, knot_mm_t *mm);
/*!
* \brief Compares two domain names by labels (case sensitive).
* \brief Compares two domain names by labels (case insensitive).
*
* \warning Since it would be hard to catch errors, because negative value
* is also a good result, there are assertions that expect neither
......@@ -300,6 +300,8 @@ int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2);
/*!
* \brief Compares two domain names (case sensitive).
*
* \warning d1 and d2 must not be NULL.
*
* \param d1 First domain name.
* \param d2 Second domain name.
*
......@@ -309,6 +311,20 @@ int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2);
_pure_
bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2);
/*!
* \brief Compares two domain name labels (case insensitive).
*
* \warning label1 and label2 must not be NULL.
*
* \param label1 First label.
* \param label2 Second label.
*
* \retval true if the labels are identical
* \retval false if the labels are NOT identical
*/
_pure_
bool knot_dname_label_is_equal(const uint8_t *label1, const uint8_t *label2);
/*!
* \brief Concatenates two domain names.
*
......
......@@ -22,26 +22,6 @@
#include "libknot/packet/pkt.h"
#include "contrib/tolower.h"
/*! \brief Case insensitive label compare for compression. */
static bool compr_label_match(const uint8_t *n, const uint8_t *p)
{
assert(n);
assert(p);
if (*n != *p) {
return false;
}
uint8_t len = *n;
for (uint8_t i = 1; i <= len; ++i) {
if (knot_tolower(n[i]) != knot_tolower(p[i])) {
return false;
}
}
return true;
}
/*! \brief Helper for \fn knot_compr_put_dname, writes label(s) with size checks. */
#define WRITE_LABEL(dst, written, label, max, len) \
if ((written) + (len) > (max)) { \
......@@ -96,7 +76,7 @@ int knot_compr_put_dname(const knot_dname_t *dname, uint8_t *dst, uint16_t max,
const knot_dname_t *next_suffix = knot_wire_next_label(suffix, compr->wire);
/* Two labels match, extend suffix length. */
if (!compr_label_match(dname, suffix)) {
if (!knot_dname_label_is_equal(dname, suffix)) {
/* If they don't match, write unmatched labels. */
uint16_t mismatch_len = (dname - match_begin) + (*dname + 1);
WRITE_LABEL(dst, written, match_begin, max, mismatch_len);
......
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -171,6 +171,19 @@ static int pkt_rr_array_alloc(knot_pkt_t *pkt, uint16_t count)
return KNOT_EOK;
}
static void compr_clear(knot_compr_t *compr)
{
compr->rrinfo = NULL;
compr->suffix.pos = 0;
compr->suffix.labels = 0;
}
static void compr_init(knot_compr_t *compr, uint8_t *wire)
{
compr_clear(compr);
compr->wire = wire;
}
/*! \brief Clear the packet and switch wireformat pointers (possibly allocate new). */
static int pkt_init(knot_pkt_t *pkt, void *wire, uint16_t len, knot_mm_t *mm)
{
......@@ -189,6 +202,9 @@ static int pkt_init(knot_pkt_t *pkt, void *wire, uint16_t len, knot_mm_t *mm)
pkt_wire_set(pkt, wire, len);
}
/* Initialize compression context. */
compr_init(&pkt->compr, pkt->wire);
return ret;
}
......@@ -355,6 +371,9 @@ void knot_pkt_clear(knot_pkt_t *pkt)
/* Reset to header size. */
pkt->size = KNOT_WIRE_HEADER_SIZE;
memset(pkt->wire, 0, pkt->size);
/* Clear compression context. */
compr_clear(&pkt->compr);
}
_public_
......@@ -539,16 +558,16 @@ int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr,
uint8_t *pos = pkt->wire + pkt->size;
size_t maxlen = pkt_remaining(pkt);
/* Create compression context. */
knot_compr_t compr;
compr.wire = pkt->wire;
compr.rrinfo = rrinfo;
compr.suffix.pos = KNOT_WIRE_HEADER_SIZE;
compr.suffix.labels = knot_dname_labels(compr.wire + compr.suffix.pos,
compr.wire);
/* Initialize compression context if it did not happen yet. */
pkt->compr.rrinfo = rrinfo;
if (pkt->compr.suffix.pos == 0) {
pkt->compr.suffix.pos = KNOT_WIRE_HEADER_SIZE;
pkt->compr.suffix.labels = knot_dname_labels(pkt->compr.wire + pkt->compr.suffix.pos,
pkt->compr.wire);
}
/* Write RRSet to wireformat. */
ret = knot_rrset_to_wire(rr, pos, maxlen, &compr);
ret = knot_rrset_to_wire(rr, pos, maxlen, &pkt->compr);
if (ret < 0) {
/* Truncate packet if required. */
if (ret == KNOT_ESPACE && !(flags & KNOT_PF_NOTRUNC)) {
......
......@@ -94,6 +94,8 @@ struct knot_pkt {
knot_rrset_t *rr;
knot_mm_t mm; /*!< Memory allocation context. */
knot_compr_t compr; /*!< Compression context. */
};
/*!
......
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -44,6 +44,29 @@ static uint16_t dname_max(size_t wire_avail)
return MIN(wire_avail, KNOT_DNAME_MAXLEN);
}
/*!
* Case insensitive comparison of two dnames in wire format.
* The second name may be compressed in a supplied wire.
*/
static bool dname_equal_wire(const knot_dname_t *d1, const knot_dname_t *d2,
const uint8_t *wire)
{
assert(d1);
assert(d2);
d2 = knot_wire_seek_label(d2, wire);
while (*d1 != '\0' || *d2 != '\0') {
if (!knot_dname_label_is_equal(d1, d2)) {
return false;
}
d1 = knot_wire_next_label(d1, NULL);
d2 = knot_wire_next_label(d2, wire);
}
return true;
}
/*!
* \brief Get compression pointer for a given hint.
*/
......@@ -312,14 +335,32 @@ static int write_owner(const knot_rrset_t *rrset, uint8_t **dst, size_t *dst_ava
if (owner_pointer > 0) {
knot_wire_put_pointer(*dst, owner_pointer);
} else {
int written = knot_compr_put_dname(rrset->owner, *dst,
dname_max(*dst_avail), compr);
if (written < 0) {
return written;
/* Check for coincidence with previous RR set */
if (compr != NULL &&
compr->suffix.pos != 0 &&
dname_equal_wire(rrset->owner,
compr->wire + compr->suffix.pos, compr->wire)) {
knot_wire_put_pointer(*dst, compr->suffix.pos);
compr_set_ptr(compr, KNOT_COMPR_HINT_OWNER,
compr->wire + compr->suffix.pos, owner_size);
owner_size = sizeof(uint16_t);
} else {
if (compr != NULL) {
compr->suffix.pos = KNOT_WIRE_HEADER_SIZE;
compr->suffix.labels = knot_dname_labels(compr->wire + compr->suffix.pos,
compr->wire);
}
int written = knot_compr_put_dname(rrset->owner, *dst,
dname_max(*dst_avail), compr);
if (written < 0) {
return written;
}
compr_set_ptr(compr, KNOT_COMPR_HINT_OWNER, *dst, written);
owner_size = written;
}
compr_set_ptr(compr, KNOT_COMPR_HINT_OWNER, *dst, written);
owner_size = written;
}
/* Update buffer */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment