]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
can use DNS-0x20 draft casing.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 27 Feb 2008 09:21:31 +0000 (09:21 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 27 Feb 2008 09:21:31 +0000 (09:21 +0000)
git-svn-id: file:///svn/unbound/trunk@994 be551aaa-1e26-0410-a405-d3ace91eadb9

daemon/worker.c
doc/Changelog
libunbound/libworker.c
services/outside_network.c
services/outside_network.h
testcode/fake_event.c
util/config_file.c
util/config_file.h
util/data/dname.c
util/data/dname.h

index 5c2839be45e715a205312d8d5b3e2aac6754a34c..58a930d24ddd803a50b89b74a69627d5db2f5b23 100644 (file)
@@ -977,7 +977,8 @@ worker_init(struct worker* worker, struct config_file *cfg,
                cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports, 
                cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6, 
                startport, cfg->do_tcp?cfg->outgoing_num_tcp:0, 
-               worker->daemon->env->infra_cache, worker->rndstate);
+               worker->daemon->env->infra_cache, worker->rndstate,
+               cfg->use_caps_bits_for_id);
        if(!worker->back) {
                log_err("could not create outgoing sockets");
                worker_delete(worker);
index 5ba74dd46291bf2523d244c2edb5d0784b57a589..8ff1dc5e80813bc69b71a966790b116aec89f3d6 100644 (file)
@@ -1,3 +1,6 @@
+27 February 2008: Wouter
+       - option to use caps for id randomness.
+
 26 February 2008: Wouter
        - delay utility delays TCP as well. If the server that is forwarded 
          to has a TCP error, the delay utility closes the connection.
index f04cde4efff82825e88b82f3beca95ed53d2974d..446071cff88e7d1416653fc03bec520d3abba1a8 100644 (file)
@@ -153,7 +153,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg)
                (size_t)cfg->outgoing_num_ports, cfg->out_ifs,
                cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6, -1, 
                cfg->do_tcp?cfg->outgoing_num_tcp:0,
-               w->env->infra_cache, w->env->rnd);
+               w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id);
        if(!w->is_bg || w->is_bg_thread) {
                lock_basic_unlock(&ctx->cfglock);
        }
index d1d2f86d60c43d2060663b094126127264d1d93a..cf8798697042c489b2939277c4722087dd32eabc 100644 (file)
@@ -46,6 +46,7 @@
 #include "util/data/msgparse.h"
 #include "util/data/msgreply.h"
 #include "util/data/msgencode.h"
+#include "util/data/dname.h"
 #include "util/netevent.h"
 #include "util/log.h"
 #include "util/net_help.h"
@@ -91,14 +92,19 @@ serviced_cmp(const void* key1, const void* key2)
        if(q1->qbuflen > q2->qbuflen)
                return 1;
        log_assert(q1->qbuflen == q2->qbuflen);
-       /* will not detect alternate casing of qname */
-       if((r = memcmp(q1->qbuf, q2->qbuf, q1->qbuflen)) != 0)
+       log_assert(q1->qbuflen >= 15 /* 10 header, root, type, class */);
+       /* alternate casing of qname is still the same query */
+       if((r = memcmp(q1->qbuf, q2->qbuf, 10)) != 0)
+               return r;
+       if((r = memcmp(q1->qbuf+q1->qbuflen-4, q2->qbuf+q2->qbuflen-4, 4)) != 0)
                return r;
        if(q1->dnssec != q2->dnssec) {
                if(q1->dnssec < q2->dnssec)
                        return -1;
                return 1;
        }
+       if((r = query_dname_compare(q1->qbuf+10, q2->qbuf+10)) != 0)
+               return r;
        return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen);
 }
 
@@ -421,7 +427,7 @@ struct outside_network*
 outside_network_create(struct comm_base *base, size_t bufsize, 
        size_t num_ports, char** ifs, int num_ifs, int do_ip4, 
        int do_ip6, int port_base, size_t num_tcp, struct infra_cache* infra,
-       struct ub_randstate* rnd)
+       struct ub_randstate* rnd, int use_caps_for_id)
 {
        struct outside_network* outnet = (struct outside_network*)
                calloc(1, sizeof(struct outside_network));
@@ -436,6 +442,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
        outnet->infra = infra;
        outnet->rnd = rnd;
        outnet->svcd_overhead = 0;
+       outnet->use_caps_for_id = use_caps_for_id;
 #ifndef INET6
        do_ip6 = 0;
 #endif
@@ -912,10 +919,52 @@ serviced_delete(struct serviced_query* sq)
        serviced_node_del(&sq->node, NULL);
 }
 
+/** perturb a dname capitalization randomly */
+static void
+serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len)
+{
+       uint8_t lablen;
+       uint8_t* d = qbuf + 10;
+       long int random = 0;
+       int bits = 0;
+       log_assert(len >= 10 + 5 /* offset qname, root, qtype, qclass */);
+       lablen = *d++;
+       while(lablen) {
+               while(lablen--) {
+                       /* only perturb A-Z, a-z */
+                       if(isalpha((int)*d)) {
+                               /* get a random bit */  
+                               if(bits == 0) {
+                                       random = ub_random(rnd);
+                                       bits = 30;
+                               }
+                               if(random & 0x1) {
+                                       *d = (uint8_t)toupper((int)*d);
+                               } else {
+                                       *d = (uint8_t)tolower((int)*d);
+                               }
+                               random >>= 1;
+                               bits--;
+                       }
+                       d++;
+               }
+               lablen = *d++;
+       }
+       if(verbosity >= VERB_ALGO) {
+               char buf[LDNS_MAX_DOMAINLEN+1];
+               dname_str(qbuf+10, buf);
+               verbose(VERB_ALGO, "qname perturbed to %s", buf);
+       }
+}
+
 /** put serviced query into a buffer */
 static void
 serviced_encode(struct serviced_query* sq, ldns_buffer* buff, int with_edns)
 {
+       /* if we are using 0x20 bits for ID randomness, perturb them */
+       if(sq->outnet->use_caps_for_id) {
+               serviced_perturb_qname(sq->outnet->rnd, sq->qbuf, sq->qbuflen);
+       }
        /* generate query */
        ldns_buffer_clear(buff);
        ldns_buffer_write_u16(buff, 0); /* id placeholder */
@@ -968,6 +1017,49 @@ serviced_udp_send(struct serviced_query* sq, ldns_buffer* buff)
        return 1;
 }
 
+/** check that perturbed qname is identical */
+static int
+serviced_check_qname(ldns_buffer* pkt, uint8_t* qbuf, size_t qbuflen)
+{
+       uint8_t* d1 = ldns_buffer_at(pkt, 12);
+       uint8_t* d2 = qbuf+10;
+       uint8_t len1, len2;
+       int count = 0;
+       log_assert(qbuflen >= 15 /* 10 header, root, type, class */);
+       len1 = *d1++;
+       len2 = *d2++;
+       if(ldns_buffer_limit(pkt) < 12+1+4) /* packet too small for qname */
+               return 0;
+       while(len1 != 0 || len2 != 0) {
+               if(LABEL_IS_PTR(len1)) {
+                       d1 = ldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
+                       if(d1 >= ldns_buffer_at(pkt, ldns_buffer_limit(pkt)))
+                               return 0;
+                       len1 = *d1++;
+                       if(count++ > MAX_COMPRESS_PTRS)
+                               return 0;
+                       continue;
+               }
+               if(d2 > qbuf+qbuflen)
+                       return 0;
+               if(len1 != len2)
+                       return 0;
+               if(len1 > LDNS_MAX_LABELLEN)
+                       return 0;
+               log_assert(len1 <= LDNS_MAX_LABELLEN);
+               log_assert(len2 <= LDNS_MAX_LABELLEN);
+               log_assert(len1 == len2 && len1 != 0);
+               /* compare the labels - bitwise identical */
+               if(memcmp(d1, d2, len1) != 0)
+                       return 0;
+               d1 += len1;
+               d2 += len2;
+               len1 = *d1++;
+               len2 = *d2++;
+       }
+       return 1;
+}
+
 /** call the callbacks for a serviced query */
 static void
 serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
@@ -985,6 +1077,36 @@ serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
        log_assert(rem); /* should have been present */
        sq->to_be_deleted = 1; 
        verbose(VERB_ALGO, "svcd callbacks start");
+       if(sq->outnet->use_caps_for_id && error == NETEVENT_NOERROR && c) {
+               /* noerror and nxdomain must have a qname in reply */
+               if(ldns_buffer_read_u16_at(c->buffer, 4) == 0 &&
+                       (LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
+                               == LDNS_RCODE_NOERROR || 
+                        LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
+                               == LDNS_RCODE_NXDOMAIN)) {
+                       verbose(VERB_OPS, "no qname in reply to check 0x20ID");
+                       log_addr(VERB_OPS, "from server", 
+                               &sq->addr, sq->addrlen);
+                       log_buf(VERB_OPS, "for packet", c->buffer);
+                       error = NETEVENT_CLOSED;
+                       c = NULL;
+               } else if(ldns_buffer_read_u16_at(c->buffer, 4) > 0 &&
+                       !serviced_check_qname(c->buffer, sq->qbuf, 
+                       sq->qbuflen)) {
+                       verbose(VERB_OPS, "wrong 0x20-ID in reply qname, "
+                               "answer dropped");
+                       log_addr(VERB_OPS, "from server", 
+                               &sq->addr, sq->addrlen);
+                       log_buf(VERB_OPS, "for packet", c->buffer);
+                       error = NETEVENT_CLOSED;
+                       c = NULL;
+               } else {
+                       verbose(VERB_ALGO, "good 0x20-ID in reply qname");
+                       /* cleanup caps, prettier cache contents. */
+                       pkt_dname_tolower(c->buffer, 
+                               ldns_buffer_at(c->buffer, 12));
+               }
+       }
        if(dobackup && c) {
                /* make a backup of the query, since the querystate processing
                 * may send outgoing queries that overwrite the buffer.
index 97257f287b96e77d103daad854965c6862d2c52d..7286a614c58395a8c8d9070fda3225fcb17123ff 100644 (file)
@@ -71,6 +71,8 @@ struct outside_network {
        /** serviced_callbacks malloc overhead when processing multiple
         * identical serviced queries to the same server. */
        size_t svcd_overhead;
+       /** use x20 bits to encode additional ID random bits */
+       int use_caps_for_id;
 
        /** 
         * Array of udp comm point* that are used to listen to pending events.
@@ -261,12 +263,14 @@ struct serviced_query {
  * @param num_tcp: number of outgoing tcp buffers to preallocate.
  * @param infra: pointer to infra cached used for serviced queries.
  * @param rnd: stored to create random numbers for serviced queries.
+ * @param use_caps_for_id: enable to use 0x20 bits to encode id randomness.
  * @return: the new structure (with no pending answers) or NULL on error.
  */
 struct outside_network* outside_network_create(struct comm_base* base,
        size_t bufsize, size_t num_ports, char** ifs, int num_ifs,
        int do_ip4, int do_ip6, int port_base, size_t num_tcp, 
-       struct infra_cache* infra, struct ub_randstate* rnd);
+       struct infra_cache* infra, struct ub_randstate* rnd,
+       int use_caps_for_id);
 
 /**
  * Delete outside_network structure.
index 4b86dfcebe09654d30918ebbf147f5a3ae0bc94c..ff50bd3ce1de57da208c58e342f334d2827f2ac6 100644 (file)
@@ -686,7 +686,7 @@ outside_network_create(struct comm_base* base, size_t bufsize,
        int ATTR_UNUSED(num_ifs), int ATTR_UNUSED(do_ip4), 
        int ATTR_UNUSED(do_ip6), int ATTR_UNUSED(port_base),
        size_t ATTR_UNUSED(num_tcp), struct infra_cache* ATTR_UNUSED(infra),
-       struct ub_randstate* ATTR_UNUSED(rnd))
+       struct ub_randstate* ATTR_UNUSED(rnd), int ATTR_UNUSED(use_caps_for_id))
 {
        struct outside_network* outnet =  calloc(1, 
                sizeof(struct outside_network));
index 45b546e4dc49be85e1f657998790cfd72cd20764..85b5bdd97bcabf86fe9625062ee9c4ec8ed3ee72 100644 (file)
@@ -117,6 +117,7 @@ config_create()
        cfg->harden_large_queries = 0;
        cfg->harden_glue = 1;
        cfg->harden_dnssec_stripped = 1;
+       cfg->use_caps_bits_for_id = 0;
        cfg->hide_identity = 0;
        cfg->hide_version = 0;
        cfg->identity = NULL;
index de27de4e6529f6103b874edc06e96553422dc77c..9ad387dbb1a883db8d7dac194bc8a34062f92ca4 100644 (file)
@@ -140,6 +140,8 @@ struct config_file {
        int harden_glue;
        /** harden against receiving no DNSSEC data for trust anchor */
        int harden_dnssec_stripped;
+       /** use 0x20 bits in query as random ID bits */
+       int use_caps_bits_for_id;
 
        /** chrootdir, if not "" or chroot will be done */
        char* chrootdir;
index e9e004ed634a480365e0cf993e6a7d94501236b2..fca5866b989ff17a547ee9837be8145f36c787af 100644 (file)
@@ -143,10 +143,25 @@ query_dname_tolower(uint8_t* dname)
        }
 }
 
-/** maximum compression pointer position pointed to */
-#define MAX_COMPRESS_POS 16384
-/** max number of compression ptrs to follow */
-#define MAX_COMPRESS_PTRS 256
+void 
+pkt_dname_tolower(ldns_buffer* pkt, uint8_t* dname)
+{
+       uint8_t lablen;
+       lablen = *dname++;
+       while(lablen) {
+               if(LABEL_IS_PTR(lablen)) {
+                       dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+                       lablen = *dname++;
+                       continue;
+               }
+               while(lablen--) {
+                       *dname = (uint8_t)tolower((int)*dname);
+                       dname++;
+               }
+               lablen = *dname++;
+       }
+}
+
 
 size_t
 pkt_dname_len(ldns_buffer* pkt)
index 4d1d7b34f2a8c5d0e1af0e953f50e51263cb09f5..0a7803c18c3b54ab4d39167ecfbdb3e4942b7ceb 100644 (file)
@@ -46,6 +46,9 @@
 #define UTIL_DATA_DNAME_H
 #include "util/storage/lruhash.h"
 
+/** max number of compression ptrs to follow */
+#define MAX_COMPRESS_PTRS 256
+
 /** 
  * Determine length of dname in buffer, no compression ptrs allowed, 
  * @param query: the ldns buffer, current position at start of dname.
@@ -65,6 +68,14 @@ size_t dname_valid(uint8_t* dname, size_t len);
 /** lowercase query dname */
 void query_dname_tolower(uint8_t* dname);
 
+/** 
+ * lowercase pkt dname (follows compression pointers)
+ * @param pkt: the packet, used to follow compression pointers. Position 
+ *     is unchanged.
+ * @param dname: start of dname in packet.
+ */
+void pkt_dname_tolower(ldns_buffer* pkt, uint8_t* dname);
+
 /**
  * Compare query dnames (uncompressed storage). The Dnames passed do not
  * have to be lowercased, comparison routine does this.