From: Wouter Wijngaards Date: Tue, 24 Apr 2007 13:39:23 +0000 (+0000) Subject: RRSIG parsing and outputting. X-Git-Tag: release-0.3~48 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=add942bd40e6c616417de73236a820143c407cb4;p=thirdparty%2Funbound.git RRSIG parsing and outputting. git-svn-id: file:///svn/unbound/trunk@255 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 19b56dec4..0768764d8 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/testcode/unitmsgparse.c b/testcode/unitmsgparse.c index 7e3657ef4..69a80b845 100644 --- a/testcode/unitmsgparse.c +++ b/testcode/unitmsgparse.c @@ -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_MAX_LABELLEN) { + fputs("??extendedlabel??", out); + return; + } + while(lablen--) + fputc((int)*dname++, out); + fputc('.', out); + lablen = *dname++; + } +} diff --git a/util/data/dname.h b/util/data/dname.h index 90d2aaf9f..9633ce312 100644 --- a/util/data/dname.h +++ b/util/data/dname.h @@ -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 */ diff --git a/util/data/msgparse.c b/util/data/msgparse.c index 189e17609..d4fccfffa 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -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 */ - return LDNS_RCODE_FORMERR; - ldns_buffer_skip(pkt, 4); /* ttl */ - rdatalen = ldns_buffer_read_u16(pkt); - if(ldns_buffer_remaining(pkt) < rdatalen) + if(!skip_ttl_rdata(pkt)) 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; +} diff --git a/util/data/msgparse.h b/util/data/msgparse.h index 027b67836..1dab1ff4f 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -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 */ diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 9664fab00..3267bc5dc 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -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; icount; 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; irr_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; icount; 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; icount; 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; irrsig_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; irrsets[rrsets_before+i], iov, - max, num_rrs, timenow, region, used)) + if(!addit) { + for(i=0; irrsets[rrsets_before+i], iov, + max, num_rrs, timenow, region, used, 1, 1)) + return 0; + } else { + for(i=0; irrsets[rrsets_before+i], iov, + max, num_rrs, timenow, region, used, 1, 0)) + return 0; + for(i=0; irrsets[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; diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h index dfa5b7758..41f2bf267 100644 --- a/util/data/packed_rrset.h +++ b/util/data/packed_rrset.h @@ -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. diff --git a/util/log.c b/util/log.c index 9eb598d0c..c9bfdd60a 100644 --- a/util/log.c +++ b/util/log.c @@ -44,7 +44,7 @@ #include #endif -enum verbosity_value verbosity = 4; +enum verbosity_value verbosity = 0; /** the file logged to. */ static FILE* logfile = 0; /** if key has been created */