diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..43fd62d --- /dev/null +++ b/.gitignore @@ -0,0 +1,61 @@ +auto_home.c + +direntry.h +hasdevtcp.h +hasshsgr.h +haveip6.h +haven2i.h +iopause.h +select.h +sockaddr_in6.h +uint32.h +uint64.h + +socket.lib + +*.o +*.a + +auto-str +axfr-get +axfrdns +axfrdns-conf +cachetest +chkshsgr +choose +compile +dnscache +dnscache-conf +dnsfilter +dnsip +dnsip6 +dnsip6q +dnsipq +dnsmx +dnsname +dnsq +dnsqr +dnstrace +dnstracesort +dnstxt +install +instcheck +load +makelib +pickdns +pickdns-conf +pickdns-data +random-ip +rbldns +rbldns-conf +rbldns-data +rts +systype +tinydns +tinydns-conf +tinydns-data +tinydns-edit +tinydns-get +utime +walldns +walldns-conf diff --git a/Makefile b/Makefile index 9ebf4c8..aef9420 100644 --- a/Makefile +++ b/Makefile @@ -31,12 +31,20 @@ compile auto-str.c buffer.h exit.h auto_home.c: \ auto-str conf-home - ./auto-str auto_home `head -1 conf-home` > auto_home.c + ./auto-str auto_home `head -n 1 conf-home` > auto_home.c auto_home.o: \ compile auto_home.c ./compile auto_home.c +auto_destdir.c: \ +auto-str conf-home + ./auto-str auto_home `head -n 1 conf-destdir``head -n 1 conf-home` > auto_destdir.c + +auto_destdir.o: \ +compile auto_destdir.c + ./compile auto_destdir.c + axfr-get: \ load axfr-get.o iopause.o timeoutread.o timeoutwrite.o dns.a libtai.a \ alloc.a buffer.a unix.a byte.a @@ -151,12 +159,12 @@ compile byte_zero.c byte.h cache.o: \ compile cache.c alloc.h byte.h uint32.h exit.h tai.h uint64.h cache.h \ -uint32.h uint64.h +uint32.h uint64.h siphash.h ./compile cache.c cachetest: \ -load cachetest.o cache.o libtai.a buffer.a alloc.a unix.a byte.a - ./load cachetest cache.o libtai.a buffer.a alloc.a unix.a \ +load cachetest.o siphash.o cache.o dns_random.o libtai.a buffer.a alloc.a unix.a byte.a + ./load cachetest siphash.o cache.o dns_random.o libtai.a buffer.a alloc.a unix.a \ byte.a cachetest.o: \ @@ -207,7 +215,7 @@ compile chkshsgr.c exit.h choose: \ warn-auto.sh choose.sh conf-home cat warn-auto.sh choose.sh \ - | sed s}HOME}"`head -1 conf-home`"}g \ + | sed s}HOME}"`head -n 1 conf-home`"}g \ > choose chmod 755 choose @@ -218,7 +226,7 @@ compile clientloc.c open.h byte.h cdb.h ip6.h compile: \ warn-auto.sh conf-cc ( cat warn-auto.sh; \ - echo exec "`head -1 conf-cc`" '-c $${1+"$$@"}' \ + echo exec "`head -n 1 conf-cc`" '-c $${1+"$$@"}' \ ) > compile chmod 755 compile @@ -337,7 +345,7 @@ taia.h tai.h uint64.h taia.h dns_transmit.o: \ compile dns_transmit.c socket.h uint16.h alloc.h error.h byte.h \ uint16.h dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h \ -taia.h +taia.h clients.h ./compile dns_transmit.c dns_txt.o: \ @@ -346,10 +354,10 @@ stralloc.h iopause.h taia.h tai.h uint64.h taia.h ./compile dns_txt.c dnscache: \ -load dnscache.o droproot.o okclient.o log.o cache.o query.o \ +load dnscache.o droproot.o okclient.o log.o siphash.o cache.o query.o \ response.o dd.o roots.o iopause.o prot.o dns.a env.a alloc.a buffer.a \ libtai.a unix.a byte.a socket.lib - ./load dnscache droproot.o okclient.o log.o cache.o \ + ./load dnscache droproot.o okclient.o log.o siphash.o cache.o \ query.o response.o dd.o roots.o iopause.o prot.o dns.a \ env.a alloc.a buffer.a libtai.a unix.a byte.a `cat \ socket.lib` @@ -371,7 +379,7 @@ compile dnscache.c env.h exit.h scan.h strerr.h error.h ip4.h \ uint16.h uint64.h socket.h uint16.h dns.h stralloc.h gen_alloc.h \ iopause.h taia.h tai.h uint64.h taia.h taia.h byte.h roots.h fmt.h \ iopause.h query.h dns.h uint32.h alloc.h response.h uint32.h cache.h \ -uint32.h uint64.h ndelay.h log.h uint64.h okclient.h droproot.h +uint32.h uint64.h ndelay.h log.h uint64.h okclient.h droproot.h clients.h ./compile dnscache.c dnsfilter: \ @@ -495,7 +503,7 @@ stralloc.h alloc.h parsetype.h dd.h dns.h stralloc.h iopause.h taia.h dnstracesort: \ warn-auto.sh dnstracesort.sh conf-home cat warn-auto.sh dnstracesort.sh \ - | sed s}HOME}"`head -1 conf-home`"}g \ + | sed s}HOME}"`head -n 1 conf-home`"}g \ > dnstracesort chmod 755 dnstracesort @@ -573,8 +581,8 @@ compile hier.c auto_home.h ./compile hier.c install: \ -load install.o hier.o auto_home.o buffer.a unix.a byte.a - ./load install hier.o auto_home.o buffer.a unix.a byte.a +load install.o hier.o auto_destdir.o buffer.a unix.a byte.a + ./load install hier.o auto_destdir.o buffer.a unix.a byte.a install.o: \ compile install.c buffer.h strerr.h error.h open.h exit.h @@ -628,7 +636,7 @@ load: \ warn-auto.sh conf-ld ( cat warn-auto.sh; \ echo 'main="$$1"; shift'; \ - echo exec "`head -1 conf-ld`" \ + echo exec "`head -n 1 conf-ld`" \ '-o "$$main" "$$main".o $${1+"$$@"}' \ ) > load chmod 755 load @@ -666,7 +674,7 @@ compile ndelay_on.c ndelay.h ./compile ndelay_on.c okclient.o: \ -compile okclient.c str.h ip4.h okclient.h +compile okclient.c str.h ip4.h okclient.h env.h ./compile okclient.c open_read.o: \ @@ -816,7 +824,7 @@ openreadclose.h stralloc.h roots.h rts: \ warn-auto.sh rts.sh conf-home cat warn-auto.sh rts.sh \ - | sed s}HOME}"`head -1 conf-home`"}g \ + | sed s}HOME}"`head -n 1 conf-home`"}g \ > rts chmod 755 rts @@ -851,6 +859,10 @@ sgetopt.o: \ compile sgetopt.c buffer.h sgetopt.h subgetopt.h subgetopt.h ./compile sgetopt.c +siphash.o: \ +compile siphash.c uint32.h uint64.h siphash.h + ./compile siphash.c + socket.lib: \ trylsock.c compile load ( ( ./compile trylsock.c && \ @@ -1004,8 +1016,8 @@ compile subgetopt.c subgetopt.h systype: \ find-systype.sh conf-cc conf-ld trycpp.c x86cpuid.c ( cat warn-auto.sh; \ - echo CC=\'`head -1 conf-cc`\'; \ - echo LD=\'`head -1 conf-ld`\'; \ + echo CC=\'`head -n 1 conf-cc`\'; \ + echo LD=\'`head -n 1 conf-ld`\'; \ cat find-systype.sh; \ ) | sh > systype diff --git a/TARGETS b/TARGETS index 8e8e457..a9a901e 100644 --- a/TARGETS +++ b/TARGETS @@ -235,6 +235,7 @@ socket_recv6.o socket_send6.o haveip6.h haven2i.h +siphash.o sockaddr_in6.h scan_xlong.o socket_accept6.o diff --git a/auto_destdir.c b/auto_destdir.c new file mode 100644 index 0000000..789fa0b --- /dev/null +++ b/auto_destdir.c @@ -0,0 +1,3 @@ +const char auto_home[] = "\ +\057\150\157\155\145\057\152\154\171\157\057\144\152\142\144\156\163\057\144\145\163\164\057\165\163\162\057\154\157\143\141\154\ +"; diff --git a/axfr-get.c b/axfr-get.c index f6bf5bd..4cb4ad4 100644 --- a/axfr-get.c +++ b/axfr-get.c @@ -210,6 +210,26 @@ unsigned int doit(char *buf,unsigned int len,unsigned int pos) if (!stralloc_cats(&line,".:")) return 0; if (!stralloc_catulong0(&line,dist,0)) return 0; } + else if (byte_equal(data,2,DNS_T_SRV)) { + uint16 dist, weight, port; + if (!stralloc_copys(&line,"S")) return 0; + if (!dns_domain_todot_cat(&line,d1)) return 0; + if (!stralloc_cats(&line,"::")) return 0; + pos = x_copy(buf,len,pos,data,2); + uint16_unpack_big(data,&dist); + pos = x_copy(buf,len,pos,data,2); + uint16_unpack_big(data,&weight); + pos = x_copy(buf,len,pos,data,2); + uint16_unpack_big(data,&port); + x_getname(buf,len,pos,&d1); + if (!dns_domain_todot_cat(&line,d1)) return 0; + if (!stralloc_cats(&line,".:")) return 0; + if (!stralloc_catulong0(&line,dist,0)) return 0; + if (!stralloc_cats(&line,":")) return 0; + if (!stralloc_catulong0(&line,weight,0)) return 0; + if (!stralloc_cats(&line,":")) return 0; + if (!stralloc_catulong0(&line,port,0)) return 0; + } else if (byte_equal(data,2,DNS_T_A) && (dlen == 4)) { char ipstr[IP4_FMT]; if (!stralloc_copys(&line,"+")) return 0; @@ -218,6 +238,14 @@ unsigned int doit(char *buf,unsigned int len,unsigned int pos) x_copy(buf,len,pos,data,4); if (!stralloc_catb(&line,ipstr,ip4_fmt(ipstr,data))) return 0; } + else if (byte_equal(data,2,DNS_T_PTR)) { + if (!stralloc_copys(&line,"^")) return 0; + if (!dns_domain_todot_cat(&line,d1)) return 0; + if (!stralloc_cats(&line,":")) return 0; + x_getname(buf,len,pos,&d1); + if (!dns_domain_todot_cat(&line,d1)) return 0; + if (!stralloc_cats(&line,".")) return 0; + } else if (byte_equal(data,2,DNS_T_AAAA)) { char ipstr[IP6_FMT]; if (!stralloc_copys(&line,"3")) return 0; diff --git a/buffer_put.c b/buffer_put.c index f875f3f..d1327df 100644 --- a/buffer_put.c +++ b/buffer_put.c @@ -13,7 +13,7 @@ static int allwrite(int (*op)(),int fd,const char *buf,unsigned int len) if (errno == error_intr) continue; return -1; /* note that some data may have been written */ } - if (w == 0) ; /* luser's fault */ + if (w == 0) { }; /* luser's fault */ buf += w; len -= w; } diff --git a/cache.c b/cache.c index 6302428..4c409ed 100644 --- a/cache.c +++ b/cache.c @@ -1,12 +1,21 @@ #include "alloc.h" #include "byte.h" +#include "dns.h" #include "uint32.h" +#include "uint64.h" #include "exit.h" #include "tai.h" #include "cache.h" +#include "siphash.h" uint64 cache_motion = 0; +/* record cache stats */ +/* James Raftery 6 Nov. 2003 */ +uint64 cache_hit = 0; +uint64 cache_miss = 0; + +static unsigned char siphash_key[16]; static char *x = 0; static uint32 size; static uint32 hsize; @@ -64,17 +73,11 @@ static uint32 get4(uint32 pos) static unsigned int hash(const char *key,unsigned int keylen) { - unsigned int result = 5381; + uint64 h; + siphash24((unsigned char *) &h, + (const unsigned char *) key, keylen, siphash_key); - while (keylen) { - result = (result << 5) + result; - result ^= (unsigned char) *key; - ++key; - --keylen; - } - result <<= 2; - result &= hsize - 4; - return result; + return ((uint32) h) & (hsize - 4); } char *cache_get(const char *key,unsigned int keylen,unsigned int *datalen,uint32 *ttl) @@ -112,15 +115,20 @@ char *cache_get(const char *key,unsigned int keylen,unsigned int *datalen,uint32 if (u > size - pos - 20 - keylen) cache_impossible(); *datalen = u; + cache_hit++; return x + pos + 20 + keylen; } } nextpos = prevpos ^ get4(pos); prevpos = pos; pos = nextpos; - if (++loop > 100) return 0; /* to protect against hash flooding */ + if (++loop > 100) { /* to protect against hash flooding */ + cache_miss++; + return 0; + } } + cache_miss++; return 0; } @@ -183,6 +191,10 @@ void cache_set(const char *key,unsigned int keylen,const char *data,unsigned int int cache_init(unsigned int cachesize) { + unsigned int i = 0U; + do { + siphash_key[i] = (unsigned char) dns_random(0x100); + } while (++i < sizeof siphash_key); if (x) { alloc_free(x); x = 0; diff --git a/cache.h b/cache.h index f5306c5..0f45e7d 100644 --- a/cache.h +++ b/cache.h @@ -5,6 +5,12 @@ #include "uint64.h" extern uint64 cache_motion; + +/* record cache stats */ +/* James Raftery 6 Nov. 2003 */ +extern uint64 cache_hit; +extern uint64 cache_miss; + extern int cache_init(unsigned int); extern void cache_set(const char *,unsigned int,const char *,unsigned int,uint32); extern char *cache_get(const char *,unsigned int,unsigned int *,uint32 *); diff --git a/clients.h b/clients.h new file mode 100644 index 0000000..983a4ad --- /dev/null +++ b/clients.h @@ -0,0 +1,7 @@ +#ifndef CLIENTS_H +#define CLIENTS_H + +#define MAXUDP 200 +#define MAXTCP 20 + +#endif /* CLIENTS_H */ diff --git a/conf-cc b/conf-cc index b315ecb..d224474 100644 --- a/conf-cc +++ b/conf-cc @@ -1,3 +1,3 @@ -gcc -O2 -Wimplicit -Wunused -Wcomment -Wchar-subscripts -Wuninitialized -Wshadow -Wcast-qual -Wcast-align -Wwrite-strings +gcc -O2 -Wall -Wextra -Wno-parentheses -Wno-misleading-indentation -Wno-pointer-sign -Wno-old-style-declaration -Wno-unused-parameter -Wno-sign-compare This will be used to compile .c files. diff --git a/conf-destdir b/conf-destdir new file mode 100644 index 0000000..e69de29 diff --git a/dns.h b/dns.h index 5398e2b..45456ae 100644 --- a/dns.h +++ b/dns.h @@ -4,6 +4,7 @@ #include "stralloc.h" #include "iopause.h" #include "taia.h" +#include "clients.h" #define DNS_C_IN "\0\1" #define DNS_C_ANY "\0\377" @@ -20,6 +21,7 @@ #define DNS_T_SIG "\0\30" #define DNS_T_KEY "\0\31" #define DNS_T_AAAA "\0\34" +#define DNS_T_SRV "\0\41" #define DNS_T_AXFR "\0\374" #define DNS_T_ANY "\0\377" @@ -38,8 +40,14 @@ struct dns_transmit { char localip[16]; unsigned int scope_id; char qtype[2]; + struct dns_transmit *master; + struct dns_transmit *slaves[MAXUDP]; + int nslaves; } ; +extern void dns_enable_merge(void (*logger)(const char *, const char *, + const char *)); + extern void dns_random_init(const char *); extern unsigned int dns_random(unsigned int); @@ -88,10 +96,7 @@ extern int dns_ip4_qualify(stralloc *,stralloc *,const stralloc *); extern int dns_ip6_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *); extern int dns_ip6_qualify(stralloc *,stralloc *,const stralloc *); -#define DNS_IP6_INT 0 -#define DNS_IP6_ARPA 1 - -extern int dns_name6_domain(char *,const char *,int); +extern int dns_name6_domain(char *,const char *); #define DNS_NAME6_DOMAIN (4*16+11) #endif diff --git a/dns_name.c b/dns_name.c index 518a0c0..07b9b44 100644 --- a/dns_name.c +++ b/dns_name.c @@ -48,23 +48,16 @@ int dns_name4(stralloc *out,const char ip[4]) return 0; } -int dns_name6_inner(stralloc *out,const char ip[16],int t) +int dns_name6(stralloc *out,const char ip[16]) { char name[DNS_NAME6_DOMAIN]; - dns_name6_domain(name,ip,t); + if (ip6_isv4mapped(ip)) + return dns_name4(out,ip+12); + dns_name6_domain(name,ip); if (dns_resolve(name,DNS_T_PTR) == -1) return -1; if (dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1; dns_transmit_free(&dns_resolve_tx); dns_domain_free(&q); return 0; } - -int dns_name6(stralloc *out,const char ip[16]) -{ - if (ip6_isv4mapped(ip)) - return dns_name4(out,ip+12); - if (dns_name6_inner(out,ip,DNS_IP6_ARPA)) return -1; - if (!out->len) return dns_name6_inner(out,ip,DNS_IP6_INT); - return 0; -} diff --git a/dns_nd6.c b/dns_nd6.c index 6dbeb89..0248840 100644 --- a/dns_nd6.c +++ b/dns_nd6.c @@ -15,7 +15,7 @@ unsigned int mkint(unsigned char a,unsigned char b) { return ((unsigned int)a << 8) + (unsigned int)b; } -int dns_name6_domain(char name[DNS_NAME6_DOMAIN],const char ip[16],int t) +int dns_name6_domain(char name[DNS_NAME6_DOMAIN],const char ip[16]) { unsigned int j; @@ -25,11 +25,6 @@ int dns_name6_domain(char name[DNS_NAME6_DOMAIN],const char ip[16],int t) name[j*4+2]=1; name[j*4+3]=tohex((unsigned char)ip[15-j] >> 4); } - if (t==DNS_IP6_INT) - byte_copy(name + 4*16,9,"\3ip6\3int\0"); - else if (t==DNS_IP6_ARPA) - byte_copy(name + 4*16,10,"\3ip6\4arpa\0"); - else return 0; - return 4*16+9+t; + byte_copy(name + 4*16,10,"\3ip6\4arpa\0"); + return 4*16+10; } - diff --git a/dns_rcip.c b/dns_rcip.c index efd1b21..9923183 100644 --- a/dns_rcip.c +++ b/dns_rcip.c @@ -8,7 +8,7 @@ static stralloc data = {0}; -static int init(char ip[256]) +static int init(char ip[512]) { int i; int j; @@ -54,16 +54,16 @@ static int init(char ip[256]) byte_copy(ip,16,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1"); iplen = 16; } - byte_zero(ip + iplen,256 - iplen); + byte_zero(ip + iplen,512 - iplen); return 0; } static int ok = 0; static unsigned int uses; static struct taia deadline; -static char ip[256]; /* defined if ok */ +static char ip[512]; /* defined if ok */ -int dns_resolvconfip(char s[256]) +int dns_resolvconfip(char s[512]) { struct taia now; @@ -80,6 +80,6 @@ int dns_resolvconfip(char s[256]) } --uses; - byte_copy(s,256,ip); + byte_copy(s,512,ip); return 0; } diff --git a/dns_resolve.c b/dns_resolve.c index 82b5bbb..b588784 100644 --- a/dns_resolve.c +++ b/dns_resolve.c @@ -10,7 +10,7 @@ int dns_resolve(const char *q,const char qtype[2]) { struct taia stamp; struct taia deadline; - char servers[256]; + char servers[512]; iopause_fd x[1]; int r; diff --git a/dns_transmit.c b/dns_transmit.c index cba1fd2..1d46760 100644 --- a/dns_transmit.c +++ b/dns_transmit.c @@ -8,6 +8,62 @@ #include "uint16.h" #include "dns.h" #include "ip6.h" +#include "strerr.h" + +static int merge_enable; +static void (*merge_logger)(const char *, const char *, const char *); +void dns_enable_merge(void (*f)(const char *, const char *, const char *)) +{ + merge_enable = 1; + merge_logger = f; +} + +static int merge_equal(struct dns_transmit *a, struct dns_transmit *b) +{ + const char *ip1 = a->servers + 16 * a->curserver; + const char *ip2 = b->servers + 16 * b->curserver; + return + byte_equal(ip1, 16, ip2) && + byte_equal(a->qtype, 2, b->qtype) && + dns_domain_equal(a->query + 14, b->query + 14); +} + +struct dns_transmit *inprogress[MAXUDP]; + +static int try_merge(struct dns_transmit *d) +{ + int i; + for (i = 0; i < MAXUDP; i++) { + if (!inprogress[i]) continue; + if (!merge_equal(d, inprogress[i])) continue; + if (inprogress[i]->nslaves == MAXUDP) continue; + d->master = inprogress[i]; + inprogress[i]->slaves[inprogress[i]->nslaves++] = d; + return 1; + } + return 0; +} + +static void register_inprogress(struct dns_transmit *d) +{ + int i; + for (i = 0; i < MAXUDP; i++) { + if (!inprogress[i]) { + inprogress[i] = d; + return; + } + } + strerr_die1x(100, "BUG: out of inprogress slots"); +} + +static void unregister_inprogress(struct dns_transmit *d) +{ + int i; + for (i = 0; i < MAXUDP; i++) { + if (inprogress[i] == d) + inprogress[i] = 0; + } +} static int serverwantstcp(const char *buf,unsigned int len) { @@ -60,8 +116,31 @@ static void packetfree(struct dns_transmit *d) d->packet = 0; } +static void mergefree(struct dns_transmit *d) +{ + int i; + if (merge_enable) + unregister_inprogress(d); + /* unregister us from our master */ + if (d->master) { + for (i = 0; i < d->master->nslaves; i++) + if (d->master->slaves[i] == d) + d->master->slaves[i] = 0; + d->master = 0; + } + /* and unregister all of our slaves from us */ + for (i = 0; i < d->nslaves; i++) { + if (d->slaves[i]) { + d->slaves[i]->master = NULL; + d->slaves[i] = 0; + } + } + d->nslaves = 0; +} + static void queryfree(struct dns_transmit *d) { + mergefree(d); if (!d->query) return; alloc_free(d->query); d->query = 0; @@ -100,11 +179,18 @@ static int thisudp(struct dns_transmit *d) const char *ip; socketfree(d); + mergefree(d); while (d->udploop < 4) { - for (;d->curserver < 16;++d->curserver) { + for (;d->curserver < 32;++d->curserver) { ip = d->servers + 16 * d->curserver; if (byte_diff(ip,16,V6any)) { + if (merge_enable && try_merge(d)) { + if (merge_logger) + merge_logger(ip, d->qtype, d->query + 14); + return 0; + } + d->query[2] = dns_random(256); d->query[3] = dns_random(256); @@ -119,6 +205,8 @@ static int thisudp(struct dns_transmit *d) taia_uint(&d->deadline,timeouts[d->udploop]); taia_add(&d->deadline,&d->deadline,&now); d->tcpstate = 0; + if (merge_enable) + register_inprogress(d); return 0; } @@ -153,7 +241,7 @@ static int thistcp(struct dns_transmit *d) socketfree(d); packetfree(d); - for (;d->curserver < 16;++d->curserver) { + for (;d->curserver < 32;++d->curserver) { ip = d->servers + 16 * d->curserver; if (byte_diff(ip,16,V6any)) { d->query[2] = dns_random(256); @@ -167,6 +255,7 @@ static int thistcp(struct dns_transmit *d) taia_uint(&d->deadline,10); taia_add(&d->deadline,&d->deadline,&now); if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0) { + d->pos = 0; d->tcpstate = 2; return 0; } @@ -194,7 +283,7 @@ static int nexttcp(struct dns_transmit *d) return thistcp(d); } -int dns_transmit_start(struct dns_transmit *d,const char servers[256],int flagrecursive,const char *q,const char qtype[2],const char localip[16]) +int dns_transmit_start(struct dns_transmit *d,const char servers[512],int flagrecursive,const char *q,const char qtype[2],const char localip[16]) { unsigned int len; @@ -218,7 +307,7 @@ int dns_transmit_start(struct dns_transmit *d,const char servers[256],int flagre d->udploop = flagrecursive ? 1 : 0; - if (len + 16 > 512) return firsttcp(d); + if ((len + 16 > 512) || byte_equal(qtype,2,DNS_T_ANY)) return firsttcp(d); return firstudp(d); } @@ -227,8 +316,12 @@ void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline) x->fd = d->s1 - 1; switch(d->tcpstate) { - case 0: case 3: case 4: case 5: - x->events = IOPAUSE_READ; + case 0: + if (d->master) return; + if (d->packet) { taia_now(deadline); return; } + /* otherwise, fall through */ + case 3: case 4: case 5: + x->events = IOPAUSE_READ; break; case 1: case 2: x->events = IOPAUSE_WRITE; @@ -245,10 +338,14 @@ int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct tai unsigned char ch; int r; int fd; + int i; errno = error_io; fd = d->s1 - 1; + if (d->tcpstate == 0 && d->master) return 0; + if (d->tcpstate == 0 && d->packet) return 1; + if (!x->revents) { if (taia_less(when,&d->deadline)) return 0; errno = error_timeout; @@ -280,6 +377,15 @@ have sent query to curserver on UDP socket s d->packet = alloc(d->packetlen); if (!d->packet) { dns_transmit_free(d); return -1; } byte_copy(d->packet,d->packetlen,udpbuf); + + for (i = 0; i < d->nslaves; i++) { + if (!d->slaves[i]) continue; + d->slaves[i]->packetlen = d->packetlen; + d->slaves[i]->packet = alloc(d->packetlen); + if (!d->slaves[i]->packet) { dns_transmit_free(d->slaves[i]); continue; } + byte_copy(d->slaves[i]->packet,d->packetlen,udpbuf); + } + queryfree(d); return 1; } diff --git a/dnscache.c b/dnscache.c index ebf8b20..1ba45a7 100644 --- a/dnscache.c +++ b/dnscache.c @@ -1,11 +1,12 @@ #include +#include #include "env.h" #include "exit.h" #include "scan.h" #include "strerr.h" #include "error.h" -#include "ip4.h" #include "ip6.h" +#include "str.h" #include "uint16.h" #include "uint64.h" #include "socket.h" @@ -53,19 +54,27 @@ static int packetquery(char *buf,unsigned int len,char **q,char qtype[2],char qc static char myipoutgoing[16]; -static char myipincoming[16]; -static char buf[1024]; +static char buf[65535]; uint64 numqueries = 0; +struct interf { + char ip[16]; + int udp53; + int tcp53; + iopause_fd *udp53io; + iopause_fd *tcp53io; + + struct interf *next; +}; -static int udp53; +struct interf *interhead = 0; -#define MAXUDP 200 static struct udpclient { struct query q; struct taia start; uint64 active; /* query number, if active; otherwise 0 */ iopause_fd *io; + int fd; char ip[16]; uint16 port; char id[2]; @@ -73,11 +82,15 @@ static struct udpclient { } u[MAXUDP]; int uactive = 0; +#define MAXSOA 20 +int soaactive = 0; + void u_drop(int j) { if (!u[j].active) return; log_querydrop(&u[j].active); u[j].active = 0; --uactive; + if (byte_equal(u[j].q.type,2,DNS_T_SOA)) --soaactive; } void u_respond(int j) @@ -85,12 +98,13 @@ void u_respond(int j) if (!u[j].active) return; response_id(u[j].id); if (response_len > 512) response_tc(); - socket_send6(udp53,response,response_len,u[j].ip,u[j].port,u[j].scope_id); + socket_send6(u[j].fd,response,response_len,u[j].ip,u[j].port,u[j].scope_id); log_querydone(&u[j].active,response_len); u[j].active = 0; --uactive; + if (byte_equal(u[j].q.type,2,DNS_T_SOA)) --soaactive; } -void u_new(void) +void u_new(int fd) { int j; int i; @@ -115,8 +129,9 @@ void u_new(void) x = u + j; taia_now(&x->start); + x->fd=fd; - len = socket_recv6(udp53,buf,sizeof buf,x->ip,&x->port,&x->scope_id); + len = socket_recv6(x->fd,buf,sizeof buf,x->ip,&x->port,&x->scope_id); if (len == -1) return; if (len >= sizeof buf) return; if (x->port < 1024) if (x->port != 53) return; @@ -126,6 +141,16 @@ void u_new(void) x->active = ++numqueries; ++uactive; log_query(&x->active,x->ip,x->port,x->id,q,qtype); + + if (byte_equal(qtype,2,DNS_T_SOA)) { + if (soaactive >= MAXSOA) { + log_querydropmaxsoa(&x->active); + x->active = 0; --uactive; + return; + } + ++soaactive; + } + switch(query_start(&x->q,q,qtype,qclass,myipoutgoing,interface)) { case -1: u_drop(j); @@ -135,15 +160,13 @@ void u_new(void) } } -static int tcp53; - -#define MAXTCP 20 struct tcpclient { struct query q; struct taia start; struct taia timeout; uint64 active; /* query number or 1, if active; otherwise 0 */ iopause_fd *io; + int fd; char ip[16]; /* send response to this address */ uint16 port; /* send response to this port */ char id[2]; @@ -214,8 +237,9 @@ void t_respond(int j) void t_rw(int j) { struct tcpclient *x; - char ch; + char *ch; static char *q = 0; + unsigned int toread; char qtype[2]; char qclass[2]; int r; @@ -231,20 +255,25 @@ void t_rw(int j) } return; } - - r = read(x->tcp,&ch,1); + switch (x->state) { + case 1: toread = 2U; break; + case 2: toread = 1U; break; + case 3: toread = x->len - x->pos; break; + default: return; /* impossible */ + } + r = read(x->tcp, buf, toread); if (r == 0) { errno = error_pipe; t_close(j); return; } if (r < 0) { t_close(j); return; } - + ch = buf; if (x->state == 1) { - x->len = (unsigned char) ch; + x->len = (unsigned char) *ch++; x->len <<= 8; x->state = 2; - return; + if (--r <= 0) return; } if (x->state == 2) { - x->len += (unsigned char) ch; - if (!x->len) { errno = error_proto; t_close(j); return; } + x->len += (unsigned char) *ch; + if (x->len < 12) { errno = error_proto; t_close(j); return; } x->buf = alloc(x->len); if (!x->buf) { t_close(j); return; } x->pos = 0; @@ -254,7 +283,8 @@ void t_rw(int j) if (x->state != 3) return; /* impossible */ - x->buf[x->pos++] = ch; + byte_copy(&x->buf[x->pos], r, ch); + x->pos += r; if (x->pos < x->len) return; if (!packetquery(x->buf,x->len,&q,qtype,qclass,x->id)) { t_close(j); return; } @@ -273,7 +303,7 @@ void t_rw(int j) x->state = 0; } -void t_new(void) +void t_new(int fd) { int i; int j; @@ -297,8 +327,9 @@ void t_new(void) x = t + j; taia_now(&x->start); + x->fd=fd; - x->tcp = socket_accept6(tcp53,x->ip,&x->port,&x->scope_id); + x->tcp = socket_accept6(x->fd,x->ip,&x->port,&x->scope_id); if (x->tcp == -1) return; if (x->port < 1024) if (x->port != 53) { close(x->tcp); return; } if (!okclient(x->ip)) { close(x->tcp); return; } @@ -311,19 +342,24 @@ void t_new(void) log_tcpopen(x->ip,x->port); } +#define FATAL "dnscache: fatal: " -iopause_fd io[3 + MAXUDP + MAXTCP]; -iopause_fd *udp53io; -iopause_fd *tcp53io; +iopause_fd *io = 0; +int numio; static void doit(void) { int j; struct taia deadline; struct taia stamp; + struct interf *inter; int iolen; int r; + io = (iopause_fd *) alloc((numio + 1 + MAXUDP + MAXTCP) * sizeof(iopause_fd)); + if (!io) + strerr_die2sys(111,FATAL,"unable to alloc io: "); + for (;;) { taia_now(&stamp); taia_uint(&deadline,120); @@ -331,13 +367,15 @@ static void doit(void) iolen = 0; - udp53io = io + iolen++; - udp53io->fd = udp53; - udp53io->events = IOPAUSE_READ; + for (inter = interhead; inter != 0; inter = inter->next) { + inter->udp53io = io + iolen++; + inter->udp53io->fd = inter->udp53; + inter->udp53io->events = IOPAUSE_READ; - tcp53io = io + iolen++; - tcp53io->fd = tcp53; - tcp53io->events = IOPAUSE_READ; + inter->tcp53io = io + iolen++; + inter->tcp53io->fd = inter->tcp53; + inter->tcp53io->events = IOPAUSE_READ; + } for (j = 0;j < MAXUDP;++j) if (u[j].active) { @@ -379,23 +417,30 @@ static void doit(void) t_rw(j); } - if (udp53io) - if (udp53io->revents) - u_new(); - - if (tcp53io) - if (tcp53io->revents) - t_new(); - } + for (inter = interhead; inter != 0; inter = inter->next) { + if (inter->udp53io) + if (inter->udp53io->revents) + u_new(inter->udp53); + + if (inter->tcp53io) + if (inter->tcp53io->revents) + t_new(inter->tcp53); + } + } } -#define FATAL "dnscache: fatal: " - char seed[128]; int main() { char *x; + int len; + int pos; + int oldpos; + char iptmp[16]; + char iperr[IP6_FMT]; + struct interf *inter; + struct interf *itmp; unsigned int i, j, k; unsigned long cachesize; static stralloc sa = {0}; @@ -403,34 +448,54 @@ int main() x = env_get("INTERFACE"); if (x) scan_ulong(x,&interface); + signal(SIGPIPE, SIG_IGN); x = env_get("IP"); if (!x) strerr_die2x(111,FATAL,"$IP not set"); - if (!ip6_scan(x,myipincoming)) - strerr_die3x(111,FATAL,"unable to parse IP address ",x); + len = str_len(x); + numio = pos = oldpos = 0; + while (pos < len) { + if (pos) oldpos = pos + 1; + pos = oldpos + str_chr(x + oldpos,','); + x[pos] = 0; + if (!str_len(x + oldpos)) continue; + + if (!ip6_scan(x + oldpos,iptmp)) + strerr_die3x(111,FATAL,"unable to parse IP address ",x + oldpos); + + inter = (struct interf *) alloc(sizeof(struct interf)); + + if (interhead == 0) interhead = inter; + else if (interhead->next == 0) interhead->next = inter; + else { + for (itmp = interhead; itmp->next != 0; itmp = itmp->next); + itmp->next = inter; + } -#if 0 - /* if if IP is a mapped-IPv4 address, disable IPv6 functionality */ - /* this is actually a bad idea */ - if (ip6_isv4mapped(myipincoming)) - noipv6 = 1; -#endif - - udp53 = socket_udp6(); - if (udp53 == -1) - strerr_die2sys(111,FATAL,"unable to create UDP socket: "); - if (socket_bind6_reuse(udp53,myipincoming,53,interface) == -1) - strerr_die2sys(111,FATAL,"unable to bind UDP socket: "); - - tcp53 = socket_tcp6(); - if (tcp53 == -1) - strerr_die2sys(111,FATAL,"unable to create TCP socket: "); - if (socket_bind6_reuse(tcp53,myipincoming,53,interface) == -1) - strerr_die2sys(111,FATAL,"unable to bind TCP socket: "); + inter->next = 0; + inter->udp53 = socket_udp6(); + if (inter->udp53 == -1) + strerr_die4sys(111,FATAL,"unable to create UDP socket for IP address ",x + oldpos,": "); + if (socket_bind6_reuse(inter->udp53,iptmp,53,interface) == -1) + strerr_die4sys(111,FATAL,"unable to bind UDP socket for IP address ",x + oldpos,": "); + + inter->tcp53 = socket_tcp6(); + if (inter->tcp53 == -1) + strerr_die4sys(111,FATAL,"unable to create TCP socket for IP address ",x + oldpos,": "); + if (socket_bind6_reuse(inter->tcp53,iptmp,53,interface) == -1) + strerr_die4sys(111,FATAL,"unable to bind TCP socket for IP address ",x + oldpos,": "); + + numio++; + log_listen(iptmp); + } + + if (interhead == 0) + strerr_die2x(111,FATAL,"no interfaces to listen on"); droproot(FATAL); - socket_tryreservein(udp53,131072); + for (inter = interhead; inter != 0; inter = inter->next) + socket_tryreservein(inter->udp53,131072); byte_zero(seed,sizeof seed); read(0,seed,sizeof seed); @@ -468,12 +533,17 @@ int main() response_hidettl(); if (env_get("FORWARDONLY")) query_forwardonly(); + if (env_get("MERGEQUERIES")) + dns_enable_merge(log_merge); if (!roots_init()) strerr_die2sys(111,FATAL,"unable to read servers: "); - if (socket_listen(tcp53,20) == -1) - strerr_die2sys(111,FATAL,"unable to listen on TCP socket: "); + for (inter = interhead; inter != 0; inter = inter->next) + if (socket_listen(inter->tcp53,20) == -1) { + iperr[ip6_fmt(iperr,inter->ip)] = 0; + strerr_die4sys(111,FATAL,"unable to listen on TCP socket for IP ",iperr,": "); + } log_startup(); doit(); diff --git a/dnsfilter.c b/dnsfilter.c index 822ff1e..e2b303c 100644 --- a/dnsfilter.c +++ b/dnsfilter.c @@ -45,7 +45,7 @@ int flag0 = 1; iopause_fd *io; int iolen; -char servers[256]; +char servers[512]; char ip[4]; char name[DNS_NAME4_DOMAIN]; diff --git a/dnsq.c b/dnsq.c index e00bee2..1f0c654 100644 --- a/dnsq.c +++ b/dnsq.c @@ -26,7 +26,7 @@ void oops(void) static struct dns_transmit tx; -int resolve(char *q,char qtype[2],char servers[256]) +int resolve(char *q,char qtype[2],char servers[512]) { struct taia stamp; struct taia deadline; @@ -49,7 +49,7 @@ int resolve(char *q,char qtype[2],char servers[256]) return 0; } -char servers[256]; +char servers[512]; static stralloc ip; static stralloc fqdn; @@ -76,8 +76,8 @@ int main(int argc,char **argv) if (!*++argv) usage(); if (!stralloc_copys(&out,*argv)) oops(); if (dns_ip6_qualify(&ip,&fqdn,&out) == -1) oops(); - if (ip.len >= 256) ip.len = 256; - byte_zero(servers,256); + if (ip.len >= 512) ip.len = 512; + byte_zero(servers,512); byte_copy(servers,ip.len,ip.s); if (!stralloc_copys(&out,"")) oops(); diff --git a/dnsroots.global b/dnsroots.global index 3b567e1..9fb41d0 100644 --- a/dnsroots.global +++ b/dnsroots.global @@ -1,13 +1,13 @@ 198.41.0.4 -128.9.0.107 +199.9.14.201 192.33.4.12 -128.8.10.90 +199.7.91.13 192.203.230.10 192.5.5.241 192.112.36.4 -128.63.2.53 +198.97.190.53 192.36.148.17 -198.41.0.10 +192.58.128.30 193.0.14.129 -198.32.64.12 +199.7.83.42 202.12.27.33 diff --git a/dnstrace.c b/dnstrace.c index 07fe1e9..5f1795e 100644 --- a/dnstrace.c +++ b/dnstrace.c @@ -48,13 +48,13 @@ int resolve(char *q,char qtype[2],char ip[16]) struct taia start; struct taia stamp; struct taia deadline; - char servers[256]; + char servers[512]; iopause_fd x[1]; int r; taia_now(&start); - byte_zero(servers,256); + byte_zero(servers,512); byte_copy(servers,16,ip); if (dns_transmit_start(&tx,servers,0,q,qtype,V6any) == -1) return -1; diff --git a/dnstracesort.sh b/dnstracesort.sh index e57359c..108ef2f 100644 --- a/dnstracesort.sh +++ b/dnstracesort.sh @@ -12,7 +12,7 @@ awk -F: ' } print } -' | sort -t: +0 -2 +4 +3 -4 +2 -3 | uniq | awk -F: ' +' | sort -t: -k 1,3 -k 5 -k 4,5 -k 3,4 | uniq | awk -F: ' { type = $1 q = $2 diff --git a/hier.c b/hier.c index b7dd760..9cb9717 100644 --- a/hier.c +++ b/hier.c @@ -6,8 +6,6 @@ extern void c(const char* home,const char* subdir,const char* file,int uid,int g void hier() { - c("/","etc","dnsroots.global",-1,-1,0644); - h(auto_home,-1,-1,02755); d(auto_home,"bin",-1,-1,02755); diff --git a/log.c b/log.c index df465e2..1b87211 100644 --- a/log.c +++ b/log.c @@ -118,6 +118,12 @@ void log_querydrop(uint64 *qnum) line(); } +void log_querydropmaxsoa(uint64 *qnum) +{ + string("drop "); number(*qnum); space(); string("maxsoa"); + line(); +} + void log_tcpopen(const char client[16],unsigned int port) { string("tcpopen "); @@ -134,14 +140,14 @@ void log_tcpclose(const char client[16],unsigned int port) line(); } -void log_tx(const char *q,const char qtype[2],const char *control,const char servers[256],unsigned int gluelessness) +void log_tx(const char *q,const char qtype[2],const char *control,const char servers[512],unsigned int gluelessness) { int i; string("tx "); number(gluelessness); space(); logtype(qtype); space(); name(q); space(); name(control); - for (i = 0;i < 256;i += 16) + for (i = 0;i < 512;i += 16) if (byte_diff(servers + i,16,V6any)) { space(); ip(servers + i); @@ -149,6 +155,12 @@ void log_tx(const char *q,const char qtype[2],const char *control,const char ser line(); } +void log_merge(const char *addr, const char qtype[2], const char *q) +{ + string("merge "); ip(addr); space(); logtype(qtype); space(); name(q); + line(); +} + void log_cachedanswer(const char *q,const char type[2]) { string("cached "); logtype(type); space(); @@ -195,6 +207,13 @@ void log_lame(const char server[16],const char *control,const char *referral) line(); } +void log_ignore_referral(const char server[16],const char * control, const char *referral) +{ + string("ignored referral "); ip(server); space(); + name(control); space(); name(referral); + line(); +} + void log_servfail(const char *dn) { const char *x = error_str(errno); @@ -275,6 +294,12 @@ void log_stats(void) { extern uint64 numqueries; extern uint64 cache_motion; + + /* record cache stats */ + /* James Raftery 6 Nov. 2003 */ + extern uint64 cache_hit; + extern uint64 cache_miss; + extern int uactive; extern int tactive; @@ -282,6 +307,15 @@ void log_stats(void) number(numqueries); space(); number(cache_motion); space(); number(uactive); space(); - number(tactive); + number(tactive); space(); + number(cache_hit); space(); + number(cache_miss); + line(); +} + +void log_listen(const char addr[16]) +{ + string("listening on "); + ip(addr); line(); } diff --git a/log.h b/log.h index fe62fa3..67463a7 100644 --- a/log.h +++ b/log.h @@ -4,9 +4,11 @@ #include "uint64.h" extern void log_startup(void); +extern void log_listen(const char *); extern void log_query(uint64 *,const char *,unsigned int,const char *,const char *,const char *); extern void log_querydrop(uint64 *); +extern void log_querydropmaxsoa(uint64 *) ; extern void log_querydone(uint64 *,unsigned int); extern void log_tcpopen(const char *,unsigned int); @@ -18,11 +20,13 @@ extern void log_cachednxdomain(const char *); extern void log_cachedns(const char *,const char *); extern void log_tx(const char *,const char *,const char *,const char *,unsigned int); +extern void log_merge(const char *, const char *, const char *); extern void log_nxdomain(const char *,const char *,unsigned int); extern void log_nodata(const char *,const char *,const char *,unsigned int); extern void log_servfail(const char *); extern void log_lame(const char *,const char *,const char *); +extern void log_ignore_referral(const char *,const char *,const char *); extern void log_rr(const char *,const char *,const char *,const char *,unsigned int,unsigned int); extern void log_rrns(const char *,const char *,const char *,unsigned int); diff --git a/okclient.c b/okclient.c index 9a0d3c6..9368846 100644 --- a/okclient.c +++ b/okclient.c @@ -5,15 +5,26 @@ #include "ip6.h" #include "byte.h" #include "okclient.h" +#include "env.h" static char fn[3 + IP6_FMT]; int okclient(char ip[16]) { + static int init_done = 0; struct stat st; int i; char sep; + if (!init_done) { + if (env_get("OKCLIENT")) + init_done = 1; + else + init_done = 2; + } + if (init_done == 1) + return 1; + fn[0] = 'i'; fn[1] = 'p'; fn[2] = '/'; diff --git a/pickdns-data.c b/pickdns-data.c index 60cabb0..d013c55 100644 --- a/pickdns-data.c +++ b/pickdns-data.c @@ -123,7 +123,7 @@ void syntaxerror(const char *why) } void die_datatmp(void) { - strerr_die2sys(111,FATAL,"unable to create data.tmp: "); + strerr_die2sys(111,FATAL,"unable to create data.cdb.tmp: "); } int main() @@ -142,7 +142,7 @@ int main() if (fd == -1) strerr_die2sys(111,FATAL,"unable to open data: "); buffer_init(&b,buffer_unixread,fd,bspace,sizeof bspace); - fdcdb = open_trunc("data.tmp"); + fdcdb = open_trunc("data.cdb.tmp"); if (fdcdb == -1) die_datatmp(); if (cdb_make_start(&cdb,fdcdb) == -1) die_datatmp(); @@ -223,8 +223,8 @@ int main() if (cdb_make_finish(&cdb) == -1) die_datatmp(); if (fsync(fdcdb) == -1) die_datatmp(); if (close(fdcdb) == -1) die_datatmp(); /* NFS stupidity */ - if (rename("data.tmp","data.cdb") == -1) - strerr_die2sys(111,FATAL,"unable to move data.tmp to data.cdb: "); + if (rename("data.cdb.tmp","data.cdb") == -1) + strerr_die2sys(111,FATAL,"unable to move data.cdb.tmp to data.cdb: "); _exit(0); } diff --git a/query.c b/query.c index 993dc79..6de180d 100644 --- a/query.c +++ b/query.c @@ -94,6 +94,21 @@ static void cleanup(struct query *z) } } +static int move_name_to_alias(struct query *z,uint32 ttl) +{ + int j ; + + if (z->alias[QUERY_MAXALIAS - 1]) return 0 ; + for (j = QUERY_MAXALIAS - 1;j > 0;--j) + z->alias[j] = z->alias[j - 1]; + for (j = QUERY_MAXALIAS - 1;j > 0;--j) + z->aliasttl[j] = z->aliasttl[j - 1]; + z->alias[0] = z->name[0]; + z->aliasttl[0] = ttl; + z->name[0] = 0; + return 1 ; +} + static int rqa(struct query *z) { int i; @@ -126,7 +141,6 @@ static int globalip(char *d,char ip[16]) static char *t1 = 0; static char *t2 = 0; static char *t3 = 0; -static char *cname = 0; static char *referral = 0; static unsigned int *records = 0; @@ -181,15 +195,14 @@ static int doit(struct query *z,int state) uint16 datalen; char *control; char *d; + char *owner_name = 0 ; const char *dtype; unsigned int dlen; int flagout; - int flagcname; int flagreferral; int flagsoa; uint32 ttl; uint32 soattl; - uint32 cnamettl; int i; int j; int k; @@ -206,14 +219,14 @@ static int doit(struct query *z,int state) NEWNAME: - if (++z->loop == 100) goto DIE; + if (++z->loop == 250) goto DIE; d = z->name[z->level]; dtype = z->level ? (z->ipv6[z->level] ? DNS_T_AAAA : DNS_T_A) : z->type; dlen = dns_domain_length(d); if (globalip(d,misc)) { if (z->level) { - for (k = 0;k < 256;k += 16) + for (k = 0;k < 512;k += 16) if (byte_equal(z->servers[z->level - 1] + k,16,V6any)) { byte_copy(z->servers[z->level - 1] + k,12,V4mappedprefix); byte_copy(z->servers[z->level - 1] + k + 12,4,misc); @@ -226,86 +239,53 @@ static int doit(struct query *z,int state) if (!response_rstart(d,DNS_T_A,655360)) goto DIE; if (!response_addbytes(misc,4)) goto DIE; response_rfinish(RESPONSE_ANSWER); - } - cleanup(z); - return 1; - } - - if (dns_domain_equal(d,"\0011\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\003ip6\003int\0")) { - if (z->level) goto LOWERLEVEL; - if (!rqa(z)) goto DIE; - if (typematch(DNS_T_PTR,dtype)) { - if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE; - if (!response_addname("\016ipv6-localhost\0")) goto DIE; - if (!response_addname("\015ipv6-loopback\0")) goto DIE; - response_rfinish(RESPONSE_ANSWER); - } - cleanup(z); - return 1; - } - - if (dns_domain_equal(d,"\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\001e\001f\003ip6\003int\0")) { - if (z->level) goto LOWERLEVEL; - if (!rqa(z)) goto DIE; - if (typematch(DNS_T_PTR,dtype)) { - if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE; - if (!response_addname("\015ipv6-localnet\0")) goto DIE; - response_rfinish(RESPONSE_ANSWER); - } - cleanup(z); - return 1; - } - - if (dns_domain_equal(d,"\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\001f\001f\003ip6\003int\0")) { - if (z->level) goto LOWERLEVEL; - if (!rqa(z)) goto DIE; - if (typematch(DNS_T_PTR,dtype)) { - if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE; - if (!response_addname("\020ipv6-mcastprefix\0")) goto DIE; + } else if (typematch(DNS_T_AAAA,dtype)) { + if (!response_rstart(d,DNS_T_AAAA,655360)) goto DIE; + if (!response_addbytes("\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001",16)) goto DIE; response_rfinish(RESPONSE_ANSWER); } cleanup(z); return 1; } - if (dns_domain_equal(d,"\0011\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0012\0010\001f\001f\003ip6\003int\0")) { + if (dns_domain_equal(d,"\0011\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\003ip6\004arpa\0")) { if (z->level) goto LOWERLEVEL; if (!rqa(z)) goto DIE; if (typematch(DNS_T_PTR,dtype)) { if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE; - if (!response_addname("\015ipv6-allnodes\0")) goto DIE; + if (!response_addname("\011localhost\0")) goto DIE; response_rfinish(RESPONSE_ANSWER); } cleanup(z); return 1; } - if (dns_domain_equal(d,"\0012\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0012\0010\001f\001f\003ip6\003int\0")) { + if (dns_domain_equal(d,"\0011\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0012\0010\001f\001f\003ip6\004arpa\0")) { if (z->level) goto LOWERLEVEL; if (!rqa(z)) goto DIE; if (typematch(DNS_T_PTR,dtype)) { if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE; - if (!response_addname("\017ipv6-allrouters\0")) goto DIE; + if (!response_addname("\014ip6-allnodes\0")) goto DIE; response_rfinish(RESPONSE_ANSWER); } cleanup(z); return 1; } - if (dns_domain_equal(d,"\0011\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0012\0010\001f\001f\003ip6\003int\0")) { + if (dns_domain_equal(d,"\0012\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0010\0012\0010\001f\001f\003ip6\004arpa\0")) { if (z->level) goto LOWERLEVEL; if (!rqa(z)) goto DIE; if (typematch(DNS_T_PTR,dtype)) { if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE; - if (!response_addname("\015ipv6-allhosts\0")) goto DIE; + if (!response_addname("\016ip6-allrouters\0")) goto DIE; response_rfinish(RESPONSE_ANSWER); } cleanup(z); return 1; } - if (dns_domain_equal(d,"\016ipv6-localhost\0") || - dns_domain_equal(d,"\015ipv6-loopback\0")) + if (dns_domain_equal(d,"\015ip6-localhost\0") || + dns_domain_equal(d,"\014ip6-loopback\0")) { if (z->level) goto LOWERLEVEL; if (!rqa(z)) goto DIE; @@ -318,33 +298,7 @@ static int doit(struct query *z,int state) return 1; } - if (dns_domain_equal(d,"\015ipv6-localnet\0")) - { - if (z->level) goto LOWERLEVEL; - if (!rqa(z)) goto DIE; - if (typematch(DNS_T_AAAA,dtype)) { - if (!response_rstart(d,DNS_T_AAAA,655360)) goto DIE; - if (!response_addbytes("\376\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",16)) goto DIE; - response_rfinish(RESPONSE_ANSWER); - } - cleanup(z); - return 1; - } - - if (dns_domain_equal(d,"\020ipv6-mcastprefix\0")) - { - if (z->level) goto LOWERLEVEL; - if (!rqa(z)) goto DIE; - if (typematch(DNS_T_AAAA,dtype)) { - if (!response_rstart(d,DNS_T_AAAA,655360)) goto DIE; - if (!response_addbytes("\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",16)) goto DIE; - response_rfinish(RESPONSE_ANSWER); - } - cleanup(z); - return 1; - } - - if (dns_domain_equal(d,"\15ipv6-allnodes\0")) + if (dns_domain_equal(d,"\14ip6-allnodes\0")) { if (z->level) goto LOWERLEVEL; if (!rqa(z)) goto DIE; @@ -357,7 +311,7 @@ static int doit(struct query *z,int state) return 1; } - if (dns_domain_equal(d,"\17ipv6-allrouters\0")) + if (dns_domain_equal(d,"\16ip6-allrouters\0")) { if (z->level) goto LOWERLEVEL; if (!rqa(z)) goto DIE; @@ -370,19 +324,6 @@ static int doit(struct query *z,int state) return 1; } - if (dns_domain_equal(d,"\15ipv6-allhosts\0")) - { - if (z->level) goto LOWERLEVEL; - if (!rqa(z)) goto DIE; - if (typematch(DNS_T_AAAA,dtype)) { - if (!response_rstart(d,DNS_T_AAAA,655360)) goto DIE; - if (!response_addbytes("\377\002\000\000\000\000\000\000\000\000\000\000\000\000\000\003",16)) goto DIE; - response_rfinish(RESPONSE_ANSWER); - } - cleanup(z); - return 1; - } - if (dns_domain_equal(d,"\0011\0010\0010\003127\7in-addr\4arpa\0")) { if (z->level) goto LOWERLEVEL; if (!rqa(z)) goto DIE; @@ -408,7 +349,10 @@ static int doit(struct query *z,int state) byte_copy(key,2,DNS_T_CNAME); cached = cache_get(key,dlen + 2,&cachedlen,&ttl); - if (cached) { + /* A previous explicit query might have caused an empty RRSet to have been + ** cached. Take care to ignore such a thing. + */ + if (cached && cachedlen) { if (typematch(DNS_T_CNAME,dtype)) { log_cachedanswer(d,DNS_T_CNAME); if (!rqa(z)) goto DIE; @@ -417,8 +361,11 @@ static int doit(struct query *z,int state) return 1; } log_cachedcname(d,cached); - if (!dns_domain_copy(&cname,cached)) goto DIE; - goto CNAME; + if (!z->level) { + if (!move_name_to_alias(z,ttl)) goto DIE ; + } + if (!dns_domain_copy(&z->name[z->level],cached)) goto DIE; + goto NEWNAME; } if (typematch(DNS_T_NS,dtype)) { @@ -475,6 +422,29 @@ static int doit(struct query *z,int state) } } + if (typematch(DNS_T_SOA,dtype)) { + byte_copy(key,2,DNS_T_SOA); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { + log_cachedanswer(d,DNS_T_SOA); + if (!rqa(z)) goto DIE; + pos = 0; + while (pos = dns_packet_copy(cached,cachedlen,pos,misc,20)) { + pos = dns_packet_getname(cached,cachedlen,pos,&t2); + if (!pos) break; + pos = dns_packet_getname(cached,cachedlen,pos,&t3); + if (!pos) break; + if (!response_rstart(d,DNS_T_SOA,ttl)) goto DIE; + if (!response_addname(t2)) goto DIE; + if (!response_addname(t3)) goto DIE; + if (!response_addbytes(misc,20)) goto DIE; + response_rfinish(RESPONSE_ANSWER); + } + cleanup(z); + return 1; + } + } + if (typematch(DNS_T_A,dtype)) { byte_copy(key,2,DNS_T_A); cached = cache_get(key,dlen + 2,&cachedlen,&ttl); @@ -486,7 +456,7 @@ static int doit(struct query *z,int state) if (z->level) { log_cachedanswer(d,DNS_T_A); while (cachedlen >= 4) { - for (k = 0;k < 256;k += 16) + for (k = 0;k < 512;k += 16) if (byte_equal(z->servers[z->level - 1] + k,16,V6any)) { byte_copy(z->servers[z->level - 1] + k,12,V4mappedprefix); byte_copy(z->servers[z->level - 1] + k + 12,4,cached); @@ -519,7 +489,7 @@ static int doit(struct query *z,int state) if (z->level) { log_cachedanswer(d,DNS_T_AAAA); while (cachedlen >= 16) { - for (k = 0;k < 256;k += 16) + for (k = 0;k < 512;k += 16) if (byte_equal(z->servers[z->level - 1] + k,16,V6any)) { byte_copy(z->servers[z->level - 1] + k,16,cached); break; @@ -544,7 +514,7 @@ static int doit(struct query *z,int state) } } - if (!typematch(DNS_T_ANY,dtype) && !typematch(DNS_T_AXFR,dtype) && !typematch(DNS_T_CNAME,dtype) && !typematch(DNS_T_NS,dtype) && !typematch(DNS_T_PTR,dtype) && !typematch(DNS_T_A,dtype) && !typematch(DNS_T_MX,dtype) && !typematch(DNS_T_AAAA,dtype)) { + if (!typematch(DNS_T_ANY,dtype) && !typematch(DNS_T_AXFR,dtype) && !typematch(DNS_T_NS,dtype) && !typematch(DNS_T_PTR,dtype) && !typematch(DNS_T_A,dtype) && !typematch(DNS_T_MX,dtype) && !typematch(DNS_T_AAAA,dtype) && !typematch(DNS_T_SOA,dtype)) { byte_copy(key,2,dtype); cached = cache_get(key,dlen + 2,&cachedlen,&ttl); if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { @@ -583,7 +553,7 @@ static int doit(struct query *z,int state) cached = cache_get(key,dlen + 2,&cachedlen,&ttl); if (cached && cachedlen) { z->control[z->level] = d; - byte_zero(z->servers[z->level],256); + byte_zero(z->servers[z->level],512); for (j = 0;j < QUERY_MAXNS;++j) dns_domain_free(&z->ns[z->level][j]); pos = 0; @@ -617,12 +587,12 @@ static int doit(struct query *z,int state) dns_domain_free(&z->ns[z->level][j]); } - for (j = 0;j < 256;j += 16) + for (j = 0;j < 512;j += 16) if (byte_diff(z->servers[z->level] + j,16,V6any)) break; - if (j == 256) goto SERVFAIL; + if (j == 512) goto SERVFAIL; - dns_sortip6(z->servers[z->level],256); + dns_sortip6(z->servers[z->level],512); if (z->level) { dtype = z->ipv6[z->level] ? DNS_T_AAAA : DNS_T_A; log_tx(z->name[z->level],dtype,z->control[z->level],z->servers[z->level],z->level); @@ -644,7 +614,7 @@ static int doit(struct query *z,int state) HAVEPACKET: - if (++z->loop == 100) goto DIE; + if (++z->loop == 250) goto DIE; buf = z->dt.packet; len = z->dt.packetlen; @@ -667,29 +637,31 @@ static int doit(struct query *z,int state) if (rcode && (rcode != 3)) goto DIE; /* impossible; see irrelevant() */ flagout = 0; - flagcname = 0; flagreferral = 0; flagsoa = 0; soattl = 0; - cnamettl = 0; + if (!dns_domain_copy(&owner_name,d)) goto DIE; + /* This code assumes that the CNAME chain is presented in the correct + ** order. The example algorithm in RFC 1034 will actually result in this + ** being the case, but the words do not require it to be so. + */ for (j = 0;j < numanswers;++j) { pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; - if (dns_domain_equal(t1,d)) + if (dns_domain_equal(t1,owner_name)) if (byte_equal(header + 2,2,DNS_C_IN)) { /* should always be true */ if (typematch(header,dtype)) flagout = 1; else if (typematch(header,DNS_T_CNAME)) { - if (!dns_packet_getname(buf,len,pos,&cname)) goto DIE; - flagcname = 1; - cnamettl = ttlget(header + 4); + if (!dns_packet_getname(buf,len,pos,&owner_name)) goto DIE; } } uint16_unpack_big(header + 8,&datalen); pos += datalen; } + dns_domain_free(&owner_name) ; posauthority = pos; for (j = 0;j < numauthority;++j) { @@ -710,14 +682,6 @@ static int doit(struct query *z,int state) pos += datalen; } - if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa) - if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) { - log_lame(whichserver,control,referral); - byte_zero(whichserver,16); - goto HAVENS; - } - - if (records) { alloc_free(records); records = 0; } k = numanswers + numauthority + numglue; @@ -772,6 +736,10 @@ static int doit(struct query *z,int state) } if (!dns_domain_suffix(t1,control)) { i = j; continue; } + if (!flagforwardonly && byte_equal(type,2,DNS_T_NS) && dns_domain_equal(t1,control)) { + char dummy[512]; + if (!roots(dummy,control)) { i = j; continue; } + } if (!roots_same(t1,control)) { i = j; continue; } if (byte_equal(type,2,DNS_T_ANY)) @@ -779,15 +747,24 @@ static int doit(struct query *z,int state) else if (byte_equal(type,2,DNS_T_AXFR)) ; else if (byte_equal(type,2,DNS_T_SOA)) { + int non_authority = 0; + save_start(); while (i < j) { pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; pos = dns_packet_getname(buf,len,pos,&t3); if (!pos) goto DIE; pos = dns_packet_copy(buf,len,pos,misc,20); if (!pos) goto DIE; - if (records[i] < posauthority) + if (records[i] < posauthority) { log_rrsoa(whichserver,t1,t2,t3,misc,ttl); + save_data(misc,20); + save_data(t2,dns_domain_length(t2)); + save_data(t3,dns_domain_length(t3)); + non_authority++; + } ++i; } + if (non_authority) + save_finish(DNS_T_SOA,t1,ttl); } else if (byte_equal(type,2,DNS_T_CNAME)) { pos = dns_packet_skipname(buf,len,records[j - 1]); if (!pos) goto DIE; @@ -886,24 +863,36 @@ static int doit(struct query *z,int state) alloc_free(records); records = 0; + if (byte_diff(DNS_T_CNAME,2,dtype)) { + /* This code assumes that the CNAME chain is presented in the correct + ** order. The example algorithm in RFC 1034 will actually result in this + ** being the case, but the words do not require it to be so. + */ + pos = posanswers; + for (j = 0;j < numanswers;++j) { + pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + + if (dns_domain_equal(t1,d)) + if (byte_equal(header + 2,2,DNS_C_IN)) { /* should always be true */ + if (typematch(header,DNS_T_CNAME)) { + ttl = ttlget(header + 4); + if (z->level == 0) { + if (!move_name_to_alias(z,ttl)) goto DIE ; + } + if (!dns_packet_getname(buf,len,pos,&z->name[z->level])) goto DIE; + d = z->name[z->level]; + if (!dns_domain_suffix(d,control) || !roots_same(d,control)) + goto NEWNAME ; /* Cannot trust the chain further - restart using current name */ + } + } - if (flagcname) { - ttl = cnamettl; - CNAME: - if (!z->level) { - if (z->alias[QUERY_MAXALIAS - 1]) goto DIE; - for (j = QUERY_MAXALIAS - 1;j > 0;--j) - z->alias[j] = z->alias[j - 1]; - for (j = QUERY_MAXALIAS - 1;j > 0;--j) - z->aliasttl[j] = z->aliasttl[j - 1]; - z->alias[0] = z->name[0]; - z->aliasttl[0] = ttl; - z->name[0] = 0; + uint16_unpack_big(header + 8,&datalen); + pos += datalen; } - if (!dns_domain_copy(&z->name[z->level],cname)) goto DIE; - goto NEWNAME; } + /* A "no such name" error applies to the end of any CNAME chain, not to the start. */ if (rcode == 3) { log_nxdomain(whichserver,d,soattl); cachegeneric(DNS_T_ANY,d,"",0,soattl); @@ -916,10 +905,26 @@ static int doit(struct query *z,int state) return 1; } + /* We check for a lame server _after_ we have cached any records that it + ** might have returned to us. This copes better with the incorrect + ** behaviour of one content DNS server software that doesn't return + ** complete CNAME chains but instead returns only the first link in a + ** chain followed by a lame delegation to the same server. + ** Also: We check for a lame server _after_ following the CNAME chain. The + ** delegation in a referral answer applies to the _end_ of the chain, not + ** to the beginning. + */ + if (!rcode && !flagout && flagreferral && !flagsoa) + if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) { + log_lame(whichserver,control,referral); + byte_zero(whichserver,16); + goto HAVENS; + } + if (!flagout && flagsoa) + /* Don't save empty RRSets for those types that we use as special markers. */ if (byte_diff(DNS_T_ANY,2,dtype)) - if (byte_diff(DNS_T_AXFR,2,dtype)) - if (byte_diff(DNS_T_CNAME,2,dtype)) { + if (byte_diff(DNS_T_AXFR,2,dtype)) { save_start(); save_finish(dtype,d,soattl); log_nodata(whichserver,d,dtype,soattl); @@ -944,7 +949,7 @@ static int doit(struct query *z,int state) if (typematch(header,DNS_T_A)) if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */ if (datalen == 4) - for (k = 0;k < 256;k += 16) + for (k = 0;k < 512;k += 16) if (byte_equal(z->servers[z->level - 1] + k,16,V6any)) { byte_copy(z->servers[z->level - 1] + k,12,V4mappedprefix); if (!dns_packet_copy(buf,len,pos,z->servers[z->level - 1] + k + 12,4)) goto DIE; @@ -953,7 +958,7 @@ static int doit(struct query *z,int state) if (typematch(header,DNS_T_AAAA)) if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */ if (datalen == 16) - for (k = 0;k < 256;k += 16) + for (k = 0;k < 512;k += 16) if (byte_equal(z->servers[z->level - 1] + k,16,V6any)) { if (!dns_packet_copy(buf,len,pos,z->servers[z->level - 1] + k,16)) goto DIE; break; @@ -1011,9 +1016,21 @@ static int doit(struct query *z,int state) if (!dns_domain_suffix(d,referral)) goto DIE; + + /* In strict "forwardonly" mode, we don't, as the manual states, + ** contact a chain of servers according to "NS" resource records. + ** We don't obey any referral responses, therefore. Instead, we + ** eliminate the server from the list and try the next one. + */ + if (flagforwardonly) { + log_ignore_referral(whichserver,control,referral); + byte_zero(whichserver,16); + goto HAVENS; + } + control = d + dns_domain_suffixpos(d,referral); z->control[z->level] = control; - byte_zero(z->servers[z->level],256); + byte_zero(z->servers[z->level],512); for (j = 0;j < QUERY_MAXNS;++j) dns_domain_free(&z->ns[z->level][j]); k = 0; @@ -1045,6 +1062,7 @@ static int doit(struct query *z,int state) DIE: cleanup(z); if (records) { alloc_free(records); records = 0; } + dns_domain_free(&owner_name) ; return -1; } diff --git a/query.h b/query.h index 61812aa..72e79ff 100644 --- a/query.h +++ b/query.h @@ -14,7 +14,7 @@ struct query { char *name[QUERY_MAXLEVEL]; char *control[QUERY_MAXLEVEL]; /* pointing inside name */ char *ns[QUERY_MAXLEVEL][QUERY_MAXNS]; - char servers[QUERY_MAXLEVEL][256]; + char servers[QUERY_MAXLEVEL][512]; char *alias[QUERY_MAXALIAS]; uint32 aliasttl[QUERY_MAXALIAS]; char ipv6[QUERY_MAXLEVEL]; diff --git a/rbldns-data.c b/rbldns-data.c index ed495db..0c107a9 100644 --- a/rbldns-data.c +++ b/rbldns-data.c @@ -42,7 +42,7 @@ void syntaxerror(const char *why) } void die_datatmp(void) { - strerr_die2sys(111,FATAL,"unable to create data.tmp: "); + strerr_die2sys(111,FATAL,"unable to create data.cdb.tmp: "); } int main() @@ -59,7 +59,7 @@ int main() if (fd == -1) strerr_die2sys(111,FATAL,"unable to open data: "); buffer_init(&b,buffer_unixread,fd,bspace,sizeof bspace); - fdcdb = open_trunc("data.tmp"); + fdcdb = open_trunc("data.cdb.tmp"); if (fdcdb == -1) die_datatmp(); if (cdb_make_start(&cdb,fdcdb) == -1) die_datatmp(); @@ -89,6 +89,15 @@ int main() if (cdb_make_add(&cdb,"",0,tmp.s,tmp.len) == -1) die_datatmp(); break; + case '!': /* root entry */ + j = byte_chr(line.s + 1,line.len - 1,':'); + if (j >= line.len - 1) syntaxerror(": missing colon"); + if (ip4_scan(line.s + 1,ip) != j) syntaxerror(": malformed IP address"); + if (!stralloc_copyb(&tmp,ip,4)) nomem(); + if (!stralloc_catb(&tmp,line.s + j + 2,line.len - j - 2)) nomem(); + if (cdb_make_add(&cdb,"R",1,tmp.s,tmp.len) == -1) + die_datatmp(); + break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (!stralloc_0(&line)) nomem(); @@ -121,8 +130,8 @@ int main() if (cdb_make_finish(&cdb) == -1) die_datatmp(); if (fsync(fdcdb) == -1) die_datatmp(); if (close(fdcdb) == -1) die_datatmp(); /* NFS stupidity */ - if (rename("data.tmp","data.cdb") == -1) - strerr_die2sys(111,FATAL,"unable to move data.tmp to data.cdb: "); + if (rename("data.cdb.tmp","data.cdb") == -1) + strerr_die2sys(111,FATAL,"unable to move data.cdb.tmp to data.cdb: "); _exit(0); } diff --git a/rbldns.c b/rbldns.c index 2c13c27..289c8b6 100644 --- a/rbldns.c +++ b/rbldns.c @@ -33,7 +33,19 @@ static int doit(char *q,char qtype[2]) if (byte_equal(qtype,2,DNS_T_ANY)) flaga = flagtxt = 1; if (!flaga && !flagtxt) goto REFUSE; - if (dd(q,base,reverseip) != 4) goto REFUSE; + i = dd(q,base,reverseip); + if(i == 0) { /* root entry */ + r = cdb_find(&c,"R",1); + if (r == -1) return 0; + if (r && ((dlen = cdb_datalen(&c)) >= 4)) { + if (dlen > 100) dlen = 100; + if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return 0; + } + else { + goto REFUSE; + } + } else { + if (i != 4) goto REFUSE; uint32_unpack(reverseip,&ipnum); uint32_pack_big(ip,ipnum); @@ -63,7 +75,7 @@ static int doit(char *q,char qtype[2]) --dlen; dlen += ip4_fmt(data + dlen,ip); } - + } if (flaga) { if (!response_rstart(q,DNS_T_A,2048)) return 0; if (!response_addbytes(data,4)) return 0; diff --git a/response.c b/response.c index ba90c89..33b2fb1 100644 --- a/response.c +++ b/response.c @@ -34,7 +34,7 @@ int response_addname(const char *d) uint16_pack_big(buf,49152 + name_ptr[i]); return response_addbytes(buf,2); } - if (dlen <= 128) + if ((dlen <= 128) && (response_len < 16384)) if (name_num < NAMES) { byte_copy(name[name_num],dlen,d); name_ptr[name_num] = response_len; diff --git a/roots.c b/roots.c index 4162ec5..256ac77 100644 --- a/roots.c +++ b/roots.c @@ -23,7 +23,7 @@ static int roots_find(char *q) j = dns_domain_length(data.s + i); if (dns_domain_equal(data.s + i,q)) return i + j; i += j; - i += 256; + i += 512; } return -1; } @@ -41,12 +41,12 @@ static int roots_search(char *q) } } -int roots(char servers[256],char *q) +int roots(char servers[512],char *q) { int r; r = roots_find(q); if (r == -1) return 0; - byte_copy(servers,256,data.s + r); + byte_copy(servers,512,data.s + r); return 1; } @@ -61,7 +61,7 @@ static int init2(DIR *dir) const char *fqdn; static char *q; static stralloc text; - char servers[256]; + char servers[512]; int serverslen; int i; int j; @@ -86,15 +86,15 @@ static int init2(DIR *dir) j = 0; for (i = 0;i < text.len;++i) if (text.s[i] == '\n') { - if (serverslen <= 60) + if (serverslen <= 512 - 16) if (ip6_scan(text.s + j,servers + serverslen)) serverslen += 16; j = i + 1; } - byte_zero(servers + serverslen,256 - serverslen); + byte_zero(servers + serverslen,512 - serverslen); if (!stralloc_catb(&data,q,dns_domain_length(q))) return 0; - if (!stralloc_catb(&data,servers,256)) return 0; + if (!stralloc_catb(&data,servers,512)) return 0; } } } diff --git a/siphash.c b/siphash.c new file mode 100644 index 0000000..a2007db --- /dev/null +++ b/siphash.c @@ -0,0 +1,98 @@ +#include +#include "uint32.h" +#include "uint64.h" +#include "siphash.h" + +typedef uint64 u64; +typedef uint32 u32; +typedef unsigned char u8; + +#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) ) + +#define U32TO8_LE(p, v) \ + (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \ + (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24); + +#define U64TO8_LE(p, v) \ + U32TO8_LE((p), (u32)((v) )); \ + U32TO8_LE((p) + 4, (u32)((v) >> 32)); + +#define U8TO64_LE(p) \ + (((u64)((p)[0]) ) | \ + ((u64)((p)[1]) << 8) | \ + ((u64)((p)[2]) << 16) | \ + ((u64)((p)[3]) << 24) | \ + ((u64)((p)[4]) << 32) | \ + ((u64)((p)[5]) << 40) | \ + ((u64)((p)[6]) << 48) | \ + ((u64)((p)[7]) << 56)) + +#define SIPROUND \ + do { \ + x0 += x1; x1=ROTL(x1,13); x1 ^= x0; x0=ROTL(x0,32); \ + x2 += x3; x3=ROTL(x3,16); x3 ^= x2; \ + x0 += x3; x3=ROTL(x3,21); x3 ^= x0; \ + x2 += x1; x1=ROTL(x1,17); x1 ^= x2; x2=ROTL(x2,32); \ + } while(0) + +/* SipHash-2-4 */ +int siphash24( unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *k ) +{ + /* "somepseudorandomlygeneratedbytes" */ + u64 x0 = 0x736f6d6570736575ULL; + u64 x1 = 0x646f72616e646f6dULL; + u64 x2 = 0x6c7967656e657261ULL; + u64 x3 = 0x7465646279746573ULL; + u64 b; + u64 k0 = U8TO64_LE( k ); + u64 k1 = U8TO64_LE( k + 8 ); + u64 m; + const u8 *end = in + inlen - ( inlen % sizeof( u64 ) ); + const int left = inlen & 7; + b = ( ( u64 )inlen ) << 56; + x3 ^= k1; + x2 ^= k0; + x1 ^= k1; + x0 ^= k0; + + for ( ; in != end; in += 8 ) + { + m = U8TO64_LE( in ); + x3 ^= m; + SIPROUND; + SIPROUND; + x0 ^= m; + } + + switch( left ) + { + case 7: b |= ( ( u64 )in[ 6] ) << 48; + + case 6: b |= ( ( u64 )in[ 5] ) << 40; + + case 5: b |= ( ( u64 )in[ 4] ) << 32; + + case 4: b |= ( ( u64 )in[ 3] ) << 24; + + case 3: b |= ( ( u64 )in[ 2] ) << 16; + + case 2: b |= ( ( u64 )in[ 1] ) << 8; + + case 1: b |= ( ( u64 )in[ 0] ); break; + + case 0: break; + } + + x3 ^= b; + SIPROUND; + SIPROUND; + x0 ^= b; + x2 ^= 0xff; + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + b = x0 ^ x1 ^ x2 ^ x3; + U64TO8_LE( out, b ); + return 0; +} diff --git a/siphash.h b/siphash.h new file mode 100644 index 0000000..bc2e6a0 --- /dev/null +++ b/siphash.h @@ -0,0 +1,7 @@ + +#ifndef SIPHASH_H +#define SIPHASH_H + +int siphash24( unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *k ); + +#endif diff --git a/tdlookup.c b/tdlookup.c index b760340..11ba745 100644 --- a/tdlookup.c +++ b/tdlookup.c @@ -105,12 +105,13 @@ static int doname(void) return response_addname(d1); } -static int doit(char *q,char qtype[2]) +static int doit1(char **pqname,char qtype[2]) { unsigned int bpos; unsigned int anpos; unsigned int aupos; unsigned int arpos; + char *q; char *control; char *wild; int flaggavesoa; @@ -125,6 +126,12 @@ static int doit(char *q,char qtype[2]) int addrnum,addr6num; uint32 addrttl,addr6ttl; int i; + int loop = 0 ; + +RESTART: + if (loop++ >= 100) return 0 ; + + q = *pqname ; anpos = response_len; @@ -139,7 +146,14 @@ static int doit(char *q,char qtype[2]) if (byte_equal(type,2,DNS_T_NS)) flagns = 1; } if (flagns) break; - if (!*control) return 0; /* q is not within our bailiwick */ + if (!*control) { /* q is not within our bailiwick */ + if (loop <= 1) + return 0 ; + else { + response[2] &= ~4; + goto DONE; /* The administrator has issued contradictory instructions */ + } + } control += *control; control += 1; } @@ -186,9 +200,17 @@ static int doit(char *q,char qtype[2]) continue; } if (!response_rstart(q,type,ttl)) return 0; - if (byte_equal(type,2,DNS_T_NS) || byte_equal(type,2,DNS_T_CNAME) || byte_equal(type,2,DNS_T_PTR)) { + if (byte_equal(type,2,DNS_T_NS) || byte_equal(type,2,DNS_T_PTR)) { if (!doname()) return 0; } + else if (byte_equal(type,2,DNS_T_CNAME)) { + if (!doname()) return 0; + if (byte_diff(type,2,qtype)) { + response_rfinish(RESPONSE_ANSWER); + if (!dns_domain_copy(pqname,d1)) return 0 ; + goto RESTART ; + } + } else if (byte_equal(type,2,DNS_T_MX)) { if (!dobytes(2)) return 0; if (!doname()) return 0; @@ -300,9 +322,21 @@ static int doit(char *q,char qtype[2]) } } +DONE: return 1; } +static int doit(char *qname,char qtype[2]) +{ + int r ; + char * q = 0 ; + + if (!dns_domain_copy(&q, qname)) return 0 ; + r = doit1(&q, qtype) ; + dns_domain_free(&q) ; + return r ; +} + int respond(char *q,char qtype[2],char ip[16]) { int fd; diff --git a/tinydns-data.c b/tinydns-data.c index a509b21..337d73b 100644 --- a/tinydns-data.c +++ b/tinydns-data.c @@ -26,29 +26,56 @@ #define FATAL "tinydns-data: fatal: " +void die_semantic2(const char * s1, const char * s2) +{ + strerr_die3x(111,FATAL,s1,s2) ; +} +void die_semantic4(const char * s1, const char * s2,const char * s3, const char * s4) +{ + strerr_die5x(111,FATAL,s1,s2,s3,s4) ; +} void die_datatmp(void) { - strerr_die2sys(111,FATAL,"unable to create data.tmp: "); + strerr_die2sys(111,FATAL,"unable to create data.cdb.tmp: "); } void nomem(void) { strerr_die1sys(111,FATAL); } +void ttlparse(stralloc *sa,unsigned long * ttl, unsigned long defttl, const char * ltype) +{ + int ttllen ; + + if (sa->len > 0) { + if (!stralloc_0(sa)) nomem(); + ttllen = scan_ulong(sa->s,ttl) ; + if (ttllen + 1 != sa->len) + die_semantic4("unparseable TTL in ",ltype," line: ", sa->s) ; + } else + *ttl = defttl; +} + void ttdparse(stralloc *sa,char ttd[8]) { unsigned int i; char ch; byte_zero(ttd,8); - for (i = 0;(i < 16) && (i < sa->len);++i) { + for (i = 0;i < sa->len;++i) { + if (i >= 16) { + if (!stralloc_0(sa)) nomem() ; + die_semantic2("timestamp is too long: ", sa->s) ; + } ch = sa->s[i]; if ((ch >= '0') && (ch <= '9')) ch -= '0'; else if ((ch >= 'a') && (ch <= 'f')) ch -= 'a' - 10; - else - ch = 0; + else { + if (!stralloc_0(sa)) nomem() ; + die_semantic2("timestamp contains an invalid character: ", sa->s) ; + } if (!(i & 1)) ch <<= 4; ttd[i >> 1] |= ch; } @@ -56,6 +83,10 @@ void ttdparse(stralloc *sa,char ttd[8]) void locparse(stralloc *sa,char loc[2]) { + if (sa->len > 2) { + if (!stralloc_0(sa)) nomem() ; + die_semantic2("location code longer than two characters: ", sa->s) ; + } loc[0] = (sa->len > 0) ? sa->s[0] : 0; loc[1] = (sa->len > 1) ? sa->s[1] : 0; } @@ -167,11 +198,7 @@ void rr_finish(const char *owner) die_datatmp(); } -buffer b; -char bspace[1024]; - static stralloc line; -int match = 1; unsigned long linenum = 0; #define NUMFIELDS 15 @@ -198,12 +225,13 @@ static unsigned int scan_u32(const char *s,uint32 *u) { return r; } -int main() +void load(const char *fname) { int fddata; int i; int j; int k; + int iplen ; char ch; unsigned long ttl; char ttd[8]; @@ -214,20 +242,21 @@ int main() char type[2]; char soa[20]; char buf[4]; + char srv[6]; + int match; + buffer b; + char bspace[1024]; umask(022); - fddata = open_read("data"); + fddata = open_read(fname); if (fddata == -1) - strerr_die2sys(111,FATAL,"unable to open data: "); + strerr_die4sys(111,FATAL,"unable to open ",fname,": "); defaultsoa_init(fddata); buffer_init(&b,buffer_unixread,fddata,bspace,sizeof bspace); - fdcdb = open_trunc("data.tmp"); - if (fdcdb == -1) die_datatmp(); - if (cdb_make_start(&cdb,fdcdb) == -1) die_datatmp(); - + match = 1; while (match) { ++linenum; if (getln(&b,&line,&match,'\n') == -1) @@ -284,8 +313,7 @@ int main() if (!scan_u32(f[7].s,&u)) uint32_unpack_big(defaultsoa + 16,&u); uint32_pack_big(soa + 16,u); - if (!stralloc_0(&f[8])) nomem(); - if (!scan_ulong(f[8].s,&ttl)) ttl = TTL_NEGATIVE; + ttlparse(&f[8],&ttl,TTL_NEGATIVE,"Z"); ttdparse(&f[9],ttd); locparse(&f[10],loc); @@ -300,8 +328,7 @@ int main() case '.': case '&': if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); - if (!stralloc_0(&f[3])) nomem(); - if (!scan_ulong(f[3].s,&ttl)) ttl = TTL_NS; + ttlparse(&f[3],&ttl,TTL_NS,". or &"); ttdparse(&f[4],ttd); locparse(&f[5],loc); @@ -326,24 +353,26 @@ int main() rr_addname(d2); rr_finish(d1); - if (ip4_scan(f[1].s,ip)) { + iplen = ip4_scan(f[1].s,ip) ; + if (iplen != 0 && iplen + 1 == f[1].len) { rr_start(DNS_T_A,ttl,ttd,loc); rr_add(ip,4); rr_finish(d2); - } + } else if (f[1].len > 1) + die_semantic4("unparseable IP address in ","& or ."," line: ", f[1].s) ; break; case '+': case '=': if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); - if (!stralloc_0(&f[2])) nomem(); - if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE; + ttlparse(&f[2],&ttl,TTL_POSITIVE,"+ or ="); ttdparse(&f[3],ttd); locparse(&f[4],loc); if (!stralloc_0(&f[1])) nomem(); - if (ip4_scan(f[1].s,ip)) { + iplen = ip4_scan(f[1].s,ip) ; + if (iplen != 0 && iplen + 1 == f[1].len) { rr_start(DNS_T_A,ttl,ttd,loc); rr_add(ip,4); rr_finish(d1); @@ -359,8 +388,7 @@ int main() case '6': case '3': if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); - if (!stralloc_0(&f[2])) nomem(); - if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE; + ttlparse(&f[2],&ttl,TTL_POSITIVE,"6 or 3"); ttdparse(&f[3],ttd); locparse(&f[4],loc); @@ -370,24 +398,21 @@ int main() rr_add(ip6,16); rr_finish(d1); - if (line.s[0] == '6') { /* emit both .ip6.arpa and .ip6.int */ - dns_name6_domain(d6ptr,ip6,DNS_IP6_ARPA); - rr_start(DNS_T_PTR,ttl,ttd,loc); - rr_addname(d1); - rr_finish(d6ptr); - - dns_name6_domain(d6ptr,ip6,DNS_IP6_INT); + if (line.s[0] == '6') { + dns_name6_domain(d6ptr,ip6); rr_start(DNS_T_PTR,ttl,ttd,loc); rr_addname(d1); rr_finish(d6ptr); } - } + } else if (f[1].len > 1) + die_semantic4("unparseable IP address in ","+ or ="," line: ", f[1].s) ; + else + die_semantic4("missing IP address in ","+ or ="," line: ", f[1].s) ; break; case '@': if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); - if (!stralloc_0(&f[4])) nomem(); - if (!scan_ulong(f[4].s,&ttl)) ttl = TTL_POSITIVE; + ttlparse(&f[4],&ttl,TTL_POSITIVE,"@"); ttdparse(&f[5],ttd); locparse(&f[6],loc); @@ -415,11 +440,48 @@ int main() } break; + case 'S': + if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); + ttlparse(&f[6],&ttl,TTL_POSITIVE,"S"); + ttdparse(&f[7],ttd); + locparse(&f[8],loc); + + if (!stralloc_0(&f[1])) nomem(); + + if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) { + if (!stralloc_cats(&f[2],".srv.")) nomem(); + if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem(); + } + if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem(); + + if (!stralloc_0(&f[4])) nomem(); + if (!scan_u32(f[4].s,&u)) u = 0; + uint16_pack_big(srv,u); + if (!stralloc_0(&f[5])) nomem(); + if (!scan_u32(f[5].s,&u)) u = 0; + uint16_pack_big(srv + 2,u); + if (!stralloc_0(&f[3])) nomem(); + if (!scan_u32(f[3].s,&u)) nomem(); + uint16_pack_big(srv + 4,u); + + rr_start(DNS_T_SRV,ttl,ttd,loc); + rr_add(srv,6); + rr_addname(d2); + rr_finish(d1); + + iplen = ip4_scan(f[1].s,ip) ; + if (iplen != 0 && iplen + 1 == f[1].len) { + rr_start(DNS_T_A,ttl,ttd,loc); + rr_add(ip,4); + rr_finish(d2); + } else if (f[1].len > 1) + die_semantic4("unparseable IP address in ","@"," line: ", f[1].s) ; + break; + case '^': case 'C': if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); if (!dns_domain_fromdot(&d2,f[1].s,f[1].len)) nomem(); - if (!stralloc_0(&f[2])) nomem(); - if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE; + ttlparse(&f[2],&ttl,TTL_POSITIVE,"^ or C"); ttdparse(&f[3],ttd); locparse(&f[4],loc); @@ -433,8 +495,7 @@ int main() case '\'': if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); - if (!stralloc_0(&f[2])) nomem(); - if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE; + ttlparse(&f[2],&ttl,TTL_POSITIVE,"\'"); ttdparse(&f[3],ttd); locparse(&f[4],loc); @@ -444,7 +505,7 @@ int main() i = 0; while (i < f[1].len) { k = f[1].len - i; - if (k > 127) k = 127; + if (k > 255) k = 255; ch = k; rr_add(&ch,1); rr_add(f[1].s + i,k); @@ -456,8 +517,7 @@ int main() case ':': if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); - if (!stralloc_0(&f[3])) nomem(); - if (!scan_ulong(f[3].s,&ttl)) ttl = TTL_POSITIVE; + ttlparse(&f[3],&ttl,TTL_POSITIVE,":"); ttdparse(&f[4],ttd); locparse(&f[5],loc); @@ -490,12 +550,27 @@ int main() syntaxerror(": unrecognized leading character"); } } + close(fddata); +} + +int main(int argc, char **argv) +{ + umask(022); + fdcdb = open_trunc("data.cdb.tmp"); + if (fdcdb == -1) die_datatmp(); + if (cdb_make_start(&cdb,fdcdb) == -1) die_datatmp(); + + if (argc == 1) + load("data"); + while(--argc) { + load(argv[argc]); + } if (cdb_make_finish(&cdb) == -1) die_datatmp(); if (fsync(fdcdb) == -1) die_datatmp(); if (close(fdcdb) == -1) die_datatmp(); /* NFS stupidity */ - if (rename("data.tmp","data.cdb") == -1) - strerr_die2sys(111,FATAL,"unable to move data.tmp to data.cdb: "); + if (rename("data.cdb.tmp","data.cdb") == -1) + strerr_die2sys(111,FATAL,"unable to move data.cdb.tmp to data.cdb: "); _exit(0); } diff --git a/tinydns-edit.c b/tinydns-edit.c index 8633220..6324917 100644 --- a/tinydns-edit.c +++ b/tinydns-edit.c @@ -72,6 +72,12 @@ char strnum[FMT_ULONG]; static char *names[26]; static int used[26]; +static int want_ttl(unsigned long ttl) +{ + int isns = (mode == '.') || (mode == '&'); + return (isns && (ttl != TTL_NS)) || (!isns && (ttl != TTL_POSITIVE)); +} + void put(const char *buf,unsigned int len) { if (buffer_putalign(&bnew,buf,len) == -1) die_write(); @@ -270,8 +276,10 @@ int main(int argc,char **argv) if (!stralloc_cats(&f[0],":")) nomem(); break; } - if (!stralloc_cats(&f[0],":")) nomem(); - if (!stralloc_catb(&f[0],strnum,fmt_ulong(strnum,ttl))) nomem(); + if (want_ttl(ttl)) { + if (!stralloc_cats(&f[0],":")) nomem(); + if (!stralloc_catb(&f[0],strnum,fmt_ulong(strnum,ttl))) nomem(); + } if (!stralloc_cats(&f[0],"\n")) nomem(); put(f[0].s,f[0].len);