]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Parsing
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 11 Apr 2007 14:26:29 +0000 (14:26 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 11 Apr 2007 14:26:29 +0000 (14:26 +0000)
git-svn-id: file:///svn/unbound/trunk@237 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
util/data/dname.c
util/data/dname.h
util/data/msgreply.c

index e87704abd5e56ac5ef5d69f8e26945aea8d73705..065a0b6d4492704ae3c2cd5fcbf3314fa2c544ae 100644 (file)
@@ -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
index 79e4e80872fea09f94b5b78204e0a308e91343fe..8c8c0e80daee7009aab683fbd1fbdf2d5edcac1b 100644 (file)
@@ -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;
+}
index e76ea229419aac524435a688889938f380ca6495..4a10bdce6d1325274db8775476f99329d8732368 100644 (file)
@@ -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 */
index f3be220f22b83b4f65cbe4f009750d733d15e37b..d7f5ff1918242c9603332afd760436fb2d0c44fa 100644 (file)
@@ -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; i<num_rrs; i++) {
+               /* parse this RR. */
+               dname = ldns_buffer_current(pkt);
+               if((dnamelen = pkt_dname_len(pkt)) == 0)
+                       return LDNS_RCODE_FORMERR;
+               if(ldns_buffer_remaining(pkt) < 10) /* type, class, ttl, len */
+                       return LDNS_RCODE_FORMERR;
+               ldns_buffer_read(pkt, &type, sizeof(type));
+               ldns_buffer_read(pkt, &dclass, sizeof(dclass));
+
+               /* see if it is part of an existing RR set */
+               if((rrset = find_rrset(msg, dname, dnamelen, type, dclass,
+                       &hash, &prev_dname, &prev_dnamelen, &prev_type,
+                       &prev_dclass, &rrset_prev)) != 0) {
+                       /* check if it fits the existing rrset */
+                       /* add to rrset. */
+               } else {
+                       /* it is a new RR set. hash already calculated. */
+               }
+       }
+       return 0;
+}
+
+/**
+ * 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.
+ */
+static int
+parse_packet(ldns_buffer* pkt, struct msg_parse* msg,
+        region_type* region)
+{
+       int ret;
+       if(ldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE)
+               return LDNS_RCODE_FORMERR;
+       /* read the header */
+       ldns_buffer_read(pkt, &msg->id, 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;
 }