From: Wouter Wijngaards Date: Wed, 27 Feb 2008 09:21:31 +0000 (+0000) Subject: can use DNS-0x20 draft casing. X-Git-Tag: release-0.10~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=130a5f2dee12e029a9086e07f0fb6e18c12828de;p=thirdparty%2Funbound.git can use DNS-0x20 draft casing. git-svn-id: file:///svn/unbound/trunk@994 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index 5c2839be4..58a930d24 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -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); diff --git a/doc/Changelog b/doc/Changelog index 5ba74dd46..8ff1dc5e8 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/libunbound/libworker.c b/libunbound/libworker.c index f04cde4ef..446071cff 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -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); } diff --git a/services/outside_network.c b/services/outside_network.c index d1d2f86d6..cf8798697 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -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. diff --git a/services/outside_network.h b/services/outside_network.h index 97257f287..7286a614c 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -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. diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 4b86dfceb..ff50bd3ce 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -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)); diff --git a/util/config_file.c b/util/config_file.c index 45b546e4d..85b5bdd97 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -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; diff --git a/util/config_file.h b/util/config_file.h index de27de4e6..9ad387dbb 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -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; diff --git a/util/data/dname.c b/util/data/dname.c index e9e004ed6..fca5866b9 100644 --- a/util/data/dname.c +++ b/util/data/dname.c @@ -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) diff --git a/util/data/dname.h b/util/data/dname.h index 4d1d7b34f..0a7803c18 100644 --- a/util/data/dname.h +++ b/util/data/dname.h @@ -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.