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)
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;
}
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).
* @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 &&
*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)
*prev_dnamelen = dnamelen;
*prev_type = type;
*prev_dclass = dclass;
- return *rrset_prev;
+ return 1;
}
/**
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)
{
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;
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;
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)
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;
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;
+}
/** 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);
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;
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;
}
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)
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;
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);
/* 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;