From: Wouter Wijngaards Date: Fri, 10 Aug 2007 15:12:06 +0000 (+0000) Subject: canonical sort. X-Git-Tag: release-0.5~133 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=749ee526e8f7b257f111f232f4b2b1e636357854;p=thirdparty%2Funbound.git canonical sort. git-svn-id: file:///svn/unbound/trunk@508 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 03db89e18..b843f44d5 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,7 @@ 10 August 2007: Wouter - malloc and free overrides that track total allocation and frees. for memory debugging. + - work on canonical sort. 9 August 2007: Wouter - canonicalization, signature checks diff --git a/testcode/checklocks.c b/testcode/checklocks.c index eccc72d76..febc518bc 100644 --- a/testcode/checklocks.c +++ b/testcode/checklocks.c @@ -340,7 +340,7 @@ checklock_destroy(enum check_lock_type type, struct checked_lock** lock, e->create_func, e->create_file, e->create_line, (unsigned int)e->contention_count, (unsigned int)e->history_count, - 100*e->contention_count/e->history_count); + (int)(100*e->contention_count/e->history_count)); } /* delete it */ diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c index f1a2e6e84..d8ced8fd8 100644 --- a/validator/val_sigcrypt.c +++ b/validator/val_sigcrypt.c @@ -44,7 +44,9 @@ #include "validator/val_sigcrypt.h" #include "validator/validator.h" #include "util/data/msgreply.h" +#include "util/data/msgparse.h" #include "util/data/dname.h" +#include "util/rbtree.h" #include "util/module.h" #include "util/net_help.h" #include "util/region-allocator.h" @@ -432,17 +434,240 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, return sec_status_bogus; } +/** + * RR entries in a canonical sorted tree of RRs + */ +struct canon_rr { + /** rbtree node, key is this structure */ + rbnode_t node; + /** rrset the RR is in */ + struct ub_packed_rrset_key* rrset; + /** which RR in the rrset */ + size_t rr_idx; +}; + +/** + * Compare two RR for canonical order, in a field-style sweep. + * @param d: rrset data + * @param desc: ldns wireformat descriptor. + * @param i: first RR to compare + * @param j: first RR to compare + * @return comparison code. + */ +static int +canonical_compare_byfield(struct packed_rrset_data* d, + const ldns_rr_descriptor* desc, size_t i, size_t j) +{ + /* sweep across rdata, keep track of some state: + * which rr field, and bytes left in field. + * current position in rdata, length left. + * are we in a dname, length left in a label. + */ + const ldns_rdf_type* wfi = desc->_wireformat; + const ldns_rdf_type* wfj = desc->_wireformat; + uint8_t* di = d->rr_data[i]+2; + uint8_t* dj = d->rr_data[j]+2; + size_t ilen = d->rr_len[i]-2; + size_t jlen = d->rr_len[j]-2; + int dname_i = 0; + int dname_j = 0; + int lablen_i = 0; + int lablen_j = 0; + uint8_t bi, bj; + /* TODO keep track of number of dnames left */ + + /* setup initial state */ + /* if it is a domain name, set it true, lablen is then 0 to indicate + * that the current byte is the label length itself. + * If it is a string, get the length of the wireformat + */ + if(*wfi == LDNS_RDF_TYPE_DNAME) + dname_i = 1; + else if(*wfi == LDNS_RDF_TYPE_STR && ilen > 0) + lablen_i = (int)(*di)+1; + else lablen_i = (int)get_rdf_size(*wfi); + if(*wfj == LDNS_RDF_TYPE_DNAME) + dname_j = 1; + else if(*wfj == LDNS_RDF_TYPE_STR && jlen > 0) + lablen_j = (int)(*dj)+1; + else lablen_j = (int)get_rdf_size(*wfj); + + while(ilen > 0 && jlen > 0) { + /* compare these two bytes */ + /* note that labellengths are <=63 and A is 65 */ + if(dname_i && lablen_i) + bi = (uint8_t)tolower((int)*di++); + else bi = *di++; + ilen--; + if(dname_j && lablen_j) + bj = (uint8_t)tolower((int)*dj++); + else bj = *dj++; + jlen--; + if(bi != bj) { + if(bi < bj) + return -1; + return 1; + } + /* bytes are equal */ + + if(lablen_i == 0) { /* advance field i */ + if(dname_i) { + lablen_i = (int)bi; + if(!lablen_i) + dname_i = 0; + } + if(lablen_i == 0) { + wfi++; + if(wfi-desc->_wireformat > (int)desc->_maximum) + return 0; /* its formerr */ + if(*wfi == LDNS_RDF_TYPE_DNAME) + dname_i = 1; + else if(*wfi == LDNS_RDF_TYPE_STR) + lablen_i = (int)bi+1; + else lablen_i = (int)get_rdf_size(*wfi); + } + } else + lablen_i--; + if(lablen_j == 0) { /* advance field j */ + if(dname_j) { + lablen_j = (int)bj; + if(!lablen_j) + dname_j = 0; + } + if(lablen_j == 0) { + wfi++; + if(wfi-desc->_wireformat > (int)desc->_maximum) + return 0; /* its formerr */ + if(*wfi == LDNS_RDF_TYPE_DNAME) + dname_j = 1; + else if(*wfj == LDNS_RDF_TYPE_STR) + lablen_j = (int)bj+1; + else lablen_j = (int)get_rdf_size(*wfj); + } + } else + lablen_j--; + + } + /* shortest first */ + if(ilen == 0 && jlen == 0) + return 0; + if(ilen == 0) + return -1; + return 1; +} + +/** + * Compare two RRs in the same RRset and determine their relative + * canonical order. + * @param rrset: the rrset in which to perform compares. + * @param i: first RR to compare + * @param j: first RR to compare + * @return 0 if RR i== RR j, -1 if <, +1 if >. + */ +static int +canonical_compare(struct ub_packed_rrset_key* rrset, size_t i, size_t j) +{ + struct packed_rrset_data* d = (struct packed_rrset_data*) + rrset->entry.data; + const ldns_rr_descriptor* desc; + uint16_t type = ntohs(rrset->rk.type); + size_t minlen; + int c; + + if(i==j) + return 0; + + switch(type) { + /* only a name */ + case LDNS_RR_TYPE_NS: + case LDNS_RR_TYPE_MD: + case LDNS_RR_TYPE_MF: + case LDNS_RR_TYPE_CNAME: + case LDNS_RR_TYPE_MB: + case LDNS_RR_TYPE_MG: + case LDNS_RR_TYPE_MR: + case LDNS_RR_TYPE_PTR: + case LDNS_RR_TYPE_DNAME: + return query_dname_compare(d->rr_data[i]+2, + d->rr_data[j]+2); + + /* type starts with the name */ + case LDNS_RR_TYPE_NXT: + case LDNS_RR_TYPE_NSEC: + /* use rdata field formats */ + case LDNS_RR_TYPE_MINFO: + case LDNS_RR_TYPE_RP: + case LDNS_RR_TYPE_SOA: + case LDNS_RR_TYPE_RT: + case LDNS_RR_TYPE_AFSDB: + case LDNS_RR_TYPE_KX: + case LDNS_RR_TYPE_MX: + case LDNS_RR_TYPE_SIG: + case LDNS_RR_TYPE_RRSIG: + case LDNS_RR_TYPE_PX: + case LDNS_RR_TYPE_NAPTR: + case LDNS_RR_TYPE_SRV: + desc = ldns_rr_descript(type); + log_assert(desc); + /* this holds for the types that need canonicalizing */ + log_assert(desc->_minimum == desc->_maximum); + return canonical_compare_byfield(d, desc, i, j); + + /* special (TXT is lowercased) */ + case LDNS_RR_TYPE_HINFO: + + default: + /* byte for byte compare, equal means shortest first*/ + minlen = d->rr_len[i]-2; + if(minlen > d->rr_len[j]-2) + minlen = d->rr_len[j]-2; + c = memcmp(d->rr_data[i]+2, d->rr_data[j]+2, minlen); + if(c!=0) + return c; + if(d->rr_len[i] < d->rr_len[j]) + return -1; + if(d->rr_len[i] > d->rr_len[j]) + return 1; + break; + } + return 0; +} + +/** + * canonical compare for two tree entries + */ +static int +canonical_tree_compare(const void* k1, const void* k2) +{ + struct canon_rr* r1 = (struct canon_rr*)k1; + struct canon_rr* r2 = (struct canon_rr*)k2; + log_assert(r1->rrset == r2->rrset); + return canonical_compare(r1->rrset, r1->rr_idx, r2->rr_idx); +} + /** * Sort RRs for rrset in canonical order. * Does not actually canonicalize the RR rdatas. * Does not touch rrsigs. * @param rrset: to sort. + * @param d: rrset data. + * @param sortree: tree to sort into. + * @param rrs: rr storage. */ static void -canonical_sort(struct ub_packed_rrset_key* rrset) +canonical_sort(struct ub_packed_rrset_key* rrset, struct packed_rrset_data* d, + rbtree_t* sortree, struct canon_rr* rrs) { - /* check if already sorted */ - /* remove duplicates */ + size_t i; + /* insert into rbtree to sort and detect duplicates */ + for(i=0; icount; i++) { + rrs[i].node.key = &rrs[i]; + rrs[i].rrset = rrset; + rrs[i].rr_idx = i; + if(!rbtree_insert(sortree, &rrs[i].node)) { + /* this was a duplicate */ + } + } } /** @@ -610,6 +835,7 @@ canonicalize_rdata(ldns_buffer* buf, struct ub_packed_rrset_key* rrset, /** * Create canonical form of rrset in the scratch buffer. + * @param region: temporary region. * @param buf: the buffer to use. * @param k: the rrset to insert. * @param sig: RRSIG rdata to include. @@ -618,20 +844,25 @@ canonicalize_rdata(ldns_buffer* buf, struct ub_packed_rrset_key* rrset, * @return false on alloc error. */ static int -rrset_canonical(ldns_buffer* buf, struct ub_packed_rrset_key* k, - uint8_t* sig, size_t siglen) +rrset_canonical(struct region* region, ldns_buffer* buf, + struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen) { struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; - size_t i; uint8_t* can_owner = NULL; size_t can_owner_len = 0; - /* sort RRs in place */ - canonical_sort(k); + rbtree_t sortree; + struct canon_rr* walk; + struct canon_rr* rrs; + rrs = region_alloc(region, sizeof(struct canon_rr)*d->count); + if(!rrs) + return 0; + rbtree_init(&sortree, &canonical_tree_compare); + canonical_sort(k, d, &sortree, rrs); ldns_buffer_clear(buf); ldns_buffer_write(buf, sig, siglen); query_dname_tolower(sig+18); /* canonicalize signer name */ - for(i=0; icount; i++) { + RBTREE_FOR(walk, struct canon_rr*, &sortree) { /* determine canonical owner name */ if(can_owner) ldns_buffer_write(buf, can_owner, can_owner_len); @@ -640,8 +871,9 @@ rrset_canonical(ldns_buffer* buf, struct ub_packed_rrset_key* k, ldns_buffer_write(buf, &k->rk.type, 2); ldns_buffer_write(buf, &k->rk.rrset_class, 2); ldns_buffer_write(buf, sig+4, 4); - ldns_buffer_write(buf, d->rr_data[i], d->rr_len[i]); - canonicalize_rdata(buf, k, d->rr_len[i]); + ldns_buffer_write(buf, d->rr_data[walk->rr_idx], + d->rr_len[walk->rr_idx]); + canonicalize_rdata(buf, k, d->rr_len[walk->rr_idx]); } ldns_buffer_flip(buf); return 1; @@ -759,7 +991,7 @@ dnskey_verify_rrset_sig(struct module_env* env, struct val_env* ve, } /* create rrset canonical format in buffer, ready for signature */ - if(!rrset_canonical(env->scratch_buffer, rrset, sig+2, + if(!rrset_canonical(env->scratch, env->scratch_buffer, rrset, sig+2, 18 + signer_len)) { log_err("verify: failed due to alloc error"); return sec_status_unchecked;