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)
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)
.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 $@
$(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
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.
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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 */
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);
}
}
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);
#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)
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)
{ 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;
}
/* ;-- 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;
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;
+}
*/
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 */
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 );
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;
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) {
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;
}
}
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;
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);
{
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;
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 */
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;
#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 */