From: Wouter Wijngaards Date: Tue, 17 Apr 2007 11:56:48 +0000 (+0000) Subject: message parsing code in one file. X-Git-Tag: release-0.3~58 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8bda1b6893cd867c387ab933fd9116c3d402a893;p=thirdparty%2Funbound.git message parsing code in one file. git-svn-id: file:///svn/unbound/trunk@245 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 2564bc28e..2cbdd84d0 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,7 @@ - copy and decompress dnames. - store calculated hash value too. - routine to create message out of stored information. + - util/data/msgparse.c for message parsing code. 16 April 2007: Wouter - following a small change in LDNS, parsing code calculates the diff --git a/util/data/msgparse.c b/util/data/msgparse.c new file mode 100644 index 000000000..af9b91a5a --- /dev/null +++ b/util/data/msgparse.c @@ -0,0 +1,407 @@ +/* + * util/data/msgparse.c - parse wireformat DNS messages. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "util/data/msgparse.h" +#include "util/net_help.h" +#include "util/data/dname.h" +#include "util/data/packed_rrset.h" +#include "util/storage/lookup3.h" +#include "util/region-allocator.h" + +/** smart comparison of (compressed, valid) dnames from packet. */ +static int +smart_compare(ldns_buffer* pkt, uint8_t* dnow, + uint8_t *dprfirst, uint8_t* dprlast) +{ + if( (*dnow & 0xc0) == 0xc0) { + /* ptr points to a previous dname */ + uint8_t* p = ldns_buffer_at(pkt, (dnow[0]&0x3f)<<8 | dnow[1]); + if( p == dprfirst || p == dprlast ) + return 0; + /* prev dname is also a ptr, both ptrs are the same. */ + /* if( (*dprfirst & 0xc0) == 0xc0 && + dprfirst[0] == dnow[0] && dprfirst[1] == dnow[1]) + return 0; */ + if( (*dprlast & 0xc0) == 0xc0 && + dprlast[0] == dnow[0] && dprlast[1] == dnow[1]) + return 0; + /* checks for prev dnames pointing forwards in the packet + } else { + if( (*dprfirst & 0xc0) == 0xc0 ) { + if(ldns_buffer_at(pkt, (dprfirst[0]&0x3f)<<8 | + dprfirst[1]) == dnow) + return 0; + } + if( (*dprlast & 0xc0) == 0xc0 ) { + if(ldns_buffer_at(pkt, (dprlast[0]&0x3f)<<8 | + dprlast[1]) == dnow) + return 0; + } + */ + } + return dname_pkt_compare(pkt, dnow, dprlast); +} + +/** See if next rrset is nsec at zone apex. */ +static int +nsec_at_apex(ldns_buffer* pkt) +{ + /* we are at ttl position in packet. */ + size_t pos = ldns_buffer_position(pkt); + uint16_t rdatalen; + if(ldns_buffer_remaining(pkt) < 7) /* ttl+len+root */ + return 0; /* eek! */ + ldns_buffer_skip(pkt, 4); /* ttl */; + rdatalen = ldns_buffer_read_u16(pkt); + if(ldns_buffer_remaining(pkt) < rdatalen) { + ldns_buffer_set_position(pkt, pos); + return 0; /* parse error happens later */ + } + /* must validate the nsec next domain name format */ + if(pkt_dname_len(pkt) == 0) { + ldns_buffer_set_position(pkt, pos); + return 0; /* parse error */ + } + + /* see if SOA bit is set. */ + if(ldns_buffer_position(pkt) < pos+rdatalen) { + /* nsec type bitmap contains items */ + uint8_t win, blen, bits; + /* need: windownum, bitmap len, firstbyte */ + if(ldns_buffer_position(pkt)+3 <= pos+rdatalen) { + ldns_buffer_set_position(pkt, pos); + return 0; /* malformed nsec */ + } + win = ldns_buffer_read_u8(pkt); + blen = ldns_buffer_read_u8(pkt); + bits = ldns_buffer_read_u8(pkt); + /* 0window always first window. bitlen >=1 or parse + error really. bit 0x2 is SOA. */ + if(win == 0 && blen >= 1 && (bits & 0x02)) { + ldns_buffer_set_position(pkt, pos); + return 1; + } + } + + ldns_buffer_set_position(pkt, pos); + return 0; +} + +/** 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) +{ + 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 = dname_pkt_hash(pkt, dname, h); + return h; +} + +/** compare rrset_parse with data. */ +static int +rrset_parse_equals(struct rrset_parse* p, ldns_buffer* pkt, hashvalue_t h, + uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, + uint16_t type, uint16_t dclass) +{ + if(p->hash == h && p->dname_len == dnamelen && p->type == type && + p->rrset_class == dclass && p->flags == rrset_flags && + dname_pkt_compare(pkt, dname, p->dname) == 0) + return 1; + return 0; +} + + +/** + * Lookup in msg hashtable to find a rrset + */ +static struct rrset_parse* +hashtable_lookup(struct msg_parse* msg, ldns_buffer* pkt, hashvalue_t h, + uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, + uint16_t type, uint16_t dclass) +{ + struct rrset_parse* p = msg->hashtable[h & (PARSE_TABLE_SIZE-1)]; + while(p) { + if(rrset_parse_equals(p, pkt, h, rrset_flags, dname, dnamelen, + type, dclass)) + return p; + p = p->rrset_bucket_next; + } + return NULL; +} + +/** 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 dname: pointer to start of dname (compressed) in packet. + * @param dnamelen: uncompressed wirefmt length of dname. + * @param type: type of current rr. + * @param dclass: class of current rr. + * @param hash: hash value is returned if the rrset could not be found. + * @param rrset_flags: is returned if the rrset could not be found. + * @param prev_dname_first: dname of last seen RR. First seen dname. + * @param prev_dname_last: dname of last seen RR. Last seen dname. + * @param prev_dnamelen: dname len of last seen RR. + * @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. + */ +static struct rrset_parse* +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) +{ + if(rrset_prev) { + /* check if equal to previous item */ + if(type == *prev_type && dclass == *prev_dclass && + dnamelen == *prev_dnamelen && + smart_compare(pkt, dname, *prev_dname_first, + *prev_dname_last) == 0) { + /* same as previous */ + *prev_dname_last = dname; + return *rrset_prev; + } + + } + /* find by hashing and lookup in hashtable */ + *hash = pkt_hash_rrset(msg, pkt, dname, type, dclass, rrset_flags); + *rrset_prev = hashtable_lookup(msg, pkt, *hash, *rrset_flags, + dname, dnamelen, type, dclass); + if(*rrset_prev) + *prev_dname_first = (*rrset_prev)->dname; + else *prev_dname_first = dname; + *prev_dname_last = dname; + *prev_dnamelen = dnamelen; + *prev_type = type; + *prev_dclass = dclass; + return *rrset_prev; +} + +/** + * Parse query section. + * @param pkt: packet, position at call must be at start of query section. + * at end position is after query section. + * @param msg: store results here. + * @return: 0 if OK, or rcode on error. + */ +static int +parse_query_section(ldns_buffer* pkt, struct msg_parse* msg) +{ + if(msg->qdcount == 0) + return 0; + if(msg->qdcount > 1) + return LDNS_RCODE_FORMERR; + log_assert(msg->qdcount == 1); + if(ldns_buffer_remaining(pkt) <= 0) + return LDNS_RCODE_FORMERR; + msg->qname = ldns_buffer_current(pkt); + if((msg->qname_len = query_dname_len(pkt)) == 0) + return LDNS_RCODE_FORMERR; + if(ldns_buffer_remaining(pkt) < sizeof(uint16_t)*2) + return LDNS_RCODE_FORMERR; + msg->qtype = ldns_buffer_read_u16(pkt); + msg->qclass = ldns_buffer_read_u16(pkt); + 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->rr_first = 0; + p->rr_last = 0; + return p; +} + +/** 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) +{ + uint16_t rdatalen; + /* check section of rrset. */ + if(rrset->section != section) { + /* silently drop it */ + verbose(VERB_DETAIL, "Packet contains rrset data in " + "multiple sections, dropped last part."); + } else { + /* create rr */ + struct rr_parse* rr = region_alloc(region, sizeof(*rr)); + if(!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++; + } + + /* 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) + return LDNS_RCODE_FORMERR; + ldns_buffer_skip(pkt, (ssize_t)rdatalen); + return 0; +} + +/** + * Parse packet RR section, for answer, authority and additional sections. + * @param pkt: packet, position at call must be at start of section. + * at end position is after section. + * @param msg: store results here. + * @param region: how to alloc results. + * @param section: section enum. + * @param num_rrs: how many rrs are in the section. + * @param num_rrsets: returns number of rrsets in the section. + * @return: 0 if OK, or rcode on error. + */ +static int +parse_section(ldns_buffer* pkt, struct msg_parse* msg, region_type* region, + ldns_pkt_section section, uint16_t num_rrs, size_t* num_rrsets) +{ + uint16_t i; + uint8_t* dname, *prev_dname_f = NULL, *prev_dname_l = NULL; + size_t dnamelen, prev_dnamelen = 0; + uint16_t type, prev_type = 0; + uint16_t dclass, prev_dclass = 0; + uint32_t rrset_flags = 0; + hashvalue_t hash = 0; + struct rrset_parse* rrset, *rrset_prev = NULL; + int r; + + if(num_rrs == 0) + return 0; + if(ldns_buffer_remaining(pkt) <= 0) + return LDNS_RCODE_FORMERR; + for(i=0; iid, sizeof(uint16_t)); + msg->flags = ldns_buffer_read_u16(pkt); + msg->qdcount = ldns_buffer_read_u16(pkt); + msg->ancount = ldns_buffer_read_u16(pkt); + msg->nscount = ldns_buffer_read_u16(pkt); + msg->arcount = ldns_buffer_read_u16(pkt); + if(msg->qdcount > 1) + return LDNS_RCODE_FORMERR; + if((ret = parse_query_section(pkt, msg)) != 0) + return ret; + if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ANSWER, + msg->ancount, &msg->an_rrsets)) != 0) + return ret; + if((ret = parse_section(pkt, msg, region, LDNS_SECTION_AUTHORITY, + msg->nscount, &msg->ns_rrsets)) != 0) + return ret; + if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ADDITIONAL, + msg->arcount, &msg->ar_rrsets)) != 0) + return ret; + if(ldns_buffer_remaining(pkt) > 0) { + /* spurious data at end of packet. ignore */ + verbose(VERB_DETAIL, "spurious data at end of packet ignored"); + } + return 0; +} diff --git a/util/data/msgparse.h b/util/data/msgparse.h new file mode 100644 index 000000000..6fecfa4c1 --- /dev/null +++ b/util/data/msgparse.h @@ -0,0 +1,154 @@ +/* + * util/data/msgparse.h - parse wireformat DNS messages. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UTIL_DATA_MSGPARSE_H +#define UTIL_DATA_MSGPARSE_H +#include "util/storage/lruhash.h" +struct rrset_parse; +struct rr_parse; +struct region; + +/** number of buckets in parse rrset hash table. Must be power of 2. */ +#define PARSE_TABLE_SIZE 1024 +/** Maximum TTL that is allowed. */ +#define MAX_TTL 3600*24*365*10 /* ten years */ + +/** + * Data stored in scratch pad memory during parsing. + * Stores the data that will enter into the msgreply and packet result. + */ +struct msg_parse { + /** id from message, network format. */ + uint16_t id; + /** flags from message, host format. */ + uint16_t flags; + /** count of RRs, host format */ + uint16_t qdcount; + /** count of RRs, host format */ + uint16_t ancount; + /** count of RRs, host format */ + uint16_t nscount; + /** count of RRs, host format */ + uint16_t arcount; + /** count of RRsets per section. */ + size_t an_rrsets; + /** count of RRsets per section. */ + size_t ns_rrsets; + /** count of RRsets per section. */ + size_t ar_rrsets; + /** total number of rrsets found. */ + size_t rrset_count; + + /** query dname (pointer to start location in packet, NULL if none */ + uint8_t* qname; + /** length of query dname in octets, 0 if none */ + size_t qname_len; + /** query type, network order. 0 if qdcount=0 */ + uint16_t qtype; + /** query class, network order. 0 if qdcount=0 */ + uint16_t qclass; + + /** + * Hash table array used during parsing to lookup rrset types. + * Based on name, type, class. Same hash value as in rrset cache. + */ + struct rrset_parse* hashtable[PARSE_TABLE_SIZE]; + + /** linked list of rrsets that have been found (in order). */ + struct rrset_parse* rrset_first; + /** last element of rrset list. */ + struct rrset_parse* rrset_last; +}; + +/** + * Data stored for an rrset during parsing. + */ +struct rrset_parse { + /** next in hash bucket */ + struct rrset_parse* rrset_bucket_next; + /** next in list of all rrsets */ + struct rrset_parse* rrset_all_next; + /** hash value of rrset */ + hashvalue_t hash; + /** which section was it found in: one of + * LDNS_SECTION_ANSWER, LDNS_SECTION_AUTHORITY, LDNS_SECTION_ADDITIONAL + */ + ldns_pkt_section section; + /** start of (possibly compressed) dname in packet */ + uint8_t* dname; + /** length of the dname uncompressed wireformat */ + size_t dname_len; + /** type, network order. */ + uint16_t type; + /** class, network order. name so that it is not a c++ keyword. */ + uint16_t rrset_class; + /** the flags for the rrset, like for packedrrset */ + uint32_t flags; + /** number of RRs in the rr list */ + size_t rr_count; + /** linked list of RRs in this rrset. */ + struct rr_parse* rr_first; + /** last in list of RRs in this rrset. */ + struct rr_parse* rr_last; +}; + +/** + * Data stored for an RR during parsing. + */ +struct rr_parse { + /** + * Pointer to the RR. Points to start of TTL value in the packet. + * Rdata length and rdata follow it. + * its dname, type and class are the same and stored for the rrset. + */ + uint8_t* ttl_data; + /** the length of the rdata if allocated (with no dname compression)*/ + size_t size; + /** next in list of RRs. */ + struct rr_parse* next; +}; + +/** + * Parse the packet. + * @param pkt: packet, position at call must be at start of packet. + * at end position is after packet. + * @param msg: where to store results. + * @param region: how to alloc results. + * @return: 0 if OK, or rcode on error. + */ +int parse_packet(ldns_buffer* pkt, struct msg_parse* msg, + struct region* region); + +#endif /* UTIL_DATA_MSGPARSE_H */ diff --git a/util/data/msgreply.c b/util/data/msgreply.c index deb8ca2cd..dcac83abb 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -48,484 +48,7 @@ #include "util/net_help.h" #include "util/data/dname.h" #include "util/region-allocator.h" - -struct rrset_parse; -struct rr_parse; - -/** number of buckets in parse rrset hash table. Must be power of 2. */ -#define PARSE_TABLE_SIZE 1024 -/** Maximum TTL that is allowed. */ -#define MAX_TTL 3600*24*365*10 /* ten years */ - -/** - * Data stored in scratch pad memory during parsing. - * Stores the data that will enter into the msgreply and packet result. - */ -struct msg_parse { - /** id from message, network format. */ - uint16_t id; - /** flags from message, host format. */ - uint16_t flags; - /** count of RRs, host format */ - uint16_t qdcount; - /** count of RRs, host format */ - uint16_t ancount; - /** count of RRs, host format */ - uint16_t nscount; - /** count of RRs, host format */ - uint16_t arcount; - /** count of RRsets per section. */ - size_t an_rrsets; - /** count of RRsets per section. */ - size_t ns_rrsets; - /** count of RRsets per section. */ - size_t ar_rrsets; - /** total number of rrsets found. */ - size_t rrset_count; - - /** query dname (pointer to start location in packet, NULL if none */ - uint8_t* qname; - /** length of query dname in octets, 0 if none */ - size_t qname_len; - /** query type, network order. 0 if qdcount=0 */ - uint16_t qtype; - /** query class, network order. 0 if qdcount=0 */ - uint16_t qclass; - - /** - * Hash table array used during parsing to lookup rrset types. - * Based on name, type, class. Same hash value as in rrset cache. - */ - struct rrset_parse* hashtable[PARSE_TABLE_SIZE]; - - /** linked list of rrsets that have been found (in order). */ - struct rrset_parse* rrset_first; - /** last element of rrset list. */ - struct rrset_parse* rrset_last; -}; - -/** - * Data stored for an rrset during parsing. - */ -struct rrset_parse { - /** next in hash bucket */ - struct rrset_parse* rrset_bucket_next; - /** next in list of all rrsets */ - struct rrset_parse* rrset_all_next; - /** hash value of rrset */ - hashvalue_t hash; - /** which section was it found in: one of - * LDNS_SECTION_ANSWER, LDNS_SECTION_AUTHORITY, LDNS_SECTION_ADDITIONAL - */ - ldns_pkt_section section; - /** start of (possibly compressed) dname in packet */ - uint8_t* dname; - /** length of the dname uncompressed wireformat */ - size_t dname_len; - /** type, network order. */ - uint16_t type; - /** class, network order. name so that it is not a c++ keyword. */ - uint16_t rrset_class; - /** the flags for the rrset, like for packedrrset */ - uint32_t flags; - /** number of RRs in the rr list */ - size_t rr_count; - /** linked list of RRs in this rrset. */ - struct rr_parse* rr_first; - /** last in list of RRs in this rrset. */ - struct rr_parse* rr_last; -}; - -/** - * Data stored for an RR during parsing. - */ -struct rr_parse { - /** - * Pointer to the RR. Points to start of TTL value in the packet. - * Rdata length and rdata follow it. - * its dname, type and class are the same and stored for the rrset. - */ - uint8_t* ttl_data; - /** the length of the rdata if allocated (with no dname compression)*/ - size_t size; - /** next in list of RRs. */ - struct rr_parse* next; -}; - -/** smart comparison of (compressed, valid) dnames from packet. */ -static int -smart_compare(ldns_buffer* pkt, uint8_t* dnow, - uint8_t *dprfirst, uint8_t* dprlast) -{ - if( (*dnow & 0xc0) == 0xc0) { - /* ptr points to a previous dname */ - uint8_t* p = ldns_buffer_at(pkt, (dnow[0]&0x3f)<<8 | dnow[1]); - if( p == dprfirst || p == dprlast ) - return 0; - /* prev dname is also a ptr, both ptrs are the same. */ - /* if( (*dprfirst & 0xc0) == 0xc0 && - dprfirst[0] == dnow[0] && dprfirst[1] == dnow[1]) - return 0; */ - if( (*dprlast & 0xc0) == 0xc0 && - dprlast[0] == dnow[0] && dprlast[1] == dnow[1]) - return 0; - /* checks for prev dnames pointing forwards in the packet - } else { - if( (*dprfirst & 0xc0) == 0xc0 ) { - if(ldns_buffer_at(pkt, (dprfirst[0]&0x3f)<<8 | - dprfirst[1]) == dnow) - return 0; - } - if( (*dprlast & 0xc0) == 0xc0 ) { - if(ldns_buffer_at(pkt, (dprlast[0]&0x3f)<<8 | - dprlast[1]) == dnow) - return 0; - } - */ - } - return dname_pkt_compare(pkt, dnow, dprlast); -} - -/** See if next rrset is nsec at zone apex. */ -static int -nsec_at_apex(ldns_buffer* pkt) -{ - /* we are at ttl position in packet. */ - size_t pos = ldns_buffer_position(pkt); - uint16_t rdatalen; - if(ldns_buffer_remaining(pkt) < 7) /* ttl+len+root */ - return 0; /* eek! */ - ldns_buffer_skip(pkt, 4); /* ttl */; - rdatalen = ldns_buffer_read_u16(pkt); - if(ldns_buffer_remaining(pkt) < rdatalen) { - ldns_buffer_set_position(pkt, pos); - return 0; /* parse error happens later */ - } - /* must validate the nsec next domain name format */ - if(pkt_dname_len(pkt) == 0) { - ldns_buffer_set_position(pkt, pos); - return 0; /* parse error */ - } - - /* see if SOA bit is set. */ - if(ldns_buffer_position(pkt) < pos+rdatalen) { - /* nsec type bitmap contains items */ - uint8_t win, blen, bits; - /* need: windownum, bitmap len, firstbyte */ - if(ldns_buffer_position(pkt)+3 <= pos+rdatalen) { - ldns_buffer_set_position(pkt, pos); - return 0; /* malformed nsec */ - } - win = ldns_buffer_read_u8(pkt); - blen = ldns_buffer_read_u8(pkt); - bits = ldns_buffer_read_u8(pkt); - /* 0window always first window. bitlen >=1 or parse - error really. bit 0x2 is SOA. */ - if(win == 0 && blen >= 1 && (bits & 0x02)) { - ldns_buffer_set_position(pkt, pos); - return 1; - } - } - - ldns_buffer_set_position(pkt, pos); - return 0; -} - -/** 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) -{ - 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 = dname_pkt_hash(pkt, dname, h); - return h; -} - -/** compare rrset_parse with data. */ -static int -rrset_parse_equals(struct rrset_parse* p, ldns_buffer* pkt, hashvalue_t h, - uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, - uint16_t type, uint16_t dclass) -{ - if(p->hash == h && p->dname_len == dnamelen && p->type == type && - p->rrset_class == dclass && p->flags == rrset_flags && - dname_pkt_compare(pkt, dname, p->dname) == 0) - return 1; - return 0; -} - - -/** - * Lookup in msg hashtable to find a rrset - */ -static struct rrset_parse* -hashtable_lookup(struct msg_parse* msg, ldns_buffer* pkt, hashvalue_t h, - uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, - uint16_t type, uint16_t dclass) -{ - struct rrset_parse* p = msg->hashtable[h & (PARSE_TABLE_SIZE-1)]; - while(p) { - if(rrset_parse_equals(p, pkt, h, rrset_flags, dname, dnamelen, - type, dclass)) - return p; - p = p->rrset_bucket_next; - } - return NULL; -} - -/** 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 dname: pointer to start of dname (compressed) in packet. - * @param dnamelen: uncompressed wirefmt length of dname. - * @param type: type of current rr. - * @param dclass: class of current rr. - * @param hash: hash value is returned if the rrset could not be found. - * @param rrset_flags: is returned if the rrset could not be found. - * @param prev_dname_first: dname of last seen RR. First seen dname. - * @param prev_dname_last: dname of last seen RR. Last seen dname. - * @param prev_dnamelen: dname len of last seen RR. - * @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. - */ -static struct rrset_parse* -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) -{ - if(rrset_prev) { - /* check if equal to previous item */ - if(type == *prev_type && dclass == *prev_dclass && - dnamelen == *prev_dnamelen && - smart_compare(pkt, dname, *prev_dname_first, - *prev_dname_last) == 0) { - /* same as previous */ - *prev_dname_last = dname; - return *rrset_prev; - } - - } - /* find by hashing and lookup in hashtable */ - *hash = pkt_hash_rrset(msg, pkt, dname, type, dclass, rrset_flags); - *rrset_prev = hashtable_lookup(msg, pkt, *hash, *rrset_flags, - dname, dnamelen, type, dclass); - if(*rrset_prev) - *prev_dname_first = (*rrset_prev)->dname; - else *prev_dname_first = dname; - *prev_dname_last = dname; - *prev_dnamelen = dnamelen; - *prev_type = type; - *prev_dclass = dclass; - return *rrset_prev; -} - -/** - * Parse query section. - * @param pkt: packet, position at call must be at start of query section. - * at end position is after query section. - * @param msg: store results here. - * @return: 0 if OK, or rcode on error. - */ -static int -parse_query_section(ldns_buffer* pkt, struct msg_parse* msg) -{ - if(msg->qdcount == 0) - return 0; - if(msg->qdcount > 1) - return LDNS_RCODE_FORMERR; - log_assert(msg->qdcount == 1); - if(ldns_buffer_remaining(pkt) <= 0) - return LDNS_RCODE_FORMERR; - msg->qname = ldns_buffer_current(pkt); - if((msg->qname_len = query_dname_len(pkt)) == 0) - return LDNS_RCODE_FORMERR; - if(ldns_buffer_remaining(pkt) < sizeof(uint16_t)*2) - return LDNS_RCODE_FORMERR; - msg->qtype = ldns_buffer_read_u16(pkt); - msg->qclass = ldns_buffer_read_u16(pkt); - 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->rr_first = 0; - p->rr_last = 0; - return p; -} - -/** 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) -{ - uint16_t rdatalen; - /* check section of rrset. */ - if(rrset->section != section) { - /* silently drop it */ - verbose(VERB_DETAIL, "Packet contains rrset data in " - "multiple sections, dropped last part."); - } else { - /* create rr */ - struct rr_parse* rr = region_alloc(region, sizeof(*rr)); - if(!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++; - } - - /* 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) - return LDNS_RCODE_FORMERR; - ldns_buffer_skip(pkt, (ssize_t)rdatalen); - return 0; -} - -/** - * Parse packet RR section, for answer, authority and additional sections. - * @param pkt: packet, position at call must be at start of section. - * at end position is after section. - * @param msg: store results here. - * @param region: how to alloc results. - * @param section: section enum. - * @param num_rrs: how many rrs are in the section. - * @param num_rrsets: returns number of rrsets in the section. - * @return: 0 if OK, or rcode on error. - */ -static int -parse_section(ldns_buffer* pkt, struct msg_parse* msg, region_type* region, - ldns_pkt_section section, uint16_t num_rrs, size_t* num_rrsets) -{ - uint16_t i; - uint8_t* dname, *prev_dname_f = NULL, *prev_dname_l = NULL; - size_t dnamelen, prev_dnamelen = 0; - uint16_t type, prev_type = 0; - uint16_t dclass, prev_dclass = 0; - uint32_t rrset_flags = 0; - hashvalue_t hash = 0; - struct rrset_parse* rrset, *rrset_prev = NULL; - int r; - - if(num_rrs == 0) - return 0; - if(ldns_buffer_remaining(pkt) <= 0) - return LDNS_RCODE_FORMERR; - for(i=0; iid, sizeof(uint16_t)); - msg->flags = ldns_buffer_read_u16(pkt); - msg->qdcount = ldns_buffer_read_u16(pkt); - msg->ancount = ldns_buffer_read_u16(pkt); - msg->nscount = ldns_buffer_read_u16(pkt); - msg->arcount = ldns_buffer_read_u16(pkt); - if(msg->qdcount > 1) - return LDNS_RCODE_FORMERR; - if((ret = parse_query_section(pkt, msg)) != 0) - return ret; - if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ANSWER, - msg->ancount, &msg->an_rrsets)) != 0) - return ret; - if((ret = parse_section(pkt, msg, region, LDNS_SECTION_AUTHORITY, - msg->nscount, &msg->ns_rrsets)) != 0) - return ret; - if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ADDITIONAL, - msg->arcount, &msg->ar_rrsets)) != 0) - return ret; - if(ldns_buffer_remaining(pkt) > 0) { - /* spurious data at end of packet. ignore */ - verbose(VERB_DETAIL, "spurious data at end of packet ignored"); - } - return 0; -} +#include "util/data/msgparse.h" /** copy and allocate an uncompressed dname. */ static uint8_t*