mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
canonical sort.
git-svn-id: file:///svn/unbound/trunk@508 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
b12ae95d1d
commit
749ee526e8
3 changed files with 246 additions and 13 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
10 August 2007: Wouter
|
10 August 2007: Wouter
|
||||||
- malloc and free overrides that track total allocation and frees.
|
- malloc and free overrides that track total allocation and frees.
|
||||||
for memory debugging.
|
for memory debugging.
|
||||||
|
- work on canonical sort.
|
||||||
|
|
||||||
9 August 2007: Wouter
|
9 August 2007: Wouter
|
||||||
- canonicalization, signature checks
|
- canonicalization, signature checks
|
||||||
|
|
|
||||||
|
|
@ -340,7 +340,7 @@ checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
|
||||||
e->create_func, e->create_file, e->create_line,
|
e->create_func, e->create_file, e->create_line,
|
||||||
(unsigned int)e->contention_count,
|
(unsigned int)e->contention_count,
|
||||||
(unsigned int)e->history_count,
|
(unsigned int)e->history_count,
|
||||||
100*e->contention_count/e->history_count);
|
(int)(100*e->contention_count/e->history_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* delete it */
|
/* delete it */
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,9 @@
|
||||||
#include "validator/val_sigcrypt.h"
|
#include "validator/val_sigcrypt.h"
|
||||||
#include "validator/validator.h"
|
#include "validator/validator.h"
|
||||||
#include "util/data/msgreply.h"
|
#include "util/data/msgreply.h"
|
||||||
|
#include "util/data/msgparse.h"
|
||||||
#include "util/data/dname.h"
|
#include "util/data/dname.h"
|
||||||
|
#include "util/rbtree.h"
|
||||||
#include "util/module.h"
|
#include "util/module.h"
|
||||||
#include "util/net_help.h"
|
#include "util/net_help.h"
|
||||||
#include "util/region-allocator.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;
|
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.
|
* Sort RRs for rrset in canonical order.
|
||||||
* Does not actually canonicalize the RR rdatas.
|
* Does not actually canonicalize the RR rdatas.
|
||||||
* Does not touch rrsigs.
|
* Does not touch rrsigs.
|
||||||
* @param rrset: to sort.
|
* @param rrset: to sort.
|
||||||
|
* @param d: rrset data.
|
||||||
|
* @param sortree: tree to sort into.
|
||||||
|
* @param rrs: rr storage.
|
||||||
*/
|
*/
|
||||||
static void
|
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 */
|
size_t i;
|
||||||
/* remove duplicates */
|
/* insert into rbtree to sort and detect duplicates */
|
||||||
|
for(i=0; i<d->count; 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.
|
* Create canonical form of rrset in the scratch buffer.
|
||||||
|
* @param region: temporary region.
|
||||||
* @param buf: the buffer to use.
|
* @param buf: the buffer to use.
|
||||||
* @param k: the rrset to insert.
|
* @param k: the rrset to insert.
|
||||||
* @param sig: RRSIG rdata to include.
|
* @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.
|
* @return false on alloc error.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
rrset_canonical(ldns_buffer* buf, struct ub_packed_rrset_key* k,
|
rrset_canonical(struct region* region, ldns_buffer* buf,
|
||||||
uint8_t* sig, size_t siglen)
|
struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen)
|
||||||
{
|
{
|
||||||
struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
|
struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
|
||||||
size_t i;
|
|
||||||
uint8_t* can_owner = NULL;
|
uint8_t* can_owner = NULL;
|
||||||
size_t can_owner_len = 0;
|
size_t can_owner_len = 0;
|
||||||
/* sort RRs in place */
|
rbtree_t sortree;
|
||||||
canonical_sort(k);
|
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_clear(buf);
|
||||||
ldns_buffer_write(buf, sig, siglen);
|
ldns_buffer_write(buf, sig, siglen);
|
||||||
query_dname_tolower(sig+18); /* canonicalize signer name */
|
query_dname_tolower(sig+18); /* canonicalize signer name */
|
||||||
for(i=0; i<d->count; i++) {
|
RBTREE_FOR(walk, struct canon_rr*, &sortree) {
|
||||||
/* determine canonical owner name */
|
/* determine canonical owner name */
|
||||||
if(can_owner)
|
if(can_owner)
|
||||||
ldns_buffer_write(buf, can_owner, can_owner_len);
|
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.type, 2);
|
||||||
ldns_buffer_write(buf, &k->rk.rrset_class, 2);
|
ldns_buffer_write(buf, &k->rk.rrset_class, 2);
|
||||||
ldns_buffer_write(buf, sig+4, 4);
|
ldns_buffer_write(buf, sig+4, 4);
|
||||||
ldns_buffer_write(buf, d->rr_data[i], d->rr_len[i]);
|
ldns_buffer_write(buf, d->rr_data[walk->rr_idx],
|
||||||
canonicalize_rdata(buf, k, d->rr_len[i]);
|
d->rr_len[walk->rr_idx]);
|
||||||
|
canonicalize_rdata(buf, k, d->rr_len[walk->rr_idx]);
|
||||||
}
|
}
|
||||||
ldns_buffer_flip(buf);
|
ldns_buffer_flip(buf);
|
||||||
return 1;
|
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 */
|
/* 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)) {
|
18 + signer_len)) {
|
||||||
log_err("verify: failed due to alloc error");
|
log_err("verify: failed due to alloc error");
|
||||||
return sec_status_unchecked;
|
return sec_status_unchecked;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue