work on domain name compression.

git-svn-id: file:///svn/unbound/trunk@257 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-04-25 15:28:03 +00:00
parent 02f5c63635
commit f48553bc2d
12 changed files with 771 additions and 59 deletions

View file

@ -53,7 +53,7 @@ INSTALL=$(srcdir)/install-sh
COMMON_SRC=$(wildcard services/*.c util/*.c util/data/*.c util/storage/*.c) util/configparser.c util/configlexer.c testcode/checklocks.c
COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.o))
COMPAT_OBJ=$(addprefix $(BUILD)compat/,$(LIBOBJS))
UNITTEST_SRC=$(wildcard testcode/unit*.c) $(COMMON_SRC)
UNITTEST_SRC=$(wildcard testcode/unit*.c) testcode/readhex.c $(COMMON_SRC)
UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.o)) $(COMPAT_OBJ)
DAEMON_SRC=$(wildcard daemon/*.c) $(COMMON_SRC)
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.o)) $(COMPAT_OBJ)
@ -61,6 +61,8 @@ TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c daemon/worker.c daem
TESTBOUND_OBJ=$(addprefix $(BUILD),$(TESTBOUND_SRC:.c=.o)) $(COMPAT_OBJ)
LOCKVERIFY_SRC=testcode/lock_verify.c $(COMMON_SRC)
LOCKVERIFY_OBJ=$(addprefix $(BUILD),$(LOCKVERIFY_SRC:.c=.o)) $(COMPAT_OBJ)
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c $(COMMON_SRC)
PKTVIEW_OBJ=$(addprefix $(BUILD),$(PKTVIEW_SRC:.c=.o)) $(COMPAT_OBJ)
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) $(TESTBOUND_SRC) $(LOCKVERIFY_SRC)
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) $(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ)
@ -75,7 +77,7 @@ $(BUILD)%.o: $(srcdir)/%.c
.PHONY: clean realclean doc lint all
all: $(COMMON_OBJ) unbound unittest testbound lock-verify
all: $(COMMON_OBJ) unbound unittest testbound lock-verify pktview
unbound: $(DAEMON_OBJ)
$(INFO) Link $@
@ -93,6 +95,10 @@ lock-verify: $(LOCKVERIFY_OBJ)
$(INFO) Link $@
$Q$(LINK) -o $@ $^ $(LIBS)
pktview: $(PKTVIEW_OBJ)
$(INFO) Link $@
$Q$(LINK) -o $@ $^ $(LIBS)
testcode/ldns-testpkts.c: $(ldnsdir)/examples/ldns-testpkts.c \
$(ldnsdir)/examples/ldns-testpkts.h
cp $(ldnsdir)/examples/ldns-testpkts.c testcode/ldns-testpkts.c

View file

@ -1,5 +1,7 @@
25 April 2007: Wouter
- prettier code; parse_rrset->type kept in host byte order.
- datatype used for hashvalue of converted rrsig structure.
- unit test compares edns section data too.
24 April 2007: Wouter
- ttl per RR, for RRSIG rrsets and others.

192
testcode/pktview.c Normal file
View file

@ -0,0 +1,192 @@
/*
* testcode/pktview.c - debug program to disassemble a DNS packet.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This program shows a dns packet wire format.
*/
#include "config.h"
#include "util/log.h"
#include "util/data/dname.h"
#include "util/data/msgparse.h"
#include "testcode/unitmain.h"
#include "testcode/readhex.h"
/** usage information for pktview */
void usage(char* argv[])
{
printf("usage: %s\n", argv[0]);
printf("present hex packet on stdin.\n");
exit(1);
}
/** read hex input */
void read_input(ldns_buffer* pkt, FILE* in)
{
char buf[102400];
char* np = buf;
while(fgets(np, (int)sizeof(buf) - (np-buf), in)) {
if(buf[0] == ';') /* comment */
continue;
np = &np[strlen(np)];
}
hex_to_buf(pkt, buf);
}
/** analyze domain name in packet, possibly compressed. */
void analyze_dname(ldns_buffer* pkt)
{
size_t oldpos = ldns_buffer_position(pkt);
size_t len;
printf("[pos %d] dname: ", (int)oldpos);
dname_print(stdout, pkt, ldns_buffer_current(pkt));
len = pkt_dname_len(pkt);
printf(" len=%d", (int)len);
if(ldns_buffer_position(pkt)-oldpos != len)
printf(" comprlen=%d\n",
(int)(ldns_buffer_position(pkt)-oldpos));
else printf("\n");
}
/** analyze rdata in packet */
void analyze_rdata(ldns_buffer*pkt, const ldns_rr_descriptor* desc,
uint16_t rdlen)
{
int rdf = 0;
int count = (int)desc->_dname_count;
size_t len, oldpos;
while(rdlen > 0 && count) {
switch(desc->_wireformat[rdf]) {
case LDNS_RDF_TYPE_DNAME:
oldpos = ldns_buffer_position(pkt);
analyze_dname(pkt);
rdlen -= ldns_buffer_position(pkt)-oldpos;
count --;
len = 0;
break;
case LDNS_RDF_TYPE_STR:
len = ldns_buffer_current(pkt)[0] + 1;
break;
default:
len = get_rdf_size(desc->_wireformat[rdf]);
}
if(len) {
printf(" wf[%d]", (int)len);
ldns_buffer_skip(pkt, (ssize_t)len);
rdlen -= len;
}
rdf++;
}
if(rdlen)
printf(" remain[%d]\n", (int)rdlen);
else printf("\n");
ldns_buffer_skip(pkt, (ssize_t)rdlen);
}
/** analyze rr in packet. */
void analyze_rr(ldns_buffer* pkt, int q)
{
uint16_t type, dclass, len;
uint32_t ttl;
analyze_dname(pkt);
type = ldns_buffer_read_u16(pkt);
dclass = ldns_buffer_read_u16(pkt);
printf("type %s(%d)", ldns_rr_descript(type)?
ldns_rr_descript(type)->_name: "??" , (int)type);
printf(" class %s(%d) ", ldns_lookup_by_id(ldns_rr_classes,
(int)dclass)?ldns_lookup_by_id( ldns_rr_classes,
(int)dclass)->name:"??", (int)dclass);
if(q) {
printf("\n");
} else {
ttl = ldns_buffer_read_u32(pkt);
printf(" ttl %d (0x%x)", ttl, ttl);
len = ldns_buffer_read_u16(pkt);
printf(" rdata len %d:\n", len);
if(ldns_rr_descript(type))
analyze_rdata(pkt, ldns_rr_descript(type), len);
else ldns_buffer_skip(pkt, (ssize_t)len);
}
}
/** analyse pkt */
void analyze(ldns_buffer* pkt)
{
uint16_t i, f, qd, an, ns, ar;
int rrnum = 0;
printf("packet length %d\n", (int)ldns_buffer_limit(pkt));
if(ldns_buffer_limit(pkt) < 12) return;
i = ldns_buffer_read_u16(pkt);
printf("id (hostorder): %d (0x%x)\n", i, i);
f = ldns_buffer_read_u16(pkt);
printf("flags: 0x%x\n", f);
qd = ldns_buffer_read_u16(pkt);
printf("qdcount: %d\n", qd);
an = ldns_buffer_read_u16(pkt);
printf("ancount: %d\n", an);
ns = ldns_buffer_read_u16(pkt);
printf("nscount: %d\n", ns);
ar = ldns_buffer_read_u16(pkt);
printf("arcount: %d\n", ar);
printf(";-- query section\n");
while(ldns_buffer_remaining(pkt) > 0) {
if(rrnum == qd) printf(";-- answer section\n");
if(rrnum == qd+an) printf(";-- authority section\n");
if(rrnum == qd+an+ns) printf(";-- additional section\n");
printf("rr %d ", rrnum);
analyze_rr(pkt, rrnum < qd);
rrnum++;
}
}
/** main program for pktview */
int main(int argc, char* argv[])
{
ldns_buffer* pkt = ldns_buffer_new(65553);
if(argc != 1) {
usage(argv);
}
if(!pkt) fatal_exit("out of memory");
read_input(pkt, stdin);
analyze(pkt);
ldns_buffer_free(pkt);
return 0;
}

82
testcode/readhex.c Normal file
View file

@ -0,0 +1,82 @@
/*
* testcode/readhex.c - read hex data.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Declarations useful for the unit tests.
*/
#include "config.h"
#include "testcode/readhex.h"
#include "util/log.h"
/** skip whitespace */
static void
skip_whites(const char** p)
{
while(1) {
while(isspace(**p))
(*p)++;
if(**p == ';') {
/* comment, skip until newline */
while(**p && **p != '\n')
(*p)++;
if(**p == '\n')
(*p)++;
} else return;
}
}
/** takes a hex string and puts into buffer */
void hex_to_buf(ldns_buffer* pkt, const char* hex)
{
const char* p = hex;
int val;
ldns_buffer_clear(pkt);
while(*p) {
skip_whites(&p);
if(ldns_buffer_position(pkt) == ldns_buffer_limit(pkt))
fatal_exit("hex_to_buf: buffer too small");
if(!isalnum(*p))
break;
val = ldns_hexdigit_to_int(*p++) << 4;
skip_whites(&p);
log_assert(*p && isalnum(*p));
val |= ldns_hexdigit_to_int(*p++);
ldns_buffer_write_u8(pkt, (uint8_t)val);
skip_whites(&p);
}
ldns_buffer_flip(pkt);
}

47
testcode/readhex.h Normal file
View file

@ -0,0 +1,47 @@
/*
* testcode/readhex.h - read hex data.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Declarations useful for the unit tests.
*/
#ifndef TESTCODE_READHEX_H
#define TESTCODE_READHEX_H
/** helper to convert hex string to packet buffer */
void hex_to_buf(ldns_buffer* pkt, const char* hex);
#endif /* TESTCODE_READHEX_H */

View file

@ -192,6 +192,13 @@ msgreply_test()
unit_assert(query_dname_compare((uint8_t*)"\003abc\001Z",
(uint8_t*)"\003abc\001a") == 1);
unit_assert(dname_count_labels((uint8_t*)"") == 1);
unit_assert(dname_count_labels((uint8_t*)"\003com") == 2);
unit_assert(dname_count_labels((uint8_t*)"\003org") == 2);
unit_assert(dname_count_labels((uint8_t*)"\007example\003com") == 3);
unit_assert(dname_count_labels((uint8_t*)"\003bla\007example\003com")
== 4);
ldns_buffer_free(buff);
}
@ -211,11 +218,13 @@ main(int argc, char* argv[])
}
printf("Start of %s unit test.\n", PACKAGE_STRING);
checklock_start();
if(0) {
net_test();
alloc_test();
msgreply_test();
lruhash_test();
slabhash_test();
}
msgparse_test();
checklock_stop();
printf("%d checks ok.\n", testcount);

View file

@ -47,52 +47,11 @@
#include "util/alloc.h"
#include "util/region-allocator.h"
#include "util/net_help.h"
#include "testcode/readhex.h"
/** verbose message parse unit test */
static int vbmp = 0;
/** skip whitespace */
static void
skip_whites(const char** p)
{
while(1) {
while(isspace(**p))
(*p)++;
if(**p == ';') {
/* comment, skip until newline */
while(**p && **p != '\n')
(*p)++;
if(**p == '\n')
(*p)++;
} else return;
}
}
/** takes a hex string and puts into buffer */
static void hex_to_buf(ldns_buffer* pkt, const char* hex)
{
const char* p = hex;
int val;
ldns_buffer_clear(pkt);
while(*p) {
skip_whites(&p);
if(ldns_buffer_position(pkt) == ldns_buffer_limit(pkt))
fatal_exit("hex_to_buf: buffer too small");
if(!isalnum(*p))
break;
val = ldns_hexdigit_to_int(*p++) << 4;
skip_whites(&p);
log_assert(*p && isalnum(*p));
val |= ldns_hexdigit_to_int(*p++);
ldns_buffer_write_u8(pkt, (uint8_t)val);
skip_whites(&p);
}
ldns_buffer_flip(pkt);
if(vbmp) {
printf("packet size %u\n", (unsigned)ldns_buffer_limit(pkt));
}
}
/** match two rr lists */
static int
match_list(ldns_rr_list* q, ldns_rr_list *p)
@ -122,6 +81,25 @@ match_list(ldns_rr_list* q, ldns_rr_list *p)
return 1;
}
/** match edns sections */
static int
match_edns(ldns_pkt* q, ldns_pkt* p)
{
if(ldns_pkt_edns_udp_size(q) != ldns_pkt_edns_udp_size(p))
return 0;
if(ldns_pkt_edns_extended_rcode(q) != ldns_pkt_edns_extended_rcode(p))
return 0;
if(ldns_pkt_edns_version(q) != ldns_pkt_edns_version(p))
return 0;
if(ldns_pkt_edns_do(q) != ldns_pkt_edns_do(p))
return 0;
if(ldns_pkt_edns_z(q) != ldns_pkt_edns_z(p))
return 0;
if(ldns_rdf_compare(ldns_pkt_edns_data(q), ldns_pkt_edns_data(p)) != 0)
return 0;
return 1;
}
/** compare two booleans */
static int
cmp_bool(int x, int y)
@ -172,6 +150,8 @@ match_all(ldns_pkt* q, ldns_pkt* p)
{ verbose(3, "allmatch: ns section different"); return 0;}
if(!match_list(ldns_pkt_additional(q), ldns_pkt_additional(p)))
{ verbose(3, "allmatch: ar section different"); return 0;}
if(!match_edns(q, p))
{ verbose(3, "edns different."); return 0;}
return 1;
}
@ -388,7 +368,7 @@ testfromdrillfile(ldns_buffer* pkt, struct alloc_cache* alloc,
/* ;-- is used to indicate a new message */
FILE* in = fopen(fname, "r");
char buf[102400];
char *np = buf;
char* np = buf;
if(!in) {
perror("fname");
return;

View file

@ -333,3 +333,103 @@ void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname)
lablen = *dname++;
}
}
int
dname_count_labels(uint8_t* dname)
{
uint8_t lablen;
int labs = 1;
lablen = *dname++;
while(lablen) {
labs++;
dname += lablen;
lablen = *dname++;
}
return labs;
}
/**
* Compare labels in memory, lowercase while comparing.
* @param p1: label 1
* @param p2: label 2
* @param len: number of bytes to compare.
* @return: 0, -1, +1 comparison result.
*/
static int
memlowercmp(uint8_t* p1, uint8_t* p2, uint8_t len)
{
while(len--) {
if(tolower((int)*p1++) != tolower((int)*p2++)) {
if(tolower((int)p1[-1]) < tolower((int)p2[-1]))
return -1;
return 1;
}
}
return 0;
}
int
dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
{
uint8_t len1, len2;
int atlabel = labs1;
int lastmlabs;
int lastdiff = 0;
int c;
/* first skip so that we compare same label. */
if(labs1 > labs2) {
while(atlabel > labs2) {
len1 = *d1++;
d1 += len1;
atlabel--;
}
log_assert(atlabel == labs2);
} else if(labs1 < labs2) {
atlabel = labs2;
while(atlabel > labs1) {
len2 = *d2++;
d2 += len2;
atlabel--;
}
log_assert(atlabel == labs1);
}
lastmlabs = atlabel+1;
/* now at same label in d1 and d2, atlabel */
/* www.example.com. */
/* 4 3 2 1 atlabel number */
/* repeat until at root label (which is always the same) */
while(atlabel > 1) {
len1 = *d1++;
len2 = *d2++;
if(len1 != len2) {
log_assert(len1 != 0 && len2 != 0);
if(len1<len2)
lastdiff = -1;
else lastdiff = 1;
lastmlabs = atlabel;
} else if((c=memlowercmp(d1, d2, len1)) != 0) {
if(c<0)
lastdiff = -1;
else lastdiff = 1;
lastmlabs = atlabel;
}
d1 += len1;
d2 += len2;
atlabel--;
}
/* last difference atlabel number, so number of labels matching,
* at the right side, is one less. */
*mlabs = lastmlabs-1;
if(lastdiff == 0) {
/* all labels compared were equal, check if one has more
* labels, so that example.com. > com. */
if(labs1 > labs2)
return 1;
else if(labs1 < labs2)
return -1;
}
return lastdiff;
}

View file

@ -126,4 +126,23 @@ void dname_pkt_copy(ldns_buffer* pkt, uint8_t* to, uint8_t* dname);
*/
void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname);
/**
* Count the number of labels in an uncompressed dname in memory.
* @param dname: pointer to uncompressed dname.
* @return: count of labels, including root label, "com." has 2 labels.
*/
int dname_count_labels(uint8_t* dname);
/**
* Compare dnames, sorted not canonical, but by label.
* Such that zone contents follows zone apex.
* @param d1: first dname. pointer to uncompressed wireformat.
* @param labs1: number of labels in first dname.
* @param d2: second dname. pointer to uncompressed wireformat.
* @param labs2: number of labels in second dname.
* @param mlabs: number of labels that matched exactly.
* @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
*/
int dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs);
#endif /* UTIL_DATA_DNAME_H */

View file

@ -354,7 +354,7 @@ change_rrsig_rrset(struct rrset_parse* sigset, struct msg_parse* msg,
int hasother, ldns_pkt_section section, region_type* region)
{
struct rrset_parse* dataset = sigset;
hashvalue_t hash = pkt_hash_rrset(pkt, sigset->dname, sigset->type,
hashvalue_t hash = pkt_hash_rrset(pkt, sigset->dname, datatype,
sigset->rrset_class, rrset_flags);
log_assert( sigset->type == LDNS_RR_TYPE_RRSIG );
log_assert( datatype != LDNS_RR_TYPE_RRSIG );

View file

@ -486,15 +486,209 @@ reply_info_answer(struct reply_info* rep, uint16_t qflags,
ldns_buffer_flip(buffer);
}
/**
* Data structure to help domain name compression in outgoing messages.
* A tree of dnames and their offsets in the packet is kept.
* It is kept sorted, not canonical, but by label at least, so that after
* a lookup of a name you know its closest match, and the parent from that
* closest match. These are possible compression targets.
*
* It is a binary tree, not a rbtree or balanced tree, as the effort
* of keeping it balanced probably outweighs usefulness (given typical
* DNS packet size).
*/
struct compress_tree_node {
/** left node in tree, all smaller to this */
struct compress_tree_node* left;
/** right node in tree, all larger than this */
struct compress_tree_node* right;
/** the parent node - not for tree, but zone parent. One less label */
struct compress_tree_node* parent;
/** the domain name for this node. Pointer to uncompressed memory. */
uint8_t* dname;
/** number of labels in domain name, kept to help compare func. */
int labs;
/** offset in packet that points to this dname */
size_t offset;
};
/**
* Find domain name in tree, returns exact and closest match.
* @param tree: root of tree.
* @param dname: pointer to uncompressed dname.
* @param labs: number of labels in domain name.
* @param match: closest or exact match.
* guaranteed to be smaller or equal to the sought dname.
* can be null if the tree is empty.
* @param matchlabels: number of labels that match with closest match.
* can be zero is there is no match.
* @return: 0 if no exact match.
*/
static int
compress_tree_search(struct compress_tree_node* tree, uint8_t* dname,
int labs, struct compress_tree_node** match, int* matchlabels)
{
int c, n, closen=0;
struct compress_tree_node* p = tree;
struct compress_tree_node* close = 0;
while(p) {
if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
== 0) {
*matchlabels = n;
*match = p;
return 1;
}
if(c<0) p = p->left;
else {
closen = n;
close = p; /* p->dname is smaller than dname */
p = p->right;
}
}
*matchlabels = closen;
*match = close;
return 0;
}
/**
* Lookup a domain name in compression tree.
* @param tree: root of tree (not the node with '.').
* @param dname: pointer to uncompressed dname.
* @param labs: number of labels in domain name.
* @return: 0 if not found or compress treenode with best compression.
*/
static struct compress_tree_node*
compress_tree_lookup(struct compress_tree_node* tree, uint8_t* dname,
int labs)
{
struct compress_tree_node* p;
int m;
if(labs <= 1)
return 0; /* do not compress root node */
if(compress_tree_search(tree, dname, labs, &p, &m)) {
/* exact match */
return p;
}
/* return some ancestor of p that compresses well. */
if(m>1) {
/* www.example.com. (labs=4) matched foo.example.com.(labs=4)
* then matchcount = 3. need to go up. */
while(p && p->labs > m)
p = p->parent;
return p;
}
return 0;
}
/**
* Insert node into domain name compression tree.
* @param tree: root of tree (may be modified)
* @param dname: pointer to uncompressed dname (stored in tree).
* @param labs: number of labels in dname.
* @param offset: offset into packet for dname.
* @param region: how to allocate memory for new node.
* @return new node or 0 on malloc failure.
*/
static struct compress_tree_node*
compress_tree_insert(struct compress_tree_node** tree, uint8_t* dname,
int labs, size_t offset, region_type* region)
{
int c, m;
struct compress_tree_node* p, **prev;
struct compress_tree_node* n = (struct compress_tree_node*)
region_alloc(region, sizeof(struct compress_tree_node));
if(!n) return 0;
n->left = 0;
n->right = 0;
n->parent = 0;
n->dname = dname;
n->labs = labs;
n->offset = offset;
/* find spot to insert it into */
prev = tree;
p = *tree;
while(p) {
c = dname_lab_cmp(dname, labs, p->dname, p->labs, &m);
log_assert(c != 0); /* may not already be in tree */
if(c==0) return p;
if(c<0) {
prev = &p->left;
p = p->left;
} else {
prev = &p->right;
p = p->right;
}
}
*prev = n;
return n;
}
/**
* Store domain name and ancestors into compression tree.
* @param tree: root of tree (may be modified)
* @param dname: pointer to uncompressed dname (stored in tree).
* @param labs: number of labels in dname.
* @param offset: offset into packet for dname.
* @param region: how to allocate memory for new node.
* @param closest: match from previous lookup, used to compress dname.
* may be NULL if no previous match.
* if the tree has an ancestor of dname already, this must be it.
* @return: 0 on memory error.
*/
static int
compress_tree_store(struct compress_tree_node** tree, uint8_t* dname,
int labs, size_t offset, region_type* region,
struct compress_tree_node* closest)
{
uint8_t lablen;
struct compress_tree_node** lastparentptr = 0;
struct compress_tree_node* newnode;
int uplabs = labs-1; /* does not store root in tree */
if(closest) uplabs = labs - closest->labs;
log_assert(uplabs >= 0);
while(uplabs--) {
if(offset > 0x3fff) { /* largest valid compr. offset */
if(lastparentptr)
*lastparentptr = closest;
return 1; /* compression pointer no longer useful */
}
/* store dname, labs, offset */
if(!(newnode = compress_tree_insert(tree, dname, labs, offset,
region))) {
if(lastparentptr)
*lastparentptr = closest;
return 0;
}
if(lastparentptr)
*lastparentptr = newnode;
lastparentptr = &newnode->parent;
/* next label */
lablen = *dname++;
dname += lablen;
offset += lablen+1;
labs--;
}
if(lastparentptr)
*lastparentptr = closest;
return 1;
}
/** bake a new type-class-ttl value, or 0 on malloc error */
static uint32_t*
bake_tcttl(int do_sig, region_type* region,
struct packed_rrset_key* rk, uint32_t ttl, uint32_t timenow)
struct packed_rrset_key* rk, uint32_t ttl, uint32_t timenow,
uint32_t* prevttl, uint32_t* prevtcttl)
{
/* type, class, ttl,
type-class-ttl used for rrsigs.
ttl used for data itself. */
uint32_t* t;
if(prevttl && *prevttl == ttl)
return prevtcttl;
if(do_sig) {
t = (uint32_t*)region_alloc(region, 2*sizeof(uint32_t));
if(!t) return 0;
@ -510,14 +704,76 @@ bake_tcttl(int do_sig, region_type* region,
return t;
}
/** bake dname iov */
static int
bakedname(int dosig, struct compress_tree_node** tree, size_t* offset,
region_type* region, struct iovec* iov, struct packed_rrset_key* rk)
{
/* see if this name can be compressed */
struct compress_tree_node* p;
int labs = dname_count_labels(rk->dname);
size_t atset = *offset;
p = compress_tree_lookup(*tree, rk->dname, labs);
if(p) {
/* compress it */
int labcopy = labs - p->labs;
size_t len = 0;
uint8_t lablen;
uint8_t* from = rk->dname;
uint16_t ptr;
uint8_t* dat = (uint8_t*)region_alloc(region,
sizeof(uint16_t)*2*dosig + rk->dname_len);
/* note: oversized memory allocation. */
if(!dat) return 0;
iov->iov_base = dat;
/* copy the first couple of labels */
while(labcopy--) {
lablen = *from++;
*dat++ = lablen;
memmove(dat, from, lablen);
len += lablen+1;
dat += lablen;
from += lablen;
}
/* insert compression ptr */
ptr = 0xc000 | p->offset;
ptr = htons(ptr);
memmove(dat, &ptr, sizeof(ptr));
len += sizeof(ptr);
dat += sizeof(ptr);
if(!dosig) {
/* add type and class */
memmove(dat, &rk->dname[rk->dname_len], 4);
dat += 4;
len += 4;
}
log_assert(len <= sizeof(uint16_t)*2*dosig + rk->dname_len);
iov->iov_len = len;
*offset += len;
} else {
/* uncompressed */
iov->iov_base = rk->dname;
if(dosig)
iov->iov_len = rk->dname_len;
else iov->iov_len = rk->dname_len + 4;
*offset += iov->iov_len;
}
/* store this name for future compression */
if(!compress_tree_store(tree, rk->dname, labs, atset, region, p))
return 0;
return 1;
}
/** store rrset in iov vector */
static int
packed_rrset_iov(struct ub_packed_rrset_key* key, struct iovec* iov,
size_t max, uint16_t* num_rrs, uint32_t timenow, region_type* region,
size_t* used, int do_data, int do_sig)
size_t* used, int do_data, int do_sig,
struct compress_tree_node** tree, size_t* offset)
{
size_t i;
uint32_t* tcttl;
uint32_t* tcttl = 0;
struct packed_rrset_data* data = (struct packed_rrset_data*)
key->entry.data;
if(do_data) {
@ -525,15 +781,22 @@ packed_rrset_iov(struct ub_packed_rrset_key* key, struct iovec* iov,
for(i=0; i<data->count; i++) {
if(max - *used < 3) return 0;
if(!(tcttl = bake_tcttl(0, region, &key->rk,
data->rr_ttl[i], timenow)))
data->rr_ttl[i], timenow,
i>0?&data->rr_ttl[i-1]:0, tcttl)))
return 0;
/* no compression of dnames yet */
if(0)
if(!bakedname(0, tree, offset, region, &iov[*used],
&key->rk))
return 0;
iov[*used].iov_base = (void*)key->rk.dname;
iov[*used].iov_len = key->rk.dname_len + 4;
iov[*used+1].iov_base = (void*)tcttl;
iov[*used+1].iov_len = sizeof(uint32_t);
iov[*used+2].iov_base = (void*)data->rr_data[i];
iov[*used+2].iov_len = data->rr_len[i];
*offset += iov[*used].iov_len + sizeof(uint32_t) +
data->rr_len[i];
*used += 3;
}
}
@ -543,7 +806,8 @@ packed_rrset_iov(struct ub_packed_rrset_key* key, struct iovec* iov,
for(i=0; i<data->rrsig_count; i++) {
if(max - *used < 3) return 0;
if(!(tcttl = bake_tcttl(1, region, &key->rk,
data->rr_ttl[data->count+i], timenow)))
data->rr_ttl[data->count+i], timenow,
i>0?&data->rr_ttl[i-1]:0, tcttl)))
return 0;
/* no compression of dnames yet */
iov[*used].iov_base = (void*)key->rk.dname;
@ -563,23 +827,27 @@ packed_rrset_iov(struct ub_packed_rrset_key* key, struct iovec* iov,
static int
insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
struct iovec* iov, size_t max, size_t rrsets_before,
uint32_t timenow, region_type* region, size_t* used, int addit)
uint32_t timenow, region_type* region, size_t* used, int addit,
struct compress_tree_node** tree, size_t* offset)
{
size_t i;
*num_rrs = 0;
if(!addit) {
for(i=0; i<num_rrsets; i++)
if(!packed_rrset_iov(rep->rrsets[rrsets_before+i], iov,
max, num_rrs, timenow, region, used, 1, 1))
max, num_rrs, timenow, region, used, 1, 1,
tree, offset))
return 0;
} else {
for(i=0; i<num_rrsets; i++)
if(!packed_rrset_iov(rep->rrsets[rrsets_before+i], iov,
max, num_rrs, timenow, region, used, 1, 0))
max, num_rrs, timenow, region, used, 1, 0,
tree, offset))
return 0;
for(i=0; i<num_rrsets; i++)
if(!packed_rrset_iov(rep->rrsets[rrsets_before+i], iov,
max, num_rrs, timenow, region, used, 0, 1))
max, num_rrs, timenow, region, used, 0, 1,
tree, offset))
return 0;
}
*num_rrs = htons(*num_rrs);
@ -592,6 +860,8 @@ size_t reply_info_iov_regen(struct query_info* qinfo, struct reply_info* rep,
{
size_t used;
uint16_t* hdr = (uint16_t*)region_alloc(region, sizeof(uint16_t)*6);
size_t offset = 0;
struct compress_tree_node* tree = 0;
if(!hdr) return 0;
if(max<1) return 0;
hdr[0] = id;
@ -599,6 +869,7 @@ size_t reply_info_iov_regen(struct query_info* qinfo, struct reply_info* rep,
iov[0].iov_base = (void*)&hdr[0];
iov[0].iov_len = sizeof(uint16_t)*6;
hdr[2] = htons(rep->qdcount);
offset = sizeof(uint16_t)*6;
used=1;
/* insert query section */
@ -609,29 +880,33 @@ size_t reply_info_iov_regen(struct query_info* qinfo, struct reply_info* rep,
if(max-used < 3) return 0;
iov[used].iov_base = (void*)qinfo->qname;
iov[used].iov_len = qinfo->qnamesize;
if(!compress_tree_store(&tree, qinfo->qname,
dname_count_labels(qinfo->qname), offset, region, NULL))
return 0;
*qt = htons(qinfo->qtype);
*qc = htons(qinfo->qclass);
iov[used+1].iov_base = (void*)qt;
iov[used+1].iov_len = sizeof(uint16_t);
iov[used+2].iov_base = (void*)qc;
iov[used+2].iov_len = sizeof(uint16_t);
offset += qinfo->qnamesize + sizeof(uint16_t)*2;
used += 3;
}
/* insert answer section */
if(!insert_section(rep, rep->an_numrrsets, &hdr[3], iov, max,
0, timenow, region, &used, 0))
0, timenow, region, &used, 0, &tree, &offset))
return 0;
/* insert auth section */
if(!insert_section(rep, rep->ns_numrrsets, &hdr[4], iov, max,
rep->an_numrrsets, timenow, region, &used, 0))
rep->an_numrrsets, timenow, region, &used, 0, &tree, &offset))
return 0;
/* insert add section */
if(!insert_section(rep, rep->ar_numrrsets, &hdr[5], iov, max,
rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
&used, 1))
&used, 1, &tree, &offset))
return 0;
return used;

View file

@ -44,7 +44,7 @@
#include <time.h>
#endif
enum verbosity_value verbosity = 0;
enum verbosity_value verbosity = 4;
/** the file logged to. */
static FILE* logfile = 0;
/** if key has been created */