+18 April 2007: Wouter
+ - review of msgparse code.
+ - smaller test cases.
+
17 April 2007: Wouter
- copy and decompress dnames.
- store calculated hash value too.
by parsing of messages. Delay malloc, as above, or try to reverse release
special id numbers, and if you release the next_id number for the thread
it reuses that id number.
+o calculate rdata size during parse step, instead of delay until copy.
}
printf("Start of %s unit test.\n", PACKAGE_STRING);
checklock_start();
- /*
net_test();
alloc_test();
msgreply_test();
lruhash_test();
slabhash_test();
- */
msgparse_test();
checklock_stop();
printf("%d tests succeeded\n", testcount);
perror("fname");
return;
}
- while(fgets(buf, sizeof(buf), in)) {
+ while(fgets(buf, (int)sizeof(buf), in)) {
if(buf[0] == ';') /* comment */
continue;
if(strlen(buf) < 10) /* skip pcat line numbers. */
printf("testmsgparse\n");
simpletest(pkt, &alloc, out);
testfromfile(pkt, &alloc, out, "testdata/test_packets.1");
+ testfromfile(pkt, &alloc, out, "testdata/test_packets.2");
+ testfromfile(pkt, &alloc, out, "testdata/test_packets.3");
/* cleanup */
alloc_clear(&alloc);
#include "config.h"
#include "util/data/dname.h"
+#include "util/data/msgparse.h"
#include "util/log.h"
#include "util/storage/lookup3.h"
if(ldns_buffer_remaining(query) < 1)
return 0; /* parse error, need label len */
labellen = ldns_buffer_read_u8(query);
- if(labellen & 0xC0)
+ if(labellen&0xc0)
return 0; /* no compression allowed in queries */
len += labellen + 1;
if(len > LDNS_MAX_DOMAINLEN)
if(ldns_buffer_remaining(pkt) < 1)
return 0;
labellen = ldns_buffer_read_u8(pkt);
- if( (labellen & 0xc0) == 0xc0 ) {
+ if(LABEL_IS_PTR(labellen)) {
/* compression ptr */
- uint16_t ptr = (labellen & 0x3f) << 8;
+ uint16_t ptr;
if(ldns_buffer_remaining(pkt) < 1)
return 0;
- ptr |= ldns_buffer_read_u8(pkt);
+ ptr = PTR_OFFSET(labellen, ldns_buffer_read_u8(pkt));
if(loopcheck(loop, ptr))
return 0; /* loop! */
if(ldns_buffer_limit(pkt) <= ptr)
len2 = *d2++;
while( len1 != 0 || len2 != 0 ) {
/* resolve ptrs */
- if( (len1 & 0xc0) == 0xc0) {
- d1 = ldns_buffer_at(pkt, (len1&0x3f)<<8 | *d1);
+ if(LABEL_IS_PTR(len1)) {
+ d1 = ldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
len1 = *d1++;
continue;
}
- if( (len2 & 0xc0) == 0xc0) {
- d2 = ldns_buffer_at(pkt, (len2&0x3f)<<8 | *d2);
+ if(LABEL_IS_PTR(len2)) {
+ d2 = ldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
len2 = *d2++;
continue;
}
/* preserve case of query, make hash label by label */
lablen = *dname++;
while(lablen) {
- if((lablen & 0xc0) == 0xc0) {
+ if(LABEL_IS_PTR(lablen)) {
/* follow pointer */
- dname = ldns_buffer_at(pkt, (lablen&0x3f)<<8 | *dname);
+ dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
lablen = *dname++;
continue;
}
uint8_t lablen;
lablen = *dname++;
while(lablen) {
- if((lablen & 0xc0) == 0xc0) {
+ if(LABEL_IS_PTR(lablen)) {
/* follow pointer */
- dname = ldns_buffer_at(pkt, (lablen&0x3f)<<8 | *dname);
+ dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
lablen = *dname++;
continue;
}
/** smart comparison of (compressed, valid) dnames from packet. */
static int
smart_compare(ldns_buffer* pkt, uint8_t* dnow,
- uint8_t *dprfirst, uint8_t* dprlast)
+ uint8_t* dprfirst, uint8_t* dprlast)
{
- if( (*dnow & 0xc0) == 0xc0) {
+ if(LABEL_IS_PTR(*dnow)) {
/* ptr points to a previous dname */
- uint8_t* p = ldns_buffer_at(pkt, (dnow[0]&0x3f)<<8 | dnow[1]);
+ uint8_t* p = ldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1]));
if( p == dprfirst || p == dprlast )
return 0;
/* prev dname is also a ptr, both ptrs are the same. */
- /* if( (*dprfirst & 0xc0) == 0xc0 &&
- dprfirst[0] == dnow[0] && dprfirst[1] == dnow[1])
- return 0; */
- if( (*dprlast & 0xc0) == 0xc0 &&
+ if(LABEL_IS_PTR(*dprlast) &&
dprlast[0] == dnow[0] && dprlast[1] == dnow[1])
return 0;
- /* checks for prev dnames pointing forwards in the packet
- } else {
- if( (*dprfirst & 0xc0) == 0xc0 ) {
- if(ldns_buffer_at(pkt, (dprfirst[0]&0x3f)<<8 |
- dprfirst[1]) == dnow)
- return 0;
- }
- if( (*dprlast & 0xc0) == 0xc0 ) {
- if(ldns_buffer_at(pkt, (dprlast[0]&0x3f)<<8 |
- dprlast[1]) == dnow)
- return 0;
- }
- */
}
return dname_pkt_compare(pkt, dnow, dprlast);
}
/* nsec type bitmap contains items */
uint8_t win, blen, bits;
/* need: windownum, bitmap len, firstbyte */
- if(ldns_buffer_position(pkt)+3 <= pos+rdatalen) {
+ if(ldns_buffer_position(pkt)+3 > pos+rdatalen) {
ldns_buffer_set_position(pkt, pos);
return 0; /* malformed nsec */
}
p->rrset_class = dclass;
p->flags = rrset_flags;
p->rr_count = 0;
+ p->size = 0;
p->rr_first = 0;
p->rr_last = 0;
return p;
}
+size_t
+get_rdf_size(ldns_rdf_type rdf)
+{
+ switch(rdf) {
+ case LDNS_RDF_TYPE_CLASS:
+ case LDNS_RDF_TYPE_ALG:
+ case LDNS_RDF_TYPE_INT8:
+ return 1;
+ break;
+ case LDNS_RDF_TYPE_INT16:
+ case LDNS_RDF_TYPE_TYPE:
+ case LDNS_RDF_TYPE_CERT_ALG:
+ return 2;
+ break;
+ case LDNS_RDF_TYPE_INT32:
+ case LDNS_RDF_TYPE_TIME:
+ case LDNS_RDF_TYPE_A:
+ case LDNS_RDF_TYPE_PERIOD:
+ return 4;
+ break;
+ case LDNS_RDF_TYPE_TSIGTIME:
+ return 6;
+ break;
+ case LDNS_RDF_TYPE_AAAA:
+ return 16;
+ break;
+ default:
+ log_assert(false); /* add type above */
+ /* only types that appear before a domain *
+ * name are needed. rest is simply copied. */
+ }
+ return 0;
+}
+
+/** calculate the size of one rr */
+static int
+calc_size(ldns_buffer* pkt, uint16_t type, struct rr_parse* rr)
+{
+ const ldns_rr_descriptor* desc;
+ uint16_t pkt_len; /* length of rr inside the packet */
+ rr->size = sizeof(uint16_t); /* the rdatalen */
+ ldns_buffer_skip(pkt, 4); /* skip ttl */
+ pkt_len = ldns_buffer_read_u16(pkt);
+ if(ldns_buffer_remaining(pkt) < pkt_len)
+ return 0;
+ desc = ldns_rr_descript(type);
+ if(pkt_len > 0 && desc->_dname_count > 0) {
+ int count = (int)desc->_dname_count;
+ int rdf = 0;
+ size_t len;
+ size_t oldpos;
+ /* skip first part. */
+ while(pkt_len > 0 && count) {
+ switch(desc->_wireformat[rdf]) {
+ case LDNS_RDF_TYPE_DNAME:
+ /* decompress every domain name */
+ oldpos = ldns_buffer_position(pkt);
+ if((len = pkt_dname_len(pkt)) == 0)
+ return 0; /* malformed dname */
+ if(ldns_buffer_position(pkt)-oldpos > pkt_len)
+ return 0; /* dname exceeds rdata */
+ pkt_len -= ldns_buffer_position(pkt)-oldpos;
+ rr->size += len;
+ count--;
+ len = 0;
+ break;
+ case LDNS_RDF_TYPE_STR:
+ if(pkt_len < 1)
+ return 0; /* len byte exceeds rdata */
+ len = ldns_buffer_current(pkt)[0] + 1;
+ break;
+ default:
+ len = get_rdf_size(desc->_wireformat[rdf]);
+ }
+ if(len) {
+ if(pkt_len < len)
+ return 0; /* exceeds rdata */
+ pkt_len -= len;
+ ldns_buffer_skip(pkt, (ssize_t)len);
+ rr->size += len;
+ }
+ rdf++;
+ }
+ }
+ /* remaining rdata */
+ rr->size += pkt_len;
+ ldns_buffer_skip(pkt, (ssize_t)pkt_len);
+ return 1;
+}
+
+
/** Add rr (from packet here) to rrset, skips rr */
static int
add_rr_to_rrset(struct rrset_parse* rrset, ldns_buffer* pkt,
region_type* region, ldns_pkt_section section)
{
uint16_t rdatalen;
+ struct rr_parse* rr;
/* check section of rrset. */
if(rrset->section != section) {
- /* silently drop it */
+ /* silently drop it - it is a security problem, since
+ * trust in rr data depends on the section it is in.
+ * the less trustworthy part is discarded. */
verbose(VERB_DETAIL, "Packet contains rrset data in "
"multiple sections, dropped last part.");
- } else {
- /* create rr */
- struct rr_parse* rr = region_alloc(region, sizeof(*rr));
- if(!rr) return LDNS_RCODE_SERVFAIL;
- rr->ttl_data = ldns_buffer_current(pkt);
- rr->next = 0;
- if(rrset->rr_last)
- rrset->rr_last->next = rr;
- else rrset->rr_first = rr;
- rrset->rr_last = rr;
- rrset->rr_count++;
- }
+ /* forwards */
+ if(ldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */
+ return LDNS_RCODE_FORMERR;
+ ldns_buffer_skip(pkt, 4); /* ttl */
+ rdatalen = ldns_buffer_read_u16(pkt);
+ if(ldns_buffer_remaining(pkt) < rdatalen)
+ return LDNS_RCODE_FORMERR;
+ ldns_buffer_skip(pkt, (ssize_t)rdatalen);
+ return 0;
+ }
+ /* create rr */
+ if(!(rr = (struct rr_parse*)region_alloc(region, sizeof(*rr))))
+ return LDNS_RCODE_SERVFAIL;
+ rr->ttl_data = ldns_buffer_current(pkt);
+ rr->next = 0;
+ if(rrset->rr_last)
+ rrset->rr_last->next = rr;
+ else rrset->rr_first = rr;
+ rrset->rr_last = rr;
+ rrset->rr_count++;
- /* forwards */
- if(ldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */
+ /* calc decompressed size */
+ if(!calc_size(pkt, ntohs(rrset->type), rr))
return LDNS_RCODE_FORMERR;
- ldns_buffer_skip(pkt, 4); /* ttl */
- rdatalen = ldns_buffer_read_u16(pkt);
- if(ldns_buffer_remaining(pkt) < rdatalen)
- return LDNS_RCODE_FORMERR;
- ldns_buffer_skip(pkt, (ssize_t)rdatalen);
+ rrset->size += rr->size;
+
return 0;
}
ldns_buffer_read(pkt, &dclass, sizeof(dclass));
/* see if it is part of an existing RR set */
- if((rrset = find_rrset(msg, pkt, dname, dnamelen, type, dclass,
+ if(!(rrset = find_rrset(msg, pkt, dname, dnamelen, type, dclass,
&hash, &rrset_flags, &prev_dname_f, &prev_dname_l,
&prev_dnamelen, &prev_type, &prev_dclass,
- &rrset_prev)) != 0) {
- /* check if it fits the existing rrset */
- /* add to rrset. */
- } else {
+ &rrset_prev))) {
/* it is a new RR set. hash&flags already calculated.*/
(*num_rrsets)++;
rrset = new_rrset(msg, dname, dnamelen, type, dclass,
hash, rrset_flags, section, region);
- if(!rrset) return LDNS_RCODE_SERVFAIL;
+ if(!rrset)
+ return LDNS_RCODE_SERVFAIL;
rrset_prev = rrset;
}
- if((r=add_rr_to_rrset(rrset, pkt, region, section)))
+ /* add to rrset. */
+ if((r=add_rr_to_rrset(rrset, pkt, region, section)) != 0)
return r;
}
return 0;
size_t dname_len;
/** type, network order. */
uint16_t type;
- /** class, network order. name so that it is not a c++ keyword. */
+ /** class, network order. var name so that it is not a c++ keyword. */
uint16_t rrset_class;
/** the flags for the rrset, like for packedrrset */
uint32_t flags;
/** number of RRs in the rr list */
size_t rr_count;
+ /** sum of RR rdata sizes */
+ size_t size;
/** linked list of RRs in this rrset. */
struct rr_parse* rr_first;
/** last in list of RRs in this rrset. */
struct rr_parse* next;
};
+/** Check if label length is first octet of a compression pointer, pass u8. */
+#define LABEL_IS_PTR(x) ( ((x)&0xc0) == 0xc0 )
+/** Calculate destination offset of a compression pointer. pass first and
+ * second octets of the compression pointer. */
+#define PTR_OFFSET(x, y) ( ((x)&0x3f)<<8 | (y) )
+
+/**
+ * Obtain size in the packet of an rr type, that is before dname type.
+ * Do TYPE_DNAME, and type STR, yourself. Gives size for most regular types.
+ * @param rdf: the rdf type from the descriptor.
+ * @return: size in octets. 0 on failure.
+ */
+size_t get_rdf_size(ldns_rdf_type rdf);
+
/**
* Parse the packet.
* @param pkt: packet, position at call must be at start of packet.
copy_uncompr(uint8_t* dname, size_t len)
{
uint8_t* p = (uint8_t*)malloc(len);
- if(!p) return 0;
+ if(!p)
+ return 0;
memmove(p, dname, len);
return p;
}
parse_create_repinfo(struct msg_parse* msg, struct reply_info** rep)
{
/* rrset_count-1 because the first ref is part of the struct. */
- *rep = malloc(sizeof(struct reply_info) +
+ *rep = (struct reply_info*)malloc(sizeof(struct reply_info) +
sizeof(struct rrset_ref) * (msg->rrset_count-1) +
sizeof(struct ub_packed_rrset_key*) * msg->rrset_count);
if(!*rep) return 0;
return 1;
}
-/**
- * Obtain size in the packet of an rr type, that is before dname type.
- * Do TYPE_DNAME, and type STR, yourself.
- * @param rdf: the rdf type from the descriptor.
- * @return: size in octets. 0 on failure.
- */
-static size_t
-get_rdf_size(ldns_rdf_type rdf)
-{
- switch(rdf) {
- case LDNS_RDF_TYPE_CLASS:
- case LDNS_RDF_TYPE_ALG:
- case LDNS_RDF_TYPE_INT8:
- return 1;
- break;
- case LDNS_RDF_TYPE_INT16:
- case LDNS_RDF_TYPE_TYPE:
- case LDNS_RDF_TYPE_CERT_ALG:
- return 2;
- break;
- case LDNS_RDF_TYPE_INT32:
- case LDNS_RDF_TYPE_TIME:
- case LDNS_RDF_TYPE_A:
- case LDNS_RDF_TYPE_PERIOD:
- return 4;
- break;
- case LDNS_RDF_TYPE_TSIGTIME:
- return 6;
- break;
- case LDNS_RDF_TYPE_AAAA:
- return 16;
- break;
- default:
- log_assert(false); /* add type above */
- /* only types that appear before a domain *
- * name are needed. rest is simply copied. */
- }
- return 0;
-}
-
-/** calculate the size of one rr */
-static int
-calc_size(ldns_buffer* pkt, uint16_t type, struct rr_parse* rr)
-{
- const ldns_rr_descriptor* desc;
- uint16_t pkt_len; /* length of rr inside the packet */
- rr->size = sizeof(uint16_t); /* the rdatalen */
- ldns_buffer_set_position(pkt, (size_t)(rr->ttl_data -
- ldns_buffer_begin(pkt) + 4)); /* skip ttl */
- pkt_len = ldns_buffer_read_u16(pkt);
- if(ldns_buffer_remaining(pkt) < pkt_len)
- return 0;
- desc = ldns_rr_descript(type);
- if(pkt_len > 0 && desc->_dname_count > 0) {
- int count = (int)desc->_dname_count;
- int rdf = 0;
- size_t len;
- size_t oldpos;
- /* skip first part. */
- while(pkt_len > 0 && count) {
- switch(desc->_wireformat[rdf]) {
- case LDNS_RDF_TYPE_DNAME:
- /* decompress every domain name */
- oldpos = ldns_buffer_position(pkt);
- if((len = pkt_dname_len(pkt)) == 0)
- return 0; /* malformed dname */
- if(ldns_buffer_position(pkt)-oldpos > pkt_len)
- return 0; /* dname exceeds rdata */
- pkt_len -= ldns_buffer_position(pkt)-oldpos;
- rr->size += len;
- count--;
- len = 0;
- break;
- case LDNS_RDF_TYPE_STR:
- if(pkt_len < 1)
- return 0; /* len byte exceeds rdata */
- len = ldns_buffer_current(pkt)[0] + 1;
- break;
- default:
- len = get_rdf_size(desc->_wireformat[rdf]);
- }
- if(len) {
- if(pkt_len < len)
- return 0; /* exceeds rdata */
- pkt_len -= len;
- ldns_buffer_skip(pkt, (ssize_t)len);
- rr->size += len;
- }
- rdf++;
- }
- }
- /* remaining rdata */
- rr->size += pkt_len;
- return 1;
-}
-
-/** calculate size of rrs in rrset, 0 on parse failure */
-static int
-parse_rr_size(ldns_buffer* pkt, struct rrset_parse* pset, size_t* allocsize)
-{
- struct rr_parse* p = pset->rr_first;
- *allocsize = 0;
- /* size of rrs */
- while(p) {
- if(!calc_size(pkt, ntohs(pset->type), p))
- return 0;
- *allocsize += p->size;
- p = p->next;
- }
- /* TODO calc size of rrsig */
- return 1;
-}
-
/** do the rdata copy */
static int
rdata_copy(ldns_buffer* pkt, struct rrset_parse* pset,
const ldns_rr_descriptor* desc;
ldns_buffer_set_position(pkt, (size_t)
(rr->ttl_data - ldns_buffer_begin(pkt)));
- if(ldns_buffer_remaining(pkt) < 6)
- return 0;
+ log_assert(ldns_buffer_remaining(pkt) >= 6 /* ttl + rdatalen */);
ttl = ldns_buffer_read_u32(pkt);
if(ttl < data->ttl)
data->ttl = ttl;
- /* insert decompressed size into memory rdata len */
+ /* insert decompressed size into rdata len stored in memory */
pkt_len = htons(rr->size);
memmove(to, &pkt_len, sizeof(uint16_t));
to += 2;
parse_create_rrset(ldns_buffer* pkt, struct rrset_parse* pset,
struct packed_rrset_data** data)
{
- /* calculate sizes of rr rdata */
- size_t allocsize;
- if(!parse_rr_size(pkt, pset, &allocsize))
- return LDNS_RCODE_FORMERR;
/* allocate */
*data = malloc(sizeof(struct packed_rrset_data) + pset->rr_count*
- (sizeof(size_t)+sizeof(uint8_t*)+sizeof(uint32_t)) + allocsize);
+ (sizeof(size_t)+sizeof(uint8_t*)+sizeof(uint32_t)) +
+ pset->size);
if(!*data)
return LDNS_RCODE_SERVFAIL;
/* copy & decompress */
for(i=0; i<rep->rrset_count; i++) {
rep->rrsets[i]->rk.flags = pset->flags;
rep->rrsets[i]->rk.dname_len = pset->dname_len;
- rep->rrsets[i]->rk.dname = malloc(pset->dname_len + 4);
+ rep->rrsets[i]->rk.dname = (uint8_t*)malloc(
+ pset->dname_len + 4 /* size of type and class */ );
if(!rep->rrsets[i]->rk.dname)
return LDNS_RCODE_SERVFAIL;
/** copy & decompress dname */
qinf->qname = NULL;
*rep = NULL;
- msg = region_alloc(region, sizeof(*msg));
- if(!msg)
- goto malloc_error;
+ if(!(msg = region_alloc(region, sizeof(*msg)))) {
+ region_free_all(region);
+ region_destroy(region);
+ return LDNS_RCODE_SERVFAIL;
+ }
memset(msg, 0, sizeof(*msg));
log_assert(ldns_buffer_position(pkt) == 0);
/* parse OK, allocate return structures */
/* this also performs dname decompression */
- *rep = NULL;
if((ret = parse_create_msg(pkt, msg, alloc, qinf, rep)) != 0) {
query_info_clear(qinf);
reply_info_parsedelete(*rep, alloc);
region_free_all(region);
region_destroy(region);
return 0;
-malloc_error:
- region_free_all(region);
- region_destroy(region);
- return LDNS_RCODE_SERVFAIL;
}
void