From: Wouter Wijngaards Date: Wed, 11 Apr 2007 14:26:29 +0000 (+0000) Subject: Parsing X-Git-Tag: release-0.3~66 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=67636506241579cb1a468e6d2df182a94c6affc6;p=thirdparty%2Funbound.git Parsing git-svn-id: file:///svn/unbound/trunk@237 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index e87704abd..065a0b6d4 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +11 April 2007: Wouter + - parse work - dname packet parse, msgparse, querysection parse, + start of sectionparse. 10 April 2007: Wouter - Improved alignment of reply_info packet, nice for 32 and 64 bit. - Put RRset counts in reply_info, because the number of RRs can change diff --git a/util/data/dname.c b/util/data/dname.c index 79e4e8087..8c8c0e80d 100644 --- a/util/data/dname.c +++ b/util/data/dname.c @@ -83,4 +83,72 @@ query_dname_tolower(uint8_t* dname, size_t len) } } +/** maximum compression pointer position pointed to */ +#define MAX_COMPRESS_POS 16384 +/** size of bitmap for loop detection */ +#define LOOP_BITMAP_SIZE (MAX_COMPRESS_POS/8) +/** check bit in bitmap for loop detection, then set it for next check */ +static uint8_t +loopcheck(uint8_t loop[], size_t pos) +{ + const uint8_t bits[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; + uint8_t ret; + log_assert(pos < MAX_COMPRESS_POS); + ret = loop[ pos / 8 ] & bits[ pos % 8 ]; + loop[ pos / 8 ] |= bits[ pos % 8 ]; + return ret; +} + + +size_t +pkt_dname_len(ldns_buffer* pkt) +{ + size_t len = 0; + uint8_t loop[LOOP_BITMAP_SIZE]; /* loopcheck array. */ + uint8_t labellen; + size_t endpos = 0; + + /* read dname and determine length */ + /* check compression pointers, loops, out of bounds */ + memset(loop, 0, sizeof(loop)); + + while(1) { + /* read next label */ + if(ldns_buffer_remaining(pkt) < 1) + return 0; + labellen = ldns_buffer_read_u8(pkt); + if( (labellen & 0xc0) == 0xc0 ) { + /* compression ptr */ + uint16_t ptr = (labellen & 0x3f) << 8; + if(ldns_buffer_remaining(pkt) < 1) + return 0; + ptr |= ldns_buffer_read_u8(pkt); + if(loopcheck(loop, ptr)) + return 0; /* loop! */ + if(ldns_buffer_limit(pkt) <= ptr) + return 0; /* out of bounds! */ + if(!endpos) + endpos = ldns_buffer_position(pkt); + ldns_buffer_set_position(pkt, ptr); + } else { + /* label contents */ + if(labellen > 0x3f) + return 0; /* label too long */ + len += 1 + labellen; + if(len > LDNS_MAX_DOMAINLEN) + return 0; + if(labellen == 0) { + /* end of dname */ + break; + } + if(ldns_buffer_remaining(pkt) < labellen) + return 0; + ldns_buffer_skip(pkt, (ssize_t)labellen); + } + } + if(endpos) + ldns_buffer_set_position(pkt, endpos); + + return len; +} diff --git a/util/data/dname.h b/util/data/dname.h index e76ea2294..4a10bdce6 100644 --- a/util/data/dname.h +++ b/util/data/dname.h @@ -56,4 +56,15 @@ size_t query_dname_len(ldns_buffer* query); /** lowercase query dname */ void query_dname_tolower(uint8_t* dname, size_t len); +/** + * Determine correct, compressed, dname present in packet. + * Checks for parse errors. + * @param pkt: packet to read from (from current start position). + * @return: 0 on parse error. + * At exit the position is right after the (compressed) dname. + * Compression pointers are followed and checked for loops. + * The uncompressed wireformat length is returned. + */ +size_t pkt_dname_len(ldns_buffer* pkt); + #endif /* UTIL_DATA_DNAME_H */ diff --git a/util/data/msgreply.c b/util/data/msgreply.c index f3be220f2..d7f5ff191 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -46,6 +46,7 @@ #include "util/netevent.h" #include "util/net_help.h" #include "util/data/dname.h" +#include "util/region-allocator.h" struct rrset_parse; struct rr_parse; @@ -58,6 +59,8 @@ struct rr_parse; * 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 */ @@ -108,8 +111,14 @@ struct rrset_parse { 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. */ @@ -134,10 +143,183 @@ struct rr_parse { struct rr_parse* next; }; +/** Find rrset. If equal to previous it is fast. hash if not so. + * @param msg: the message with hash table. + * @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 prev_dname: dname of last seen RR. + * @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, uint8_t* dname, size_t dnamelen, + uint16_t type, uint16_t dclass, hashvalue_t* hash, + uint8_t** prev_dname, 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 */ + + } + /* find by hashing and lookup in hashtable */ + return NULL; +} + +/** + * 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; +} + +/** + * 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 = NULL; + size_t dnamelen, prev_dnamelen = 0; + uint16_t type, prev_type = 0; + uint16_t dclass, prev_dclass = 0; + hashvalue_t hash; + struct rrset_parse* rrset, *rrset_prev = NULL; + + 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, ign."); + } + return 0; +} + + int reply_info_parse(ldns_buffer* pkt, struct alloc_cache* alloc, struct query_info* qinf, struct reply_info** rep) { /* use scratch pad region-allocator during parsing. */ + region_type* region = region_create(malloc, free); + struct msg_parse* msg; + int ret; + + *rep = NULL; + msg = region_alloc(region, sizeof(*msg)); + if(!msg) + goto malloc_error; + memset(msg, 0, sizeof(*msg)); + + log_assert(ldns_buffer_position(pkt) == 0); + if((ret = parse_packet(pkt, msg, region)) != 0) { + region_free_all(region); + region_destroy(region); + return ret; + } + /* parse OK, allocate return structures */ + + /* exit and cleanup */ + region_free_all(region); + region_destroy(region); + return 0; +malloc_error: + region_free_all(region); + region_destroy(region); return LDNS_RCODE_SERVFAIL; }