]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Dname compare routines with compression pointers.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 12 Apr 2007 14:54:34 +0000 (14:54 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 12 Apr 2007 14:54:34 +0000 (14:54 +0000)
git-svn-id: file:///svn/unbound/trunk@239 be551aaa-1e26-0410-a405-d3ace91eadb9

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

index b9d0bdfc8e07e3c85411b2afbddc43875407d7b9..6fa94c2b262f16bdf58c40319ce7a5386dbbb6b1 100644 (file)
@@ -185,3 +185,44 @@ pkt_dname_len(ldns_buffer* pkt)
 
        return len;
 }
+
+int 
+dname_pkt_compare(ldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
+{
+       uint8_t len1, len2;
+       log_assert(pkt && d1 && d2);
+       len1 = *d1++;
+       len2 = *d2++;
+       while( len1 != 0 || len2 != 0 ) {
+               /* resolve ptrs */
+               if( (len1 & 0xc0) == 0xc0) {
+                       d1 = ldns_buffer_at(pkt, (len1&0x3f)<<8 | *d1);
+                       len1 = *d1++;
+                       continue;
+               }
+               if( (len2 & 0xc0) == 0xc0) {
+                       d2 = ldns_buffer_at(pkt, (len2&0x3f)<<8 | *d2);
+                       len2 = *d2++;
+                       continue;
+               }
+               /* check label length */
+               log_assert(len1 <= LDNS_MAX_LABELLEN);
+               log_assert(len2 <= LDNS_MAX_LABELLEN);
+               if(len1 != len2) {
+                       if(len1 < len2) return -1;
+                       return 1;
+               }
+               log_assert(len1 == len2 && len1 != 0);
+               /* compare labels */
+               while(len1--) {
+                       if(tolower((int)*d1++) != tolower((int)*d2++)) {
+                               if(tolower((int)d1[-1]) < tolower((int)d2[-1]))
+                                       return -1;
+                               return 1;
+                       }
+               }
+               len1 = *d1++;
+               len2 = *d2++;
+       }
+       return 0;
+}
index ce4eaecf3ea6ed6344436a00676c456dc00dae13..f63b9ed738f1e4c51915551bba9a283e1bc75f12 100644 (file)
@@ -78,4 +78,15 @@ int query_dname_compare(uint8_t* d1, uint8_t* d2);
  */
 size_t pkt_dname_len(ldns_buffer* pkt);
 
+/**
+ * Compare dnames in packet (compressed). Dnames must be valid.
+ * routine performs lowercasing, so the packet casing is preserved.
+ * @param pkt: packet, used to resolve compression pointers.
+ * @param d1: dname to compare
+ * @param d2: dname to compare
+ * @return: -1, 0, or +1 depending on comparison results.
+ *     Sort order is first difference found. not the canonical ordering.
+ */
+int dname_pkt_compare(ldns_buffer* pkt, uint8_t* d1, uint8_t* d2);
+
 #endif /* UTIL_DATA_DNAME_H */
index b69e984d1acbeeb30b64a5ffd9518697e11e7359..ed644c83dc579e36c1478f164a23858b0397f4d0 100644 (file)
@@ -143,14 +143,51 @@ struct rr_parse {
        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)
+{
+       uint8_t* p;
+       if( (*dnow & 0xc0) == 0xc0) {
+               /* 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;
+               /* ptr points to a previous dname */
+               p = ldns_buffer_at(pkt, (dnow[0]&0x3f)<<8 | dnow[1]);
+               if( p == dprfirst || p == dprlast )
+                       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);
+}
+
 /** 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 prev_dname: dname of last seen RR.
+ * @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.
@@ -158,13 +195,22 @@ struct rr_parse {
  * @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,
+find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname, 
+       size_t dnamelen, uint16_t type, uint16_t dclass, hashvalue_t* hash, 
+       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 */
@@ -214,7 +260,7 @@ 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;
+       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;
@@ -236,9 +282,9 @@ parse_section(ldns_buffer* pkt, struct msg_parse* msg, region_type* region,
                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) {
+               if((rrset = find_rrset(msg, pkt, dname, dnamelen, type, dclass,
+                       &hash, &prev_dname_f, &prev_dname_l, &prev_dnamelen, 
+                       &prev_type, &prev_dclass, &rrset_prev)) != 0) {
                        /* check if it fits the existing rrset */
                        /* add to rrset. */
                } else {