Commit 52637f59 authored by Mark Andrews's avatar Mark Andrews
Browse files

Add wire compression.

parent 2429685d
......@@ -75,6 +75,7 @@ dump_packet(unsigned char *buf, u_int len)
dns_message_t message;
dns_result_t result;
isc_buffer_t source, target;
unsigned int i;
rdcount = 0;
rlcount = 0;
......@@ -83,6 +84,17 @@ dump_packet(unsigned char *buf, u_int len)
dctx.allowed = DNS_COMPRESS_GLOBAL14;
dns_name_init(&dctx.owner_name, NULL);
for (i = 0 ; i < len ; /* */ ) {
fprintf(stdout, "%02x", buf[i]);
if ((++i % 20) == 0)
fputs("\n", stdout);
else
if (i == len)
fputs("\n", stdout);
else
fputs(" ", stdout);
}
isc_buffer_init(&source, buf, len, ISC_BUFFERTYPE_BINARY);
isc_buffer_add(&source, len);
isc_buffer_init(&target, t, sizeof(t), ISC_BUFFERTYPE_BINARY);
......@@ -144,8 +156,9 @@ resolve_packet(dns_db_t *db, isc_buffer_t *source, isc_buffer_t *target)
dctx.allowed = DNS_COMPRESS_GLOBAL14;
dns_name_init(&dctx.owner_name, NULL);
cctx.allowed = DNS_COMPRESS_GLOBAL14;
dns_name_init(&cctx.owner_name, NULL);
result = dns_compress_init(&cctx, -1, db->mctx);
if (result != DNS_R_SUCCESS)
return (result);
/*
* Expand the name requested into buffer (tbuf)
......@@ -244,6 +257,7 @@ resolve_packet(dns_db_t *db, isc_buffer_t *source, isc_buffer_t *target)
target->used = oldused;
}
}
dns_compress_invalidate(&cctx);
return (DNS_R_SUCCESS);
}
......@@ -98,8 +98,8 @@ main(int argc, char *argv[]) {
}
}
memset(&cctx, '0', sizeof cctx);
memset(&dctx, '0', sizeof dctx);
dctx.allowed = DNS_COMPRESS_ALL;
RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
RUNTIME_CHECK(isc_lex_create(mctx, 256, &lex) == ISC_R_SUCCESS);
......@@ -187,6 +187,7 @@ main(int argc, char *argv[]) {
dns_rdata_init(&rdata);
isc_buffer_init(&dbuf, inbuf, sizeof(inbuf),
ISC_BUFFERTYPE_BINARY);
RUNTIME_CHECK(dns_compress_init(&cctx, -1, mctx) == DNS_R_SUCCESS);
result = dns_rdata_fromtext(&rdata, class, type, lex,
NULL, ISC_FALSE, &dbuf, NULL);
if (result != DNS_R_SUCCESS) {
......@@ -194,6 +195,7 @@ main(int argc, char *argv[]) {
"dns_rdata_fromtext returned %s(%d)\n",
dns_result_totext(result), result);
fflush(stdout);
dns_compress_invalidate(&cctx);
continue;
}
if (raw) {
......@@ -219,6 +221,7 @@ main(int argc, char *argv[]) {
fprintf(stdout,
"dns_rdata_towire returned %s(%d)\n",
dns_result_totext(result), result);
dns_compress_invalidate(&cctx);
continue;
}
len = wbuf.used - wbuf.current;
......@@ -257,6 +260,7 @@ main(int argc, char *argv[]) {
"dns_rdata_fromwire returned %s(%d)\n",
dns_result_totext(result), result);
fflush(stdout);
dns_compress_invalidate(&cctx);
continue;
}
}
......@@ -285,6 +289,7 @@ main(int argc, char *argv[]) {
fprintf(stdout, "\"%.*s\"\n",
(int)tbuf.used, (char*)tbuf.base);
fflush(stdout);
dns_compress_invalidate(&cctx);
if (lasttype == type) {
fprintf(stdout, "dns_rdata_compare = %d\n",
dns_rdata_compare(&rdata, &last));
......
......@@ -5,7 +5,7 @@ Overview.
BIND 4.x and BIND 8.x only had one methods of compression to deal
with 14 bit compression. BIND 9 has 3 methods of compression
to deal with 14 bit, 16 bit and local compression.
to deal with 14 bit, 16 bit and local compression (14 and 16 bit).
In addition to this the allowed compression methods vary across
types and across client revisions thanks to EDNS.
......@@ -59,20 +59,17 @@ Implementation:
We only need to maintain one global RBT as 16 bit compression
pointers are either valid or invalid for the whole message.
Functions:
unsigned int
dns_compress_allowed(dns_rdatatype_t type, int edns,
isc_boolean_t isowner);
dns_rdata_towire() will set the allowed methods based on the
edns version.
Returns allowed compression methods based on type, edns, and whether
we are about to compress a owner name.
Functions:
dns_result_t
dns_compress_init(dns_compress_t *cctx, isc_boolean_t global16,
isc_mem_t *mctx);
dns_compress_init(dns_compress_t *cctx, int edns, isc_mem_t *mctx);
Initalises cctx to empty and sets whether 16 bit global
compression targets are to be added to the global RBT.
compression targets are to be added to the global RBT based on the
edns value.
dns_result_t
dns_compress_localinit(dns_compress_t *cctx, dns_name_t *owner,
......@@ -85,12 +82,19 @@ Functions:
Free any RBT's and make empty.
dns_compress_localinvalidate(dns_compress_t *cctx);
Free the local RBT.
void
dns_compress_setmethods(dns_compress_t *cctx, unsigned int allowed);
unsigned int
dns_compress_getmethods(dns_compress_t *cctx);
int
dns_compress_getedns(dns_compress_t *cctx);
dns_result_t
dns_name_towire(dns_name_t *name, dns_compress_t *cctx,
isc_buffer_t *target);
......@@ -109,6 +113,33 @@ Functions:
isc_mem_t *mctx; /* Required by RBT */
};
sets allowed based on the value of edns.
isc_boolean_t
dns_compress_findglobal(dns_compress_t *cctx, dns_name_t *name,
dns_name_t *prefix, dns_name_t *suffix,
isc_uint16_t *offset, isc_buffer_t *workspace);
isc_boolean_t
dns_compress_findlocal(dns_compress_t *cctx, dns_name_t *name,
dns_name_t *prefix, dns_name_t *suffix,
isc_uint16_t *offset, isc_buffer_t *workspace);
Find the best best match in the global / local RBT. Returns prefix,
suffix and offset of the bestmatch. Findglobal(), findlocal()
requires as workspace as it may be neccessary to spit a bit stream
label. The result prefix will be such that it can be added to the
wire format followed by a compression pointer pointing to offset.
Suffix is returned so that it is possible to add the compression
pointers via dns_compress_add().
void
dns_compress_add(dns_compress_t *cctx, dns_name_t *prefix,
dns_name_t *suffix, isc_uint16_t offset);
Add compression pointers pointing to lebels (if any) in prefix.
The offset to the first label is passed in offset.
Dependancy:
Requires RBT deepest match.
......
......@@ -265,6 +265,16 @@ towire_<I>typename</I>(dns_rdata_t *rdata, dns_compress_t *cctx,
towire_<I>classname_typename</I>(dns_rdata_t *rdata, dns_compress_t *cctx,
isc_buffer_t *target);<CODE>
</PRE>
<P>
<CODE>towire_<I>classname_typename</I>()</CODE> is required to set the
allowed name compression methods based on EDNS version if there is a
domain name in the rdata.
<PRE>
<CODE>if (dns_compress_getedns(cctx) >= #)
dns_compress_setmethods(cctx, DNS_COMPRESS_ALL);
else
dns_compress_setmethods(cctx, DNS_COMPRESS_LOCAL);</CODE>
</PRE>
<DL>
<DT><CODE>rdata</CODE></DT>
<DD>
......
/*
* Copyright (C) 1999 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/* $Id: compress.c,v 1.1 1999/02/22 07:23:54 marka Exp $ */
#include <config.h>
#include <isc/types.h>
#include <isc/assertions.h>
#include <isc/buffer.h>
#include <dns/compress.h>
#define CCTX_MAGIC 0x43435458U
#define VALID_CCTX(x) ((x) != NULL && (x)->magic == CCTX_MAGIC)
static void free_offset(void *offset, void *mctx);
isc_boolean_t compress_find(dns_rbt_t *root, dns_name_t *name,
dns_name_t *prefix, dns_name_t *suffix,
isc_uint16_t *offset,
isc_buffer_t *workspace);
void compress_add(dns_rbt_t *root, dns_name_t *prefix,
dns_name_t *suffix, isc_uint16_t offset,
isc_boolean_t global16, isc_mem_t *mctx);
dns_result_t
dns_compress_init(dns_compress_t *cctx, int edns, isc_mem_t *mctx)
{
dns_result_t result;
REQUIRE(cctx != NULL);
REQUIRE(mctx != NULL);
cctx->allowed = 0;
cctx->rdata = 0;
cctx->global16 = (edns >= 1) ? ISC_TRUE : ISC_FALSE;
cctx->edns = edns;
cctx->local = NULL;
cctx->global = NULL;
result = dns_rbt_create(mctx, free_offset, mctx, &cctx->global);
if (result != DNS_R_SUCCESS)
return (result);
cctx->mctx = mctx;
cctx->magic = CCTX_MAGIC;
return (DNS_R_SUCCESS);
}
dns_result_t
dns_compress_localinit(dns_compress_t *cctx, dns_name_t *owner,
isc_buffer_t *target)
{
dns_result_t result;
unsigned int labels;
unsigned int ll, wl;
unsigned int bits;
dns_name_t name;
dns_name_t prefix;
dns_name_t suffix;
dns_label_t label;
isc_uint16_t *data;
unsigned char buf[34];
unsigned char namebuf[255];
isc_buffer_t t;
isc_region_t region;
REQUIRE(VALID_CCTX(cctx));
REQUIRE(cctx->local == NULL);
REQUIRE(dns_name_isabsolute(owner) == ISC_TRUE);
REQUIRE(target != NULL);
result = dns_rbt_create(cctx->mctx, free_offset, cctx->mctx,
&cctx->local);
if (result != DNS_R_SUCCESS)
return (result);
/*
* Errors from here on are not passed back up.
*/
cctx->rdata = target->used; /* XXX layer violation */
labels = dns_name_countlabels(owner);
ll = 0;
wl = 0;
dns_name_init(&name, NULL);
dns_name_init(&prefix, NULL);
dns_name_init(&suffix, NULL);
/*
* XXX we should be adding all the logical label in a
* bit stream as well.
* See also compress_add().
*/
while (labels > 0) {
dns_name_getlabelsequence(owner, wl, labels, &name);
data = isc_mem_get(cctx->mctx, sizeof *data);
if (data != NULL)
return (DNS_R_SUCCESS);
*data = ll;
result = dns_rbt_addname(cctx->local, &name, data);
if (result != DNS_R_SUCCESS) {
isc_mem_put(cctx->mctx, data, sizeof *data);
return (DNS_R_SUCCESS);
}
labels --;
wl++;
ll++;
if (ll > 255)
return (DNS_R_SUCCESS);
dns_name_getlabel(&name, 0, &label);
if (dns_label_type(&label) != dns_labeltype_bitstring)
continue;
bits = dns_label_countbits(&label);
if (bits == 1)
continue;
INSIST(label.length < sizeof buf);
memcpy(buf, label.base, label.length);
region.base = buf;
dns_name_getlabelsequence(owner, wl, labels, &suffix);
do {
/* clear bit */
buf[2 + bits / 8] &= ~(1 << (7 - (bits % 8)));
bits--;
region.length = 2 + (bits + 7) / 8;
buf[1] = bits;
dns_name_fromregion(&prefix, &region);
isc_buffer_init(&t, namebuf, sizeof namebuf,
ISC_BUFFERTYPE_BINARY);
result = dns_name_cat(&prefix, &suffix, &name, &t);
if (result != DNS_R_SUCCESS)
return (DNS_R_SUCCESS);
data = isc_mem_get(cctx->mctx, sizeof *data);
if (data != NULL)
return (DNS_R_SUCCESS);
*data = ll;
result = dns_rbt_addname(cctx->local, &name, data);
if (result != DNS_R_SUCCESS) {
isc_mem_put(cctx->mctx, data, sizeof *data);
return (DNS_R_SUCCESS);
}
ll++;
if (ll > 255)
return (DNS_R_SUCCESS);
} while (bits > 1);
}
return (DNS_R_SUCCESS);
}
void
dns_compress_invalidate(dns_compress_t *cctx) {
REQUIRE(VALID_CCTX(cctx));
cctx->magic = 0;
if (cctx->global != NULL)
dns_rbt_destroy(&cctx->global);
if (cctx->local != NULL)
dns_rbt_destroy(&cctx->local);
cctx->allowed = 0;
cctx->rdata = 0;
cctx->global16 = ISC_FALSE;
cctx->edns = -1;
}
void
dns_compress_localinvalidate(dns_compress_t *cctx) {
REQUIRE(VALID_CCTX(cctx));
if (cctx->local != NULL)
dns_rbt_destroy(&cctx->local);
}
void
dns_compress_setmethods(dns_compress_t *cctx, unsigned int allowed) {
REQUIRE(VALID_CCTX(cctx));
if (cctx->edns >= 1 && (allowed & DNS_COMPRESS_GLOBAL14) != 0)
allowed |= DNS_COMPRESS_GLOBAL16;
cctx->allowed = allowed;
}
unsigned int
dns_compress_getmethods(dns_compress_t *cctx) {
REQUIRE(VALID_CCTX(cctx));
return (cctx->allowed);
}
int
dns_compress_getedns(dns_compress_t *cctx) {
REQUIRE(VALID_CCTX(cctx));
return (cctx->edns);
}
isc_boolean_t
dns_compress_findglobal(dns_compress_t *cctx, dns_name_t *name,
dns_name_t *prefix, dns_name_t *suffix,
isc_uint16_t *offset, isc_buffer_t *workspace)
{
REQUIRE(VALID_CCTX(cctx));
REQUIRE(dns_name_isabsolute(name) == ISC_TRUE);
REQUIRE(offset != NULL);
return (compress_find(cctx->global, name, prefix, suffix, offset,
workspace));
}
isc_boolean_t
dns_compress_findlocal(dns_compress_t *cctx, dns_name_t *name,
dns_name_t *prefix, dns_name_t *suffix,
isc_uint16_t *offset, isc_buffer_t *workspace)
{
REQUIRE(VALID_CCTX(cctx));
REQUIRE(dns_name_isabsolute(name) == ISC_TRUE);
REQUIRE(offset != NULL);
if (cctx->local == NULL)
return (ISC_FALSE);
return (compress_find(cctx->local, name, prefix, suffix, offset,
workspace));
}
void
dns_compress_add(dns_compress_t *cctx, dns_name_t *prefix,
dns_name_t *suffix, isc_uint16_t offset)
{
isc_uint16_t local;
REQUIRE(VALID_CCTX(cctx));
if (cctx->local != NULL && (cctx->allowed & DNS_COMPRESS_LOCAL) != 0) {
REQUIRE(cctx->rdata <= offset);
local = offset - cctx->rdata + 256;
compress_add(cctx->local, prefix, suffix, local, ISC_TRUE,
cctx->mctx);
}
compress_add(cctx->global, prefix, suffix, offset, cctx->global16,
cctx->mctx);
}
void
dns_compress_backout(dns_compress_t *cctx, isc_uint16_t offset) {
REQUIRE(VALID_CCTX(cctx));
/* XXX need tree walking code */
}
/***
*** Private
***/
static void
free_offset(void *offset, void *mctx) {
REQUIRE(offset != NULL);
REQUIRE(mctx != NULL);
isc_mem_put(mctx, offset, sizeof(isc_uint16_t));
}
/*
* Add the labels in prefix to RBT.
*/
void
compress_add(dns_rbt_t *root, dns_name_t *prefix, dns_name_t *suffix,
isc_uint16_t offset, isc_boolean_t global16, isc_mem_t *mctx)
{
dns_name_t name;
dns_name_t full;
dns_label_t label;
unsigned int count;
unsigned int start;
unsigned int limit;
isc_uint16_t *data;
dns_result_t result;
unsigned char buffer[255];
isc_buffer_t target;
dns_offsets_t offsets;
count = dns_name_countlabels(prefix);
limit = dns_name_isabsolute(prefix) ? 1 : 0;
start = 0;
dns_name_init(&full, offsets);
dns_name_init(&name, NULL);
while (count > limit) {
if (offset >= 16384 && !global16)
break;
dns_name_getlabelsequence(prefix, start, count, &name);
isc_buffer_init(&target, buffer, sizeof buffer,
ISC_BUFFERTYPE_BINARY);
result = dns_name_cat(&name, suffix, &full, &target);
if (result != DNS_R_SUCCESS)
return;
data = isc_mem_get(mctx, sizeof *data);
if (data == NULL)
return;
*data = offset;
result = dns_rbt_addname(root, &full, data);
if (result != DNS_R_SUCCESS) {
isc_mem_put(mctx, data, sizeof *data);
return;
}
dns_name_getlabel(&name, 0, &label);
offset += label.length;
start++;
count--;
}
}
/*
* Find the loggest match of name in root.
* If match is found return ISC_TRUE. prefix, suffix and offset
* are updated.
* If no match is found return ISC_FALSE.
* XXX should used dns_rbt_findlongestmatch() when written.
*/
isc_boolean_t
compress_find(dns_rbt_t *root, dns_name_t *name, dns_name_t *prefix,
dns_name_t *suffix, isc_uint16_t *offset,
isc_buffer_t *workspace)
{
unsigned int count;
unsigned int labels;
unsigned int start;
unsigned int bits;
isc_uint16_t *data;
dns_name_t tmpname;
dns_name_t tmpprefix;
dns_name_t tmpsuffix;
isc_region_t region;
unsigned char buf[255];
dns_label_t label;
unsigned int i, j;
dns_result_t result;
dns_bitlabel_t bit;
labels = count = dns_name_countlabels(name);
start = 0;
data = NULL;
bits = 0;
dns_name_init(&tmpname, NULL);
dns_name_init(&tmpsuffix, NULL);
dns_name_init(&tmpprefix, NULL);
/* Don't look for the root label (count == 1). */
while (count > 1) {
dns_name_getlabelsequence(name, start, count, &tmpname);
data = dns_rbt_findname(root, &tmpname);
if (data != NULL)
break;
count--;
start++;
if (workspace == NULL)
continue;
dns_name_getlabel(&tmpname, 0, &label);
if (dns_label_type(&label) != dns_labeltype_bitstring)
continue;
bits = dns_label_countbits(&label);
if (bits == 1) {
bits = 0;
continue;
}
INSIST(label.length < sizeof buf);
memcpy(buf, label.base, label.length);
region.base = buf;
dns_name_getlabelsequence(name, start, count, &tmpsuffix);
do {
/* clear lsb */
buf[2 + bits / 8] &= ~(1 << (7 - (bits % 8)));
bits--;
region.length = 2 + (bits + 7) / 8;
buf[1] = bits;
dns_name_fromregion(&tmpprefix, &region);
isc_buffer_clear(workspace);
result = dns_name_cat(&tmpprefix, &tmpsuffix,
&tmpname, workspace);
if (result != DNS_R_SUCCESS)
continue;
data = dns_rbt_findname(root, &tmpname);
if (data != NULL)
break;
if (bits == 1)
bits = 0;
} while (bits > 1);
if (data != NULL)
break;
}
if (data == NULL)
return (ISC_FALSE);
if (bits == 0) {
if (start != 0)
dns_name_getlabelsequence(name, 0, start, prefix);
dns_name_getlabelsequence(name, start, count, suffix);
*offset = *data;
return (ISC_TRUE);
}
*suffix = tmpname;
i = dns_label_countbits(&label);
j = 0;
while (bits < i) {
bit = dns_label_getbit(&label, bits);
bits++;
if (bit)
buf[2 + j / 8] |= (1 << (7 - (j % 8)));
else
buf[2 + j / 8] &= ~(1 << (7 - (j % 8)));
j++;
}
buf[1] = j;
while ((j % 8) != 0) {