RRSIG parsing and outputting.

git-svn-id: file:///svn/unbound/trunk@255 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-04-24 13:39:23 +00:00
parent 2d53f65c92
commit add942bd40
9 changed files with 724 additions and 117 deletions

View file

@ -1,7 +1,16 @@
24 April 2007: Wouter
- ttl per RR, for RRSIG rrsets and others.
- dname_print debug function.
- if type is not known, size calc will skip DNAME decompression.
- RRSIG parsing and storing and putting in messages.
- dnssec enabled unit tests (from nlnetlabs.nl and se queries).
- EDNS extraction routine.
20 April 2007: Wouter
- code comes through all of the unit tests now.
- disabled warning about spurious extra data.
- documented the RRSIG parse plan in msgparse.h.
- rrsig reading and outputting.
19 April 2007: Wouter
- fix unit test to actually to tests.

View file

@ -98,8 +98,12 @@ static int
match_list(ldns_rr_list* q, ldns_rr_list *p)
{
size_t i;
if(ldns_rr_list_rr_count(q) != ldns_rr_list_rr_count(p))
if(ldns_rr_list_rr_count(q) != ldns_rr_list_rr_count(p)) {
verbose(3, "rrlistcount different %d %d",
(int)ldns_rr_list_rr_count(q),
(int)ldns_rr_list_rr_count(p));
return 0;
}
for(i=0; i<ldns_rr_list_rr_count(q); i++)
{
if(ldns_rr_compare(ldns_rr_list_rr(q, i),
@ -165,9 +169,9 @@ match_all(ldns_pkt* q, ldns_pkt* p)
if(!match_list(ldns_pkt_answer(q), ldns_pkt_answer(p)))
{ verbose(3, "allmatch: an section different"); return 0;}
if(!match_list(ldns_pkt_authority(q), ldns_pkt_authority(p)))
{ verbose(3, "allmatch: ar section different"); return 0;}
if(!match_list(ldns_pkt_additional(q), ldns_pkt_additional(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;}
return 1;
}
@ -185,6 +189,30 @@ test_buffers(ldns_buffer* pkt, ldns_buffer* out)
(unsigned)ldns_buffer_limit(pkt));
return 1;
}
if(vbmp) {
size_t sz = 16;
size_t count;
size_t lim = ldns_buffer_limit(out);
if(ldns_buffer_limit(pkt) < lim)
lim = ldns_buffer_limit(pkt);
for(count=0; count<lim; count+=sz) {
size_t rem = sz;
if(lim-count < sz) rem = lim-count;
if(memcmp(ldns_buffer_at(pkt, count),
ldns_buffer_at(out, count), rem) == 0) {
log_info("same %d %d", count, rem);
log_hex("same: ", ldns_buffer_at(pkt, count),
rem);
} else {
log_info("diff %d %d", count, rem);
log_hex("difp: ", ldns_buffer_at(pkt, count),
rem);
log_hex("difo: ", ldns_buffer_at(out, count),
rem);
}
}
}
/* check if it 'means the same' */
s1 = ldns_buffer2pkt_wire(&p1, pkt);
s2 = ldns_buffer2pkt_wire(&p2, out);
@ -357,6 +385,7 @@ static void
testfromdrillfile(ldns_buffer* pkt, struct alloc_cache* alloc,
ldns_buffer* out, const char* fname)
{
/* ;-- is used to indicate a new message */
FILE* in = fopen(fname, "r");
char buf[102400];
char *np = buf;
@ -365,14 +394,19 @@ testfromdrillfile(ldns_buffer* pkt, struct alloc_cache* alloc,
return;
}
while(fgets(np, (int)sizeof(buf) - (np-buf), in)) {
if(strncmp(np, ";--", 3) == 0) {
/* new entry */
/* test previous */
if(np != buf)
testpkt(pkt, alloc, out, buf);
/* set for new entry */
np = buf;
continue;
}
if(np[0] == ';') /* comment */
continue;
np = &np[strlen(np)];
}
if(vbmp) {
printf("test %s", buf);
fflush(stdout);
}
testpkt(pkt, alloc, out, buf);
fclose(in);
}
@ -388,10 +422,12 @@ void msgparse_test()
printf("testmsgparse\n");
simpletest(pkt, &alloc, out);
/* plain hex dumps, like pcat */
testfromfile(pkt, &alloc, out, "testdata/test_packets.1");
testfromfile(pkt, &alloc, out, "testdata/test_packets.2");
testfromfile(pkt, &alloc, out, "testdata/test_packets.3");
if(0) testfromdrillfile(pkt, &alloc, out, "blabla");
/* like from drill -w - */
testfromdrillfile(pkt, &alloc, out, "testdata/test_packets.4");
/* cleanup */
alloc_clear(&alloc);

View file

@ -302,3 +302,34 @@ void dname_pkt_copy(ldns_buffer* pkt, uint8_t* to, uint8_t* dname)
/* copy last \0 */
*to = 0;
}
void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname)
{
uint8_t lablen;
if(!out) out = stdout;
if(!dname) return;
lablen = *dname++;
if(!lablen)
fputc('.', out);
while(lablen) {
if(LABEL_IS_PTR(lablen)) {
/* follow pointer */
if(!pkt) {
fputs("??compressionptr??", out);
return;
}
dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
lablen = *dname++;
continue;
}
if(lablen > LDNS_MAX_LABELLEN) {
fputs("??extendedlabel??", out);
return;
}
while(lablen--)
fputc((int)*dname++, out);
fputc('.', out);
lablen = *dname++;
}
}

View file

@ -119,4 +119,11 @@ hashvalue_t dname_pkt_hash(ldns_buffer* pkt, uint8_t* dname, hashvalue_t h);
*/
void dname_pkt_copy(ldns_buffer* pkt, uint8_t* to, uint8_t* dname);
/** debug helper. Print wireformat dname to output.
* @param out: like stdout or a file.
* @param pkt: if not NULL, the packet for resolving compression ptrs.
* @param dname: pointer to (start of) dname.
*/
void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname);
#endif /* UTIL_DATA_DNAME_H */

View file

@ -62,6 +62,40 @@ smart_compare(ldns_buffer* pkt, uint8_t* dnow,
return dname_pkt_compare(pkt, dnow, dprlast);
}
/**
* Allocate new rrset in region, fill with data.
*/
static struct rrset_parse*
new_rrset(struct msg_parse* msg, uint8_t* dname, size_t dnamelen,
uint16_t type, uint16_t dclass, hashvalue_t hash,
uint32_t rrset_flags, ldns_pkt_section section, region_type* region)
{
struct rrset_parse* p = region_alloc(region, sizeof(*p));
if(!p) return NULL;
p->rrset_bucket_next = msg->hashtable[hash & (PARSE_TABLE_SIZE-1)];
msg->hashtable[hash & (PARSE_TABLE_SIZE-1)] = p;
p->rrset_all_next = 0;
if(msg->rrset_last)
msg->rrset_last->rrset_all_next = p;
else msg->rrset_first = p;
msg->rrset_last = p;
p->hash = hash;
p->section = section;
p->dname = dname;
p->dname_len = dnamelen;
p->type = type;
p->rrset_class = dclass;
p->flags = rrset_flags;
p->rr_count = 0;
p->size = 0;
p->rr_first = 0;
p->rr_last = 0;
p->rrsig_count = 0;
p->rrsig_first = 0;
p->rrsig_last = 0;
return p;
}
/** See if next rrset is nsec at zone apex. */
static int
nsec_at_apex(ldns_buffer* pkt)
@ -107,21 +141,29 @@ nsec_at_apex(ldns_buffer* pkt)
return 0;
}
/** Calculate rrset flags */
static uint32_t
pkt_rrset_flags(struct msg_parse* msg, ldns_buffer* pkt, uint16_t type)
{
uint32_t f;
if(msg->flags & BIT_CD)
f = PACKED_RRSET_CD;
else f = 0;
if(type == htons(LDNS_RR_TYPE_NSEC) && nsec_at_apex(pkt)) {
f |= PACKED_RRSET_NSEC_AT_APEX;
}
return f;
}
/** Calculate hash value for rrset in packet. */
static hashvalue_t
pkt_hash_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname,
uint16_t type, uint16_t dclass, uint32_t* rrset_flags)
pkt_hash_rrset(ldns_buffer* pkt, uint8_t* dname, uint16_t type,
uint16_t dclass, uint32_t rrset_flags)
{
hashvalue_t h = 0xab;
if(msg->flags & BIT_CD)
*rrset_flags = PACKED_RRSET_CD;
else *rrset_flags = 0;
if(type == htons(LDNS_RR_TYPE_NSEC) && nsec_at_apex(pkt))
*rrset_flags |= PACKED_RRSET_NSEC_AT_APEX;
h = hashlittle(&type, sizeof(type), h);
h = hashlittle(&dclass, sizeof(dclass), h);
h = hashlittle(rrset_flags, sizeof(uint32_t), h);
h = hashlittle(&rrset_flags, sizeof(uint32_t), h);
h = dname_pkt_hash(pkt, dname, h);
return h;
}
@ -158,6 +200,203 @@ hashtable_lookup(struct msg_parse* msg, ldns_buffer* pkt, hashvalue_t h,
return NULL;
}
/** return type networkformat that rrsig in packet covers */
static int
pkt_rrsig_covered(ldns_buffer* pkt, uint8_t* here, uint16_t* type)
{
size_t pos = ldns_buffer_position(pkt);
ldns_buffer_set_position(pkt, (size_t)(here-ldns_buffer_begin(pkt)));
/* ttl + len + size of small rrsig(rootlabel, no signature) */
if(ldns_buffer_remaining(pkt) < 4+2+19)
return 0;
ldns_buffer_skip(pkt, 4); /* ttl */
if(ldns_buffer_read_u16(pkt) < 19) /* too short */ {
ldns_buffer_set_position(pkt, pos);
return 0;
}
*type = ldns_buffer_read_u16(pkt);
ldns_buffer_set_position(pkt, pos);
return 1;
}
/** true if covered type equals prevtype */
static int
pkt_rrsig_covered_equals(ldns_buffer* pkt, uint8_t* here, uint16_t type)
{
uint16_t t;
if(pkt_rrsig_covered(pkt, here, &t) && t == ntohs(type))
return 1;
return 0;
}
/** remove rrset from hash list */
static void
bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset)
{
struct rrset_parse** p;
p = &msg->hashtable[ rrset->hash & (PARSE_TABLE_SIZE-1) ];
while(*p) {
if(*p == rrset) {
*p = rrset->rrset_bucket_next;
return;
}
p = &( (*p)->rrset_bucket_next );
}
}
/** change section of rrset from previous to current section */
static void
change_section(struct msg_parse* msg, struct rrset_parse* rrset,
ldns_pkt_section section)
{
struct rrset_parse *p, *prev;
/* remove from list */
if(section == rrset->section)
return;
p = msg->rrset_first;
prev = 0;
while(p) {
if(p == rrset) {
if(prev) prev->rrset_all_next = p->rrset_all_next;
else msg->rrset_first = p->rrset_all_next;
if(msg->rrset_last == rrset)
msg->rrset_last = prev;
break;
}
prev = p;
p = p->rrset_all_next;
}
/* remove from count */
switch(rrset->section) {
case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
default: log_assert(0);
}
/* insert at end of list */
rrset->rrset_all_next = 0;
if(msg->rrset_last)
msg->rrset_last->rrset_all_next = rrset;
else msg->rrset_first = rrset;
msg->rrset_last = rrset;
/* up count of new section */
switch(section) {
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break;
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break;
default: log_assert(0);
}
rrset->section = section;
}
/** see if rrset of type RRSIG contains sig over given type */
static int
rrset_has_sigover(ldns_buffer* pkt, struct rrset_parse* rrset, uint16_t type,
int* hasother)
{
int res = 0;
struct rr_parse* rr = rrset->rr_first;
log_assert( htons(rrset->type) == LDNS_RR_TYPE_RRSIG );
while(rr) {
if(pkt_rrsig_covered_equals(pkt, rr->ttl_data, type))
res = 1;
else *hasother = 1;
rr = rr->next;
}
return res;
}
/** move rrsigs from sigset to dataset */
static int
moveover_rrsigs(ldns_buffer* pkt, region_type* region,
struct rrset_parse* sigset, struct rrset_parse* dataset, int duplicate)
{
struct rr_parse* sig = sigset->rr_first;
struct rr_parse* prev = NULL;
struct rr_parse* insert;
while(sig) {
if(pkt_rrsig_covered_equals(pkt, sig->ttl_data,
dataset->type)) {
if(duplicate) {
/* new */
insert = (struct rr_parse*)region_alloc(region,
sizeof(struct rr_parse));
insert->ttl_data = sig->ttl_data;
insert->size = sig->size;
} else {
/* remove from sigset */
if(prev) prev->next = sig->next;
else sigset->rr_first = sig->next;
if(sigset->rr_last == sig)
sigset->rr_last = prev;
sigset->rr_count--;
sigset->size -= sig->size;
insert = sig;
}
/* add to dataset */
dataset->rrsig_count++;
insert->next = 0;
if(dataset->rrsig_last)
dataset->rrsig_last->next = insert;
else dataset->rrsig_first = insert;
dataset->rrsig_last = insert;
dataset->size += insert->size;
}
prev = sig;
sig = sig->next;
}
return 1;
}
/** change an rrsig rrset for use as data rrset */
static struct rrset_parse*
change_rrsig_rrset(struct rrset_parse* sigset, struct msg_parse* msg,
ldns_buffer* pkt, uint16_t datatype, uint32_t rrset_flags,
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,
sigset->rrset_class, rrset_flags);
log_assert( ntohs(sigset->type) == LDNS_RR_TYPE_RRSIG );
log_assert( ntohs(datatype) != LDNS_RR_TYPE_RRSIG );
if(hasother) {
/* need to make new rrset to hold data type */
dataset = new_rrset(msg, sigset->dname, sigset->dname_len,
datatype, sigset->rrset_class, hash, rrset_flags,
section, region);
if(!dataset)
return NULL;
switch(section) {
case LDNS_SECTION_ANSWER: msg->an_rrsets++; break;
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break;
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break;
default: log_assert(0);
}
if(!moveover_rrsigs(pkt, region, sigset, dataset,
ntohs(msg->qtype) == LDNS_RR_TYPE_RRSIG ||
ntohs(msg->qtype) == LDNS_RR_TYPE_ANY ))
return NULL;
return dataset;
}
/* changeover the type of the rrset to data set */
bucket_remove(msg, dataset);
/* insert into new hash bucket */
dataset->rrset_bucket_next = msg->hashtable[hash&(PARSE_TABLE_SIZE-1)];
msg->hashtable[hash&(PARSE_TABLE_SIZE-1)] = dataset;
dataset->hash = hash;
/* use section of data item for result */
change_section(msg, dataset, section);
dataset->type = datatype;
dataset->flags = rrset_flags;
dataset->rrsig_count += dataset->rr_count;
dataset->rr_count = 0;
/* move sigs to end of siglist */
if(dataset->rrsig_last)
dataset->rrsig_last->next = dataset->rr_first;
else dataset->rrsig_first = dataset->rr_first;
dataset->rrsig_last = dataset->rr_last;
return dataset;
}
/** Find rrset. If equal to previous it is fast. hash if not so.
* @param msg: the message with hash table.
* @param pkt: the packet in wireformat (needed for compression ptrs).
@ -173,16 +412,20 @@ hashtable_lookup(struct msg_parse* msg, ldns_buffer* pkt, hashvalue_t h,
* @param prev_type: type of last seen RR.
* @param prev_dclass: class of last seen RR.
* @param rrset_prev: last seen RRset.
* @return the rrset if found, or null if no matching rrset exists.
* @param section: the current section in the packet.
* @param region: used to allocate temporary parsing data.
* @return 0 on out of memory.
*/
static struct rrset_parse*
static int
find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname,
size_t dnamelen, uint16_t type, uint16_t dclass, hashvalue_t* hash,
uint32_t* rrset_flags,
uint8_t** prev_dname_first, uint8_t** prev_dname_last,
size_t* prev_dnamelen, uint16_t* prev_type,
uint16_t* prev_dclass, struct rrset_parse** rrset_prev)
uint16_t* prev_dclass, struct rrset_parse** rrset_prev,
ldns_pkt_section section, region_type* region)
{
uint16_t covtype;
if(rrset_prev) {
/* check if equal to previous item */
if(type == *prev_type && dclass == *prev_dclass &&
@ -191,12 +434,72 @@ find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname,
*prev_dname_last) == 0) {
/* same as previous */
*prev_dname_last = dname;
return *rrset_prev;
return 1;
}
/* check if rrsig over previous item */
if(ntohs(type) == LDNS_RR_TYPE_RRSIG &&
dclass == *prev_dclass &&
pkt_rrsig_covered_equals(pkt, ldns_buffer_current(pkt),
*prev_type) &&
smart_compare(pkt, dname, *prev_dname_first,
*prev_dname_last) == 0) {
/* covers previous */
*prev_dname_last = dname;
return 1;
}
}
/* find by hashing and lookup in hashtable */
*hash = pkt_hash_rrset(msg, pkt, dname, type, dclass, rrset_flags);
*rrset_flags = pkt_rrset_flags(msg, pkt, type);
/* if rrsig - try to lookup matching data set first */
if(ntohs(type) == LDNS_RR_TYPE_RRSIG && pkt_rrsig_covered(pkt,
ldns_buffer_current(pkt), &covtype)) {
covtype = htons(covtype);
*hash = pkt_hash_rrset(pkt, dname, covtype, dclass,
*rrset_flags);
*rrset_prev = hashtable_lookup(msg, pkt, *hash, *rrset_flags,
dname, dnamelen, covtype, dclass);
if(!*rrset_prev && ntohs(covtype) == LDNS_RR_TYPE_NSEC) {
/* if NSEC try with NSEC apex bit twiddled */
*rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX;
*hash = pkt_hash_rrset(pkt, dname, covtype, dclass,
*rrset_flags);
*rrset_prev = hashtable_lookup(msg, pkt, *hash,
*rrset_flags, dname, dnamelen, covtype, dclass);
}
if(*rrset_prev) {
*prev_dname_first = (*rrset_prev)->dname;
*prev_dname_last = dname;
*prev_dnamelen = dnamelen;
*prev_type = covtype;
*prev_dclass = dclass;
return 1;
}
}
if(ntohs(type) != LDNS_RR_TYPE_RRSIG) {
int hasother = 0;
/* find matching rrsig */
*hash = pkt_hash_rrset(pkt, dname, htons(LDNS_RR_TYPE_RRSIG),
dclass, *rrset_flags);
*rrset_prev = hashtable_lookup(msg, pkt, *hash, *rrset_flags,
dname, dnamelen, htons(LDNS_RR_TYPE_RRSIG), dclass);
if(*rrset_prev && rrset_has_sigover(pkt, *rrset_prev, type,
&hasother)) {
/* yes! */
*prev_dname_first = (*rrset_prev)->dname;
*prev_dname_last = dname;
*prev_dnamelen = dnamelen;
*prev_type = type;
*prev_dclass = dclass;
*rrset_prev = change_rrsig_rrset(*rrset_prev, msg,
pkt, type, *rrset_flags, hasother, section,
region);
if(!*rrset_prev) return 0;
return 1;
}
}
*hash = pkt_hash_rrset(pkt, dname, type, dclass, *rrset_flags);
*rrset_prev = hashtable_lookup(msg, pkt, *hash, *rrset_flags,
dname, dnamelen, type, dclass);
if(*rrset_prev)
@ -206,7 +509,7 @@ find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname,
*prev_dnamelen = dnamelen;
*prev_type = type;
*prev_dclass = dclass;
return *rrset_prev;
return 1;
}
/**
@ -236,40 +539,6 @@ parse_query_section(ldns_buffer* pkt, struct msg_parse* msg)
return 0;
}
/**
* Allocate new rrset in region, fill with data.
*/
static struct rrset_parse*
new_rrset(struct msg_parse* msg, uint8_t* dname, size_t dnamelen,
uint16_t type, uint16_t dclass, hashvalue_t hash,
uint32_t rrset_flags, ldns_pkt_section section, region_type* region)
{
struct rrset_parse* p = region_alloc(region, sizeof(*p));
if(!p) return NULL;
p->rrset_bucket_next = msg->hashtable[hash & (PARSE_TABLE_SIZE-1)];
msg->hashtable[hash & (PARSE_TABLE_SIZE-1)] = p;
p->rrset_all_next = 0;
if(msg->rrset_last)
msg->rrset_last->rrset_all_next = p;
else msg->rrset_first = p;
msg->rrset_last = p;
p->hash = hash;
p->section = section;
p->dname = dname;
p->dname_len = dnamelen;
p->type = type;
p->rrset_class = dclass;
p->flags = rrset_flags;
p->rr_count = 0;
p->size = 0;
p->rr_first = 0;
p->rr_last = 0;
p->rrsig_count = 0;
p->rrsig_first = 0;
p->rrsig_last = 0;
return p;
}
size_t
get_rdf_size(ldns_rdf_type rdf)
{
@ -316,7 +585,7 @@ calc_size(ldns_buffer* pkt, uint16_t type, struct rr_parse* rr)
if(ldns_buffer_remaining(pkt) < pkt_len)
return 0;
desc = ldns_rr_descript(type);
if(pkt_len > 0 && desc->_dname_count > 0) {
if(pkt_len > 0 && desc && desc->_dname_count > 0) {
int count = (int)desc->_dname_count;
int rdf = 0;
size_t len;
@ -360,44 +629,113 @@ calc_size(ldns_buffer* pkt, uint16_t type, struct rr_parse* rr)
return 1;
}
/** skip rr ttl and rdata */
static int
skip_ttl_rdata(ldns_buffer* pkt)
{
uint16_t rdatalen;
if(ldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */
return 0;
ldns_buffer_skip(pkt, 4); /* ttl */
rdatalen = ldns_buffer_read_u16(pkt);
if(ldns_buffer_remaining(pkt) < rdatalen)
return 0;
ldns_buffer_skip(pkt, (ssize_t)rdatalen);
return 1;
}
/** see if RRSIG is a duplicate of another */
static int
sig_is_double(ldns_buffer* pkt, struct rrset_parse* rrset, uint8_t* ttldata)
{
uint16_t rlen, siglen;
size_t pos = ldns_buffer_position(pkt);
struct rr_parse* sig;
if(ldns_buffer_remaining(pkt) < 6)
return 0;
ldns_buffer_skip(pkt, 4); /* ttl */
rlen = ldns_buffer_read_u16(pkt);
if(ldns_buffer_remaining(pkt) < rlen) {
ldns_buffer_set_position(pkt, pos);
return 0;
}
ldns_buffer_set_position(pkt, pos);
sig = rrset->rrsig_first;
while(sig) {
/* check if rdatalen is same */
memmove(&siglen, sig->ttl_data+4, sizeof(siglen));
siglen = ntohs(siglen);
/* checks if data in packet is exactly the same, this means
* also dname in rdata is the same, but rrsig is not allowed
* to have compressed dnames anyway. If it is compressed anyway
* it will lead to duplicate rrs for qtype=RRSIG. (or ANY).
*
* Cannot use sig->size because size of the other one is not
* calculated yet.
*/
if(siglen == rlen) {
if(siglen>0 && memcmp(sig->ttl_data+6, ttldata+6,
siglen) == 0) {
/* same! */
return 1;
}
}
sig = sig->next;
}
return 0;
}
/** 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)
struct msg_parse* msg, region_type* region,
ldns_pkt_section section, uint16_t type)
{
uint16_t rdatalen;
struct rr_parse* rr;
/* check section of rrset. */
if(rrset->section != section) {
if(rrset->section != section && ntohs(type) != LDNS_RR_TYPE_RRSIG &&
ntohs(rrset->type) != LDNS_RR_TYPE_RRSIG) {
/* 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.");
/* forwards */
if(ldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */
if(!skip_ttl_rdata(pkt))
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;
}
if( (ntohs(msg->qtype) == LDNS_RR_TYPE_RRSIG ||
ntohs(msg->qtype) == LDNS_RR_TYPE_ANY)
&& sig_is_double(pkt, rrset, ldns_buffer_current(pkt))) {
if(!skip_ttl_rdata(pkt))
return LDNS_RCODE_FORMERR;
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++;
if(ntohs(type) == LDNS_RR_TYPE_RRSIG) {
if(rrset->rrsig_last)
rrset->rrsig_last->next = rr;
else rrset->rrsig_first = rr;
rrset->rrsig_last = rr;
rrset->rrsig_count++;
} else {
if(rrset->rr_last)
rrset->rr_last->next = rr;
else rrset->rr_first = rr;
rrset->rr_last = rr;
rrset->rr_count++;
}
/* calc decompressed size */
if(!calc_size(pkt, ntohs(rrset->type), rr))
if(!calc_size(pkt, ntohs(type), rr))
return LDNS_RCODE_FORMERR;
rrset->size += rr->size;
@ -426,7 +764,7 @@ parse_section(ldns_buffer* pkt, struct msg_parse* msg, region_type* region,
uint16_t dclass, prev_dclass = 0;
uint32_t rrset_flags = 0;
hashvalue_t hash = 0;
struct rrset_parse* rrset, *rrset_prev = NULL;
struct rrset_parse* rrset = NULL;
int r;
if(num_rrs == 0)
@ -443,21 +781,37 @@ parse_section(ldns_buffer* pkt, struct msg_parse* msg, region_type* region,
ldns_buffer_read(pkt, &type, sizeof(type));
ldns_buffer_read(pkt, &dclass, sizeof(dclass));
if(0) { /* debug show what is being parsed. */
printf("parse of %s(%d)",
ldns_rr_descript(ntohs(type))?
ldns_rr_descript(ntohs(type))->_name: "??",
(int)ntohs(type));
printf(" %s(%d) ",
ldns_lookup_by_id(ldns_rr_classes,
(int)ntohs(dclass))?ldns_lookup_by_id(
ldns_rr_classes, (int)ntohs(dclass))->name:
"??", (int)ntohs(dclass));
dname_print(stdout, pkt, dname);
printf("\n");
}
/* see if it is part of an existing RR set */
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))) {
if(!find_rrset(msg, pkt, dname, dnamelen, type, dclass, &hash,
&rrset_flags, &prev_dname_f, &prev_dname_l,
&prev_dnamelen, &prev_type, &prev_dclass, &rrset,
section, region))
return LDNS_RCODE_SERVFAIL;
if(!rrset) {
/* 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;
rrset_prev = rrset;
}
/* add to rrset. */
if((r=add_rr_to_rrset(rrset, pkt, region, section)) != 0)
if((r=add_rr_to_rrset(rrset, pkt, msg, region, section,
type)) != 0)
return r;
}
return 0;
@ -495,3 +849,60 @@ parse_packet(ldns_buffer* pkt, struct msg_parse* msg, region_type* region)
msg->rrset_count = msg->an_rrsets + msg->ns_rrsets + msg->ar_rrsets;
return 0;
}
int
parse_extract_edns(struct msg_parse* msg, struct edns_data* edns)
{
struct rrset_parse* rrset = msg->rrset_first;
struct rrset_parse* prev = 0;
struct rrset_parse* found = 0;
struct rrset_parse* found_prev = 0;
/* since the class encodes the UDP size, we cannot use hash table to
* find the EDNS OPT record. Scan the packet. */
while(rrset) {
if(ntohs(rrset->type) == LDNS_RR_TYPE_OPT) {
/* only one OPT RR allowed. */
if(found) return LDNS_RCODE_FORMERR;
/* found it! */
found_prev = prev;
found = rrset;
}
prev = rrset;
rrset = rrset->rrset_all_next;
}
if(!found) {
memset(edns, 0, sizeof(*edns));
edns->udp_size = 512;
return 0;
}
/* check the found RRset */
/* most lenient check possible. ignore dname, use last opt */
if(found->section != LDNS_SECTION_ADDITIONAL)
return LDNS_RCODE_FORMERR;
if(found->rr_count == 0)
return LDNS_RCODE_FORMERR;
if(0) { /* strict checking of dname and RRcount */
if(found->dname_len != 1 || !found->dname
|| found->dname[0] != 0) return LDNS_RCODE_FORMERR;
if(found->rr_count != 1) return LDNS_RCODE_FORMERR;
}
log_assert(found->rr_first == found->rr_last && found->rr_first);
/* remove from packet */
if(found_prev) found_prev->rrset_all_next = found->rrset_all_next;
else msg->rrset_first = found->rrset_all_next;
if(found == msg->rrset_last)
msg->rrset_last = found_prev;
msg->arcount --;
msg->ar_rrsets --;
msg->rrset_count --;
/* take the data ! */
edns->edns_present = 1;
edns->ext_rcode = found->rr_last->ttl_data[0];
edns->edns_version = found->rr_last->ttl_data[1];
edns->bits = ldns_read_uint16(&found->rr_last->ttl_data[2]);
edns->udp_size = ntohs(found->rrset_class);
/* ignore rdata and rrsigs */
return 0;
}

View file

@ -181,6 +181,26 @@ struct rr_parse {
* second octets of the compression pointer. */
#define PTR_OFFSET(x, y) ( ((x)&0x3f)<<8 | (y) )
/** error codes, extended with EDNS, so > 15. */
#define EDNS_RCODE_BADVERS 16 /** bad EDNS version */
/**
* EDNS data storage
* EDNS rdata is ignored.
*/
struct edns_data {
/** if EDNS OPT record was present */
int edns_present;
/** Extended RCODE */
uint8_t ext_rcode;
/** The EDNS version number */
uint8_t edns_version;
/** the EDNS bits field from ttl (host order): Z */
uint16_t bits;
/** UDP reassembly size. */
uint16_t udp_size;
};
/**
* 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.
@ -200,4 +220,22 @@ size_t get_rdf_size(ldns_rdf_type rdf);
int parse_packet(ldns_buffer* pkt, struct msg_parse* msg,
struct region* region);
/**
* After parsing the packet, extract EDNS data from packet.
* If not present this is noted in the data structure.
* If a parse error happens, an error code is returned.
*
* Quirks:
* o ignores OPT rdata.
* o ignores OPT owner name.
* o ignores extra OPT records, except the last one in the packet.
*
* @param msg: parsed message structure. Modified on exit, if EDNS was present
* it is removed from the additional section.
* @param edns: the edns data is stored here. Does not have to be initialised.
* @return: 0 on success. or an RCODE on an error.
* RCODE formerr if OPT in wrong section, and so on.
*/
int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns);
#endif /* UTIL_DATA_MSGPARSE_H */

View file

@ -121,18 +121,20 @@ parse_alloc_rrset_keys(struct msg_parse* msg, struct reply_info* rep,
/** do the rdata copy */
static int
rdata_copy(ldns_buffer* pkt, struct rrset_parse* pset,
struct packed_rrset_data* data, uint8_t* to, struct rr_parse* rr)
rdata_copy(ldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
struct rr_parse* rr, uint32_t* rr_ttl, uint16_t type)
{
uint16_t pkt_len;
uint32_t ttl;
const ldns_rr_descriptor* desc;
ldns_buffer_set_position(pkt, (size_t)
(rr->ttl_data - ldns_buffer_begin(pkt)));
log_assert(ldns_buffer_remaining(pkt) >= 6 /* ttl + rdatalen */);
ttl = ldns_buffer_read_u32(pkt);
if(ttl < data->ttl)
data->ttl = ttl;
*rr_ttl = ldns_buffer_read_u32(pkt);
/* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */
if(*rr_ttl & 0x80000000U)
*rr_ttl = 0;
if(*rr_ttl < data->ttl)
data->ttl = *rr_ttl;
/* insert decompressed size into rdata len stored in memory */
/* -2 because rdatalen bytes are not included. */
pkt_len = htons(rr->size - 2);
@ -143,8 +145,8 @@ rdata_copy(ldns_buffer* pkt, struct rrset_parse* pset,
if(ldns_buffer_remaining(pkt) < pkt_len)
return 0;
log_assert((size_t)pkt_len+2 <= rr->size);
desc = ldns_rr_descript(ntohs(pset->type));
if(pkt_len > 0 && desc->_dname_count > 0) {
desc = ldns_rr_descript(type);
if(pkt_len > 0 && desc && desc->_dname_count > 0) {
int count = (int)desc->_dname_count;
int rdf = 0;
size_t len;
@ -193,23 +195,36 @@ parse_rr_copy(ldns_buffer* pkt, struct rrset_parse* pset,
size_t i;
struct rr_parse* rr = pset->rr_first;
uint8_t* nextrdata;
size_t total = pset->rr_count + pset->rrsig_count;
data->ttl = MAX_TTL;
data->count = pset->rr_count;
data->rrsig_count = pset->rrsig_count;
/* layout: struct - rr_len - rr_data - rdata - rrsig */
data->rr_len = (size_t*)((uint8_t*)data +
sizeof(struct packed_rrset_data));
data->rr_data = (uint8_t**)&(data->rr_len[data->count]);
nextrdata = (uint8_t*)&(data->rr_data[data->count]);
data->rrsig_count = 0;
data->rr_ttl = (uint32_t*)&(data->rr_len[total]);
data->rr_data = (uint8_t**)&(data->rr_ttl[total]);
nextrdata = (uint8_t*)&(data->rr_data[total]);
for(i=0; i<data->count; i++) {
data->rr_len[i] = rr->size;
data->rr_data[i] = nextrdata;
nextrdata += rr->size;
if(!rdata_copy(pkt, pset, data, data->rr_data[i], rr))
if(!rdata_copy(pkt, data, data->rr_data[i], rr,
&data->rr_ttl[i], ntohs(pset->type)))
return 0;
rr = rr->next;
}
/* if rrsig, its rdata is at nextrdata */
rr = pset->rrsig_first;
for(i=data->count; i<total; i++) {
data->rr_len[i] = rr->size;
data->rr_data[i] = nextrdata;
nextrdata += rr->size;
if(!rdata_copy(pkt, data, data->rr_data[i], rr,
&data->rr_ttl[i], LDNS_RR_TYPE_RRSIG))
return 0;
rr = rr->next;
}
return 1;
}
@ -219,7 +234,8 @@ parse_create_rrset(ldns_buffer* pkt, struct rrset_parse* pset,
struct packed_rrset_data** data)
{
/* allocate */
*data = malloc(sizeof(struct packed_rrset_data) + pset->rr_count*
*data = malloc(sizeof(struct packed_rrset_data) +
(pset->rr_count + pset->rrsig_count) *
(sizeof(size_t)+sizeof(uint8_t*)+sizeof(uint32_t)) +
pset->size);
if(!*data)
@ -468,29 +484,74 @@ reply_info_answer(struct reply_info* rep, uint16_t qflags,
ldns_buffer_flip(buffer);
}
/** 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)
{
/* type, class, ttl,
type-class-ttl used for rrsigs.
ttl used for data itself. */
uint32_t* t;
if(do_sig) {
t = (uint32_t*)region_alloc(region, 2*sizeof(uint32_t));
if(!t) return 0;
((uint16_t*)t)[0] = htons(LDNS_RR_TYPE_RRSIG);
memcpy( &(((uint16_t*)t)[1]), &(rk->dname[rk->dname_len+2]),
sizeof(uint16_t));
t[1] = htonl(ttl - timenow);
} else {
t = (uint32_t*)region_alloc(region, sizeof(uint32_t));
if(!t) return 0;
t[0] = htonl(ttl - timenow);
}
return t;
}
/** 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)
size_t* used, int do_data, int do_sig)
{
size_t i;
uint32_t* ttl = (uint32_t*)region_alloc(region, sizeof(uint32_t));
uint32_t* tcttl;
struct packed_rrset_data* data = (struct packed_rrset_data*)
key->entry.data;
*num_rrs += data->count;
if(!ttl) return 0;
*ttl = htonl(data->ttl - timenow);
for(i=0; i<data->count; i++) {
if(max - *used < 3) return 0;
/* no compression of dnames yet */
iov[*used].iov_base = (void*)key->rk.dname;
iov[*used].iov_len = key->rk.dname_len + 4;
iov[*used+1].iov_base = (void*)ttl;
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];
*used += 3;
if(do_data) {
*num_rrs += data->count;
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)))
return 0;
/* no compression of dnames yet */
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];
*used += 3;
}
}
/* insert rrsigs */
if(do_sig) {
*num_rrs += data->rrsig_count;
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)))
return 0;
/* no compression of dnames yet */
iov[*used].iov_base = (void*)key->rk.dname;
iov[*used].iov_len = key->rk.dname_len;
iov[*used+1].iov_base = (void*)tcttl;
iov[*used+1].iov_len = sizeof(uint32_t)*2;
iov[*used+2].iov_base = (void*)data->rr_data[data->count+i];
iov[*used+2].iov_len = data->rr_len[data->count+i];
*used += 3;
}
}
return 1;
@ -500,13 +561,23 @@ 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)
uint32_t timenow, region_type* region, size_t* used, int addit)
{
size_t i;
*num_rrs = 0;
for(i=0; i<num_rrsets; i++) {
if(!packed_rrset_iov(rep->rrsets[rrsets_before+i], iov,
max, num_rrs, timenow, region, used))
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))
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))
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))
return 0;
}
*num_rrs = htons(*num_rrs);
@ -547,17 +618,18 @@ size_t reply_info_iov_regen(struct query_info* qinfo, struct reply_info* rep,
/* insert answer section */
if(!insert_section(rep, rep->an_numrrsets, &hdr[3], iov, max,
0, timenow, region, &used))
0, timenow, region, &used, 0))
return 0;
/* insert auth section */
if(!insert_section(rep, rep->ns_numrrsets, &hdr[4], iov, max,
rep->an_numrrsets, timenow, region, &used))
rep->an_numrrsets, timenow, region, &used, 0))
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))
rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
&used, 1))
return 0;
return used;

View file

@ -114,6 +114,7 @@ struct ub_packed_rrset_key {
* memory layout:
* o base struct
* o rr_len size_t array
* o rr_ttl uint32_t array
* o rr_data uint8_t* array
* o rr_data rdata wireformats
* o rrsig_data rdata wireformat(s)
@ -144,6 +145,8 @@ struct packed_rrset_data {
size_t rrsig_count;
/** length of every rr's rdata, rr_len[i] is size of rr_data[i]. */
size_t* rr_len;
/** ttl of every rr. rr_ttl[i] ttl of rr i. */
uint32_t *rr_ttl;
/**
* Array of pointers to every rr's rdata.
* The rr_data[i] rdata is stored in uncompressed wireformat.

View file

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