From 7f632c0640f174bbbc1deb532e3a3977d595d28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henryk=20Pl=C3=B6tz?= Date: Fri, 3 Oct 2014 20:22:01 +0200 Subject: Apply djbdns-1.05-dnssec.patch SHA1 62e2ce1d31f1fe908fac84fc8bd049a12621810f, contained in tinydnssec-1.05-1.3.tar.bz2 Source was http://www.tinydnssec.org/download/tinydnssec-1.05-1.3.tar.bz2, SHA1 b33d5c3e0de67f6427aad8c00a99580b59804075 --- Makefile | 67 ++++++---- TARGETS | 5 + axfrdns.c | 4 + base32hex.c | 79 ++++++++++++ base32hex.h | 23 ++++ dns.h | 9 ++ edns0.c | 45 +++++++ edns0.h | 22 ++++ parsetype.c | 2 + printpacket.c | 4 +- printrecord.c | 134 ++++++++++++++++++-- printtype.c | 47 +++++++ printtype.h | 23 ++++ response.c | 2 + response.h | 2 + server.c | 6 +- sha1.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sha1.h | 29 +++++ tdlookup.c | 308 ++++++++++++++++++++++++++++++++++++++++----- tinydns-data.c | 2 +- tinydns-get.c | 10 +- 21 files changed, 1142 insertions(+), 66 deletions(-) create mode 100644 base32hex.c create mode 100644 base32hex.h create mode 100644 edns0.c create mode 100644 edns0.h create mode 100644 printtype.c create mode 100644 printtype.h create mode 100644 sha1.c create mode 100644 sha1.h diff --git a/Makefile b/Makefile index a141bf8..fe27543 100644 --- a/Makefile +++ b/Makefile @@ -53,10 +53,10 @@ iopause.h taia.h tai.h uint64.h taia.h axfrdns: \ load axfrdns.o iopause.o droproot.o tdlookup.o response.o qlog.o \ prot.o timeoutread.o timeoutwrite.o clientloc.o dns.a libtai.a alloc.a env.a \ -cdb.a buffer.a unix.a byte.a +cdb.a buffer.a unix.a byte.a sha1.o base32hex.o edns0.o ./load axfrdns iopause.o droproot.o tdlookup.o response.o \ qlog.o prot.o timeoutread.o timeoutwrite.o clientloc.o dns.a libtai.a \ - alloc.a env.a cdb.a buffer.a unix.a byte.a + alloc.a env.a cdb.a buffer.a unix.a byte.a sha1.o base32hex.o edns0.o axfrdns-conf: \ load axfrdns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a @@ -76,6 +76,10 @@ dns.h stralloc.h iopause.h taia.h tai.h taia.h scan.h qlog.h uint16.h \ response.h uint32.h clientloc.h ./compile axfrdns.c +base32hex.o: \ +compile base32hex.c base32hex.h + ./compile base32hex.c + buffer.a: \ makelib buffer.o buffer_1.o buffer_2.o buffer_copy.o buffer_get.o \ buffer_put.o strerr_die.o strerr_sys.o @@ -454,10 +458,11 @@ gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h ip6.h dnsq: \ load dnsq.o iopause.o printrecord.o printpacket.o parsetype.o dns.a \ -env.a libtai.a buffer.a alloc.a unix.a byte.a socket.lib +env.a libtai.a buffer.a alloc.a unix.a byte.a socket.lib printtype.o \ +base32hex.o ./load dnsq iopause.o printrecord.o printpacket.o \ parsetype.o dns.a env.a libtai.a buffer.a alloc.a unix.a \ - byte.a `cat socket.lib` + byte.a `cat socket.lib` printtype.o base32hex.o dnsq.o: \ compile dnsq.c uint16.h strerr.h buffer.h scan.h str.h byte.h error.h \ @@ -467,10 +472,11 @@ gen_alloc.h parsetype.h dns.h stralloc.h iopause.h taia.h dnsqr: \ load dnsqr.o iopause.o printrecord.o printpacket.o parsetype.o dns.a \ -env.a libtai.a buffer.a alloc.a unix.a byte.a socket.lib +env.a libtai.a buffer.a alloc.a unix.a byte.a socket.lib printtype.o \ +base32hex.o ./load dnsqr iopause.o printrecord.o printpacket.o \ parsetype.o dns.a env.a libtai.a buffer.a alloc.a unix.a \ - byte.a `cat socket.lib` + byte.a `cat socket.lib` printtype.o base32hex.o dnsqr.o: \ compile dnsqr.c uint16.h strerr.h buffer.h scan.h str.h byte.h \ @@ -480,10 +486,10 @@ gen_alloc.h parsetype.h dns.h stralloc.h iopause.h taia.h dnstrace: \ load dnstrace.o dd.o iopause.o printrecord.o parsetype.o dns.a env.a \ -libtai.a alloc.a buffer.a unix.a byte.a socket.lib +libtai.a alloc.a buffer.a unix.a byte.a socket.lib printtype.o base32hex.o ./load dnstrace dd.o iopause.o printrecord.o parsetype.o \ dns.a env.a libtai.a alloc.a buffer.a unix.a byte.a `cat \ - socket.lib` + socket.lib` printtype.o base32hex.o dnstrace.o: \ compile dnstrace.c uint16.h uint32.h fmt.h str.h byte.h ip4.h \ @@ -514,6 +520,10 @@ droproot.o: \ compile droproot.c env.h scan.h prot.h strerr.h ./compile droproot.c +edns0.o: \ +compile edns0.c edns0.h uint16.h dns.h response.h iopause.h taia.h uint64.h + ./compile edns0.c + env.a: \ makelib env.o ./makelib env.a env.o @@ -689,10 +699,10 @@ iopause.h taia.h tai.h uint64.h taia.h uint16.h parsetype.h pickdns: \ load pickdns.o server.o iopause.o response.o droproot.o qlog.o prot.o dns.a \ -env.a libtai.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib +env.a libtai.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib edns0.o ./load pickdns server.o iopause.o response.o droproot.o qlog.o \ prot.o dns.a env.a libtai.a cdb.a alloc.a buffer.a unix.a \ - byte.a `cat socket.lib` + byte.a `cat socket.lib` edns0.o pickdns-conf: \ load pickdns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a @@ -725,15 +735,19 @@ response.h uint32.h printpacket.o: \ compile printpacket.c uint16.h uint32.h error.h byte.h dns.h \ stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h \ -printrecord.h stralloc.h printpacket.h stralloc.h +printrecord.h stralloc.h printpacket.h stralloc.h printtype.h ./compile printpacket.c printrecord.o: \ compile printrecord.c uint16.h uint32.h error.h byte.h dns.h \ stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h \ -printrecord.h stralloc.h +printrecord.h stralloc.h printtype.h ./compile printrecord.c +printtype.o: \ +compile printtype.c printtype.h dns.h stralloc.h byte.h uint16.h iopause.h taia.h uint64.h + ./compile printtype.c + prog: \ dnscache-conf dnscache walldns-conf walldns rbldns-conf rbldns \ rbldns-data pickdns-conf pickdns pickdns-data tinydns-conf tinydns \ @@ -767,10 +781,10 @@ gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h rbldns: \ load rbldns.o server.o iopause.o response.o dd.o droproot.o qlog.o prot.o dns.a \ -env.a libtai.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib +env.a libtai.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib edns0.o ./load rbldns server.o iopause.o response.o dd.o droproot.o qlog.o \ prot.o dns.a env.a libtai.a cdb.a alloc.a buffer.a unix.a \ - byte.a `cat socket.lib` + byte.a `cat socket.lib` edns0.o rbldns-conf: \ load rbldns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a @@ -840,7 +854,7 @@ server.o: \ compile server.c byte.h case.h env.h buffer.h strerr.h ip4.h uint16.h \ ndelay.h socket.h uint16.h droproot.h qlog.h uint16.h response.h \ uint32.h dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h \ -taia.h iopause.h alloc.h str.h +taia.h iopause.h alloc.h str.h edns0.h ./compile server.c setup: \ @@ -931,6 +945,10 @@ tryn2i.c choose compile load socket.lib haven2i.h1 haven2i.h2 cp /dev/null haven2i.h ./choose cL tryn2i haven2i.h1 haven2i.h2 socket > haven2i.h +sha1.o: \ +compile sha1.c sha1.h + ./compile sha1.c + str_chr.o: \ compile str_chr.c str.h ./compile str_chr.c @@ -1072,7 +1090,7 @@ compile taia_uint.c taia.h tai.h uint64.h tdlookup.o: \ compile tdlookup.c uint16.h open.h tai.h uint64.h cdb.h uint32.h \ byte.h case.h dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h \ -taia.h seek.h response.h uint32.h ip6.h clientloc.h +taia.h seek.h response.h uint32.h ip6.h clientloc.h sha1.h base32hex.h ./compile tdlookup.c timeoutread.o: \ @@ -1088,10 +1106,10 @@ timeoutwrite.h tinydns: \ load tinydns.o server.o iopause.o droproot.o tdlookup.o response.o qlog.o \ prot.o clientloc.o dns.a libtai.a env.a cdb.a alloc.a buffer.a unix.a byte.a \ -socket.lib +socket.lib sha1.o base32hex.o edns0.o ./load tinydns server.o iopause.o droproot.o tdlookup.o response.o \ qlog.o prot.o clientloc.o dns.a libtai.a env.a cdb.a alloc.a buffer.a \ - unix.a byte.a `cat socket.lib` + unix.a byte.a `cat socket.lib` sha1.o base32hex.o edns0.o tinydns-conf: \ load tinydns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a @@ -1126,11 +1144,12 @@ dns.h stralloc.h iopause.h taia.h tai.h uint64.h taia.h ./compile tinydns-edit.c tinydns-get: \ -load tinydns-get.o tdlookup.o response.o printpacket.o printrecord.o \ -parsetype.o clientloc.o dns.a libtai.a cdb.a buffer.a alloc.a unix.a byte.a +load tinydns-get.o tdlookup.o sha1.o response.o printpacket.o printrecord.o \ +parsetype.o clientloc.o dns.a libtai.a cdb.a buffer.a alloc.a unix.a byte.a \ +base32hex.o printtype.o ./load tinydns-get tdlookup.o response.o printpacket.o \ printrecord.o parsetype.o clientloc.o dns.a libtai.a cdb.a buffer.a \ - alloc.a unix.a byte.a + alloc.a unix.a byte.a sha1.o base32hex.o printtype.o tinydns-get.o: \ compile tinydns-get.c str.h byte.h scan.h exit.h stralloc.h \ @@ -1198,10 +1217,10 @@ compile utime.c scan.h exit.h walldns: \ load walldns.o server.o iopause.o response.o droproot.o qlog.o prot.o dd.o \ -dns.a env.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib +dns.a env.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib edns0.o ./load walldns server.o iopause.o response.o droproot.o qlog.o \ prot.o dd.o dns.a libtai.a env.a cdb.a alloc.a buffer.a unix.a \ - byte.a `cat socket.lib` + byte.a `cat socket.lib` edns0.o walldns-conf: \ load walldns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a @@ -1227,4 +1246,4 @@ trysa6.c choose compile sockaddr_in6.h1 sockaddr_in6.h2 haveip6.h ./choose c trysa6 sockaddr_in6.h1 sockaddr_in6.h2 > sockaddr_in6.h clean: - rm -f `cat TARGETS` + rm -f `cat TARGETS` data data.cdb test/[ot]* diff --git a/TARGETS b/TARGETS index 8e8e457..252d7d2 100644 --- a/TARGETS +++ b/TARGETS @@ -240,3 +240,8 @@ scan_xlong.o socket_accept6.o socket_connect6.o socket_tcp6.o +base32hex.o +sha1.o +base32hex.o +printtype.o +edns0.o diff --git a/axfrdns.c b/axfrdns.c index 85723c9..9fd6f9c 100644 --- a/axfrdns.c +++ b/axfrdns.c @@ -23,6 +23,7 @@ #include "response.h" #include "ip6.h" #include "clientloc.h" +#include "edns0.h" extern int respond(char *,char *,char *); @@ -346,6 +347,9 @@ int main() if (byte_diff(qclass,2,DNS_C_IN) && byte_diff(qclass,2,DNS_C_ANY)) strerr_die2x(111,FATAL,"bogus query: bad class"); + pos = check_edns0(header, buf, len, pos); + if (!pos) die_truncated(); + qlog(ip,port,header,zone,qtype," "); if (byte_equal(qtype,2,DNS_T_AXFR)) { diff --git a/base32hex.c b/base32hex.c new file mode 100644 index 0000000..95826fd --- /dev/null +++ b/base32hex.c @@ -0,0 +1,79 @@ +/* (C) 2012 Peter Conrad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "base32hex.h" + +#define to_32hex(c) ((c) < 10 ? (c) + '0' : (c) + 'a' - 10) + +/* out must point to a buffer of at least (len * 8 / 5) + 1 bytes. + * Encoded string is *not* padded. + * See RFC-4648. This implementation produces lowercase hex characters. + * Returns length of encoded string. + */ +unsigned int base32hex(char *out, uint8_t *in, unsigned int len) { +int buf = 0, bits = 0; +char *x = out; + + while (len-- > 0) { + buf <<= 8; + buf |= *in++; + bits += 8; + while (bits >= 5) { + char c = (buf >> (bits - 5)) & 0x1f; + *x++ = to_32hex(c); + bits -= 5; + } + } + if (bits > 0) { + char c = (buf << (5 - bits)) & 0x1f; + *x++ = to_32hex(c); + } + return x - out; +} + +#ifdef TEST +#include +#include +#include + +static void test(char *in, char *expected, int explen) { +char buf[255]; +int r; + + if ((r = base32hex(buf, in, strlen(in))) != explen) { + printf("Failed: b32h('%s') yields %d chars (expected %d)\n", + in, r, explen); + exit(1); + } + if (strncmp(buf, expected, r)) { + buf[r] = 0; + printf("Failed: b32h('%s') = '%s' (expected %s)\n", + in, buf, expected); + exit(1); + } +} + +int main(int argc, char **argv) { + test("", "", 0); + test("f", "co", 2); + test("fo", "cpng", 4); + test("foo", "cpnmu", 5); + test("foob", "cpnmuog", 7); + test("fooba", "cpnmuoj1", 8); + test("foobar", "cpnmuoj1e8", 10); + printf("Success!\n"); +} + +#endif diff --git a/base32hex.h b/base32hex.h new file mode 100644 index 0000000..8208cdc --- /dev/null +++ b/base32hex.h @@ -0,0 +1,23 @@ +/* (C) 2012 Peter Conrad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _BASE32_HEX_H +#define _BASE32_HEX_H + +#include + +extern unsigned int base32hex(char *out, uint8_t *in, unsigned int len); + +#endif diff --git a/dns.h b/dns.h index 5398e2b..c9689a2 100644 --- a/dns.h +++ b/dns.h @@ -20,8 +20,17 @@ #define DNS_T_SIG "\0\30" #define DNS_T_KEY "\0\31" #define DNS_T_AAAA "\0\34" +#define DNS_T_OPT "\0\51" +#define DNS_T_DS "\0\53" +#define DNS_T_RRSIG "\0\56" +#define DNS_T_DNSKEY "\0\60" +#define DNS_T_NSEC3 "\0\62" +#define DNS_T_NSEC3PARAM "\0\63" #define DNS_T_AXFR "\0\374" #define DNS_T_ANY "\0\377" +/* Pseudo-RRs for DNSSEC */ +#define DNS_T_HASHREF "\377\1" +#define DNS_T_HASHLIST "\377\2" struct dns_transmit { char *query; /* 0, or dynamically allocated */ diff --git a/edns0.c b/edns0.c new file mode 100644 index 0000000..0487b2b --- /dev/null +++ b/edns0.c @@ -0,0 +1,45 @@ +/* (C) 2012 Peter Conrad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "dns.h" +#include "edns0.h" +#include "response.h" +#include "uint16.h" + +unsigned int check_edns0(const char header[12], const char *buf, const int len, unsigned int pos) +{ +char opt_class[2]; +char opt_ttl[4]; + + max_response_len = 512; + do_dnssec = 0; + if (!header[6] && !header[7] && !header[8] && !header[9] + && !header[10] && header[11] == 1) { + char nametype[3]; + uint16 size, min_len; + pos = dns_packet_copy(buf,len,pos,nametype,3); if (!pos) return pos; + if (nametype[0] || nametype[1] || nametype[2] != DNS_T_OPT[1]) return pos; + pos = dns_packet_copy(buf,len,pos,opt_class,2); if (!pos) return pos; + pos = dns_packet_copy(buf,len,pos,opt_ttl,4); if (!pos) return pos; + if (opt_ttl[0]) return pos; // unsupported RCODE in query + if (opt_ttl[1]) return pos; // unsupported version + do_dnssec = opt_ttl[2] & 0x80; + uint16_unpack_big(opt_class, &size); + min_len = do_dnssec ? 1220 : 512; + max_response_len = size > 4000 ? 4000 : size; + if (max_response_len < min_len) { max_response_len = min_len; } + } + return pos; +} diff --git a/edns0.h b/edns0.h new file mode 100644 index 0000000..ce41f9f --- /dev/null +++ b/edns0.h @@ -0,0 +1,22 @@ +/* (C) 2012 Peter Conrad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _EDNS0_H + +#define _EDNS0_H + +extern unsigned int check_edns0(const char *, const char *, const int, unsigned int); + +#endif diff --git a/parsetype.c b/parsetype.c index 167aaa4..b3bc332 100644 --- a/parsetype.c +++ b/parsetype.c @@ -24,6 +24,8 @@ int parsetype(char *s,char type[2]) else if (case_equals(s,"key")) byte_copy(type,2,DNS_T_KEY); else if (case_equals(s,"aaaa")) byte_copy(type,2,DNS_T_AAAA); else if (case_equals(s,"axfr")) byte_copy(type,2,DNS_T_AXFR); + else if (case_equals(s,"dnskey")) byte_copy(type,2,DNS_T_DNSKEY); + else if (case_equals(s,"ds")) byte_copy(type,2,DNS_T_DS); else return 0; diff --git a/printpacket.c b/printpacket.c index 7571e08..39b303e 100644 --- a/printpacket.c +++ b/printpacket.c @@ -5,6 +5,7 @@ #include "dns.h" #include "printrecord.h" #include "printpacket.h" +#include "printtype.h" static char *d; @@ -67,8 +68,7 @@ unsigned int printpacket_cat(stralloc *out,char *buf,unsigned int len) X("weird class") } else { - uint16_unpack_big(data,&type); - NUM(type) + if (!printtype(out,data)) return 0; X(" ") if (!dns_domain_todot_cat(out,d)) return 0; } diff --git a/printrecord.c b/printrecord.c index 4bc7c3e..4cc744d 100644 --- a/printrecord.c +++ b/printrecord.c @@ -5,9 +5,25 @@ #include "dns.h" #include "printrecord.h" #include "ip6.h" +#include "base32hex.h" +#include "printtype.h" static char *d; +static const char *HEX = "0123456789ABCDEF"; + +static int hexout(stralloc *out,const char *buf,unsigned int len,unsigned int pos,unsigned int n) { + unsigned char c; + int i; + + for (i = 0; i < n; i++) { + pos = dns_packet_copy(buf,len,pos,&c,1); if (!pos) return 0; + if (!stralloc_catb(out,&HEX[(c>>4)&0xf],1)) return 0; + if (!stralloc_catb(out,&HEX[c&0xf],1)) return 0; + } + return pos; +} + unsigned int printrecord_cat(stralloc *out,const char *buf,unsigned int len,unsigned int pos,const char *q,const char qtype[2]) { const char *x; @@ -18,6 +34,7 @@ unsigned int printrecord_cat(stralloc *out,const char *buf,unsigned int len,unsi unsigned int newpos; int i; unsigned char ch; + int rawlen; pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0; pos = dns_packet_copy(buf,len,pos,misc,10); if (!pos) return 0; @@ -33,15 +50,20 @@ unsigned int printrecord_cat(stralloc *out,const char *buf,unsigned int len,unsi if (!dns_domain_todot_cat(out,d)) return 0; if (!stralloc_cats(out," ")) return 0; - uint32_unpack_big(misc + 4,&u32); - if (!stralloc_catulong0(out,u32,0)) return 0; + if (byte_diff(misc,2,DNS_T_OPT)) { + uint32_unpack_big(misc + 4,&u32); + if (!stralloc_catulong0(out,u32,0)) return 0; - if (byte_diff(misc + 2,2,DNS_C_IN)) { - if (!stralloc_cats(out," weird class\n")) return 0; - return newpos; + if (byte_diff(misc + 2,2,DNS_C_IN)) { + if (!stralloc_cats(out," weird class\n")) return 0; + return newpos; + } + } else { + if (!stralloc_cats(out,"0")) return 0; } x = 0; + rawlen = 0; if (byte_equal(misc,2,DNS_T_NS)) x = " NS "; if (byte_equal(misc,2,DNS_T_PTR)) x = " PTR "; if (byte_equal(misc,2,DNS_T_CNAME)) x = " CNAME "; @@ -92,12 +114,111 @@ unsigned int printrecord_cat(stralloc *out,const char *buf,unsigned int len,unsi stringlen=ip6_fmt(ip6str,misc); if (!stralloc_catb(out,ip6str,stringlen)) return 0; } + else if (byte_equal(misc,2,DNS_T_DNSKEY)) { + pos = dns_packet_copy(buf,len,pos,misc,4); if (!pos) return 0; + if (!stralloc_cats(out," DNSKEY ")) return 0; + uint16_unpack_big(misc,&u16); + if (!stralloc_catulong0(out,u16,0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[2],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[3],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + rawlen = datalen - 4; + } + else if (byte_equal(misc,2,DNS_T_DS)) { + pos = dns_packet_copy(buf,len,pos,misc,4); if (!pos) return 0; + if (!stralloc_cats(out," DS ")) return 0; + uint16_unpack_big(misc,&u16); + if (!stralloc_catulong0(out,u16,0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[2],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[3],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + pos = hexout(out,buf,len,pos,datalen - 4); if (!pos) return 0; + } + else if (byte_equal(misc,2,DNS_T_RRSIG)) { + pos = dns_packet_copy(buf,len,pos,misc,18); if (!pos) return 0; + if (!stralloc_cats(out," RRSIG ")) return 0; + if (!printtype(out,misc)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[2],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[3],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + uint32_unpack_big(misc + 4,&u32); + if (!stralloc_catulong0(out,u32,0)) return 0; + if (!stralloc_cats(out," ")) return 0; + uint32_unpack_big(misc + 8,&u32); + if (!stralloc_catulong0(out,u32,0)) return 0; + if (!stralloc_cats(out," ")) return 0; + uint32_unpack_big(misc + 12,&u32); + if (!stralloc_catulong0(out,u32,0)) return 0; + if (!stralloc_cats(out," ")) return 0; + uint16_unpack_big(misc + 16,&u16); + if (!stralloc_catulong0(out,u16,0)) return 0; + if (!stralloc_cats(out," ")) return 0; + rawlen = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0; + rawlen = datalen - 18 - (rawlen - pos); + pos += datalen - 18 - rawlen; + if (!dns_domain_todot_cat(out,d)) return 0; + if (!stralloc_cats(out," ")) return 0; + } + else if (byte_equal(misc,2,DNS_T_NSEC3)) { + char nextHash[255]; + char nextOwner[255*8/5]; + int j; + pos = dns_packet_copy(buf,len,pos,misc,5); if (!pos) return 0; + if (!stralloc_cats(out," NSEC3 ")) return 0; + if (!stralloc_catulong0(out,misc[0],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[1],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + uint16_unpack_big(misc+2,&u16); + if (!stralloc_catulong0(out,u16,0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!misc[4]) + if (!stralloc_cats(out,"-")) return 0; + pos = hexout(out,buf,len,pos,misc[4]); if (!pos) return 0; + if (!stralloc_cats(out," ")) return 0; + pos = dns_packet_copy(buf,len,pos,misc,1); if (!pos) return 0; + pos = dns_packet_copy(buf,len,pos,nextHash,misc[0]); if (!pos) return 0; + i = base32hex(nextOwner, nextHash, misc[0]); + if (!stralloc_catb(out,nextOwner,i)) return 0; + while (pos < newpos) { + pos = dns_packet_copy(buf,len,pos,misc,2); if (!pos) return 0; + pos = dns_packet_copy(buf,len,pos,nextHash,misc[1]); if (!pos) return 0; + j = 8 * misc[1]; + for (i = 0; i < j; i++) { + if (nextHash[i/8] & (1 << (7 - (i%8)))) { + misc[1] = i; + if (!stralloc_cats(out," ")) return 0; + if (!printtype(out,misc)) return 0; + } + } + } + } + else if (byte_equal(misc,2,DNS_T_OPT)) { + if (!stralloc_cats(out," OPT ")) return 0; + uint16_unpack_big(misc+2, &u16); + if (!stralloc_catulong0(out,u16,0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[4],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!stralloc_catulong0(out,misc[5],0)) return 0; + if (!stralloc_cats(out," ")) return 0; + if (!hexout(out,misc,8,6,2)) return 0; + rawlen = datalen; + } else { if (!stralloc_cats(out," ")) return 0; uint16_unpack_big(misc,&u16); if (!stralloc_catulong0(out,u16,0)) return 0; if (!stralloc_cats(out," ")) return 0; - while (datalen--) { + rawlen = datalen; + } + while (rawlen--) { pos = dns_packet_copy(buf,len,pos,misc,1); if (!pos) return 0; if ((misc[0] >= 33) && (misc[0] <= 126) && (misc[0] != '\\')) { if (!stralloc_catb(out,misc,1)) return 0; @@ -111,7 +232,6 @@ unsigned int printrecord_cat(stralloc *out,const char *buf,unsigned int len,unsi if (!stralloc_catb(out,misc,4)) return 0; } } - } if (!stralloc_cats(out,"\n")) return 0; if (pos != newpos) { errno = error_proto; return 0; } diff --git a/printtype.c b/printtype.c new file mode 100644 index 0000000..ebaa99b --- /dev/null +++ b/printtype.c @@ -0,0 +1,47 @@ +/* (C) 2012 Peter Conrad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "byte.h" +#include "dns.h" +#include "uint16.h" +#include "printtype.h" + +int printtype(stralloc *out, const char type[2]) { +uint16 u16; + + if (byte_equal(type,2,DNS_T_A)) return stralloc_cats(out,"A"); + if (byte_equal(type,2,DNS_T_NS)) return stralloc_cats(out,"NS"); + if (byte_equal(type,2,DNS_T_CNAME)) return stralloc_cats(out,"CNAME"); + if (byte_equal(type,2,DNS_T_SOA)) return stralloc_cats(out,"SOA"); + if (byte_equal(type,2,DNS_T_PTR)) return stralloc_cats(out,"PTR"); + if (byte_equal(type,2,DNS_T_HINFO)) return stralloc_cats(out,"HINFO"); + if (byte_equal(type,2,DNS_T_MX)) return stralloc_cats(out,"MX"); + if (byte_equal(type,2,DNS_T_TXT)) return stralloc_cats(out,"TXT"); + if (byte_equal(type,2,DNS_T_RP)) return stralloc_cats(out,"RP"); + if (byte_equal(type,2,DNS_T_SIG)) return stralloc_cats(out,"SIG"); + if (byte_equal(type,2,DNS_T_KEY)) return stralloc_cats(out,"KEY"); + if (byte_equal(type,2,DNS_T_AAAA)) return stralloc_cats(out,"AAAA"); + if (byte_equal(type,2,DNS_T_OPT)) return stralloc_cats(out,"OPT"); + if (byte_equal(type,2,DNS_T_DS)) return stralloc_cats(out,"DS"); + if (byte_equal(type,2,DNS_T_RRSIG)) return stralloc_cats(out,"RRSIG"); + if (byte_equal(type,2,DNS_T_DNSKEY)) return stralloc_cats(out,"DNSKEY"); + if (byte_equal(type,2,DNS_T_NSEC3)) return stralloc_cats(out,"NSEC3"); + if (byte_equal(type,2,DNS_T_NSEC3PARAM)) return stralloc_cats(out,"NSEC3PARAM"); + if (byte_equal(type,2,DNS_T_AXFR)) return stralloc_cats(out,"AXFR"); + if (byte_equal(type,2,DNS_T_ANY)) return stralloc_cats(out,"*"); + + uint16_unpack_big(type,&u16); + return stralloc_catulong0(out,u16,0); +} diff --git a/printtype.h b/printtype.h new file mode 100644 index 0000000..d0ce87c --- /dev/null +++ b/printtype.h @@ -0,0 +1,23 @@ +/* (C) 2012 Peter Conrad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _PRINTTYPE_H +#define _PRINTTYPE_H + +#include "stralloc.h" + +extern int printtype(stralloc *, const char *); + +#endif diff --git a/response.c b/response.c index 33b2fb1..d45843c 100644 --- a/response.c +++ b/response.c @@ -5,6 +5,8 @@ char response[65535]; unsigned int response_len = 0; /* <= 65535 */ +unsigned int max_response_len = 0; /* <= 65535 */ +unsigned int do_dnssec = 0; static unsigned int tctarget; #define NAMES 100 diff --git a/response.h b/response.h index 206b1d4..14a4a1e 100644 --- a/response.h +++ b/response.h @@ -5,6 +5,8 @@ extern char response[]; extern unsigned int response_len; +extern unsigned int max_response_len; +extern unsigned int do_dnssec; extern int response_query(const char *,const char *,const char *); extern void response_nxdomain(void); diff --git a/server.c b/server.c index d52ce87..b754265 100644 --- a/server.c +++ b/server.c @@ -1,3 +1,4 @@ +#include "edns0.h" #include "byte.h" #include "case.h" #include "env.h" @@ -63,6 +64,9 @@ static int doit(void) if (header[2] & 126) goto NOTIMP; if (byte_equal(qtype,2,DNS_T_AXFR)) goto NOTIMP; + pos = check_edns0(header, buf, len, pos); + if (!pos) goto NOQ; + case_lowerb(q,dns_domain_length(q)); if (!respond(q,qtype,ip)) { qlog(ip,port,header,q,qtype," - "); @@ -168,7 +172,7 @@ int main() len = socket_recv6(udp53[i],buf,sizeof buf,ip,&port,&ifid); if (len < 0) continue; if (!doit()) continue; - if (response_len > 512) response_tc(); + if (response_len > max_response_len) response_tc(); socket_send6(udp53[i],response,response_len,ip,port,ifid); /* may block for buffer space; if it fails, too bad */ } diff --git a/sha1.c b/sha1.c new file mode 100644 index 0000000..8a3dfee --- /dev/null +++ b/sha1.c @@ -0,0 +1,385 @@ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 07/2002 +By Ralph Giles +Still 100% public domain +modified for use with stdint types, autoconf +code cleanup, removed attribution comments +switched SHA1Final() argument order for consistency +use SHA1_ prefix for public api +move public api to sha1.h + +----------------- +Modified 08/2012 +By Peter Conrad +Still 100% public domain + +Taken from http://svn.ghostscript.com/jbig2dec/trunk/ for inclusion in tinydns +Added/removed some includes +Replaced WORDS_BIGENDIAN with BYTE_ORDER == BIG_ENDIAN check +Test succeeds on x86_64 +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define SHA1HANDSOFF */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "sha1.h" + +void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +/* FIXME: can we do this in an endian-proof way? */ +#if BYTE_ORDER == BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg){ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif /* VERBOSE */ + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; + +#ifdef SHA1HANDSOFF + static uint8_t workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ +void SHA1_Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ +void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len) +{ + size_t i, j; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1_Transform(context->state, data + i); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); + +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ +void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) +{ + uint32_t i; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1_Update(context, (uint8_t *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1_Update(context, (uint8_t *)"\0", 1); + } + SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ + for (i = 0; i < SHA1_DIGEST_SIZE; i++) { + digest[i] = (uint8_t) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ + +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1_Transform(context->state, context->buffer); +#endif +} + +/*************************************************************/ + +#if 0 +int main(int argc, char** argv) +{ +int i, j; +SHA1_CTX context; +unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384]; +FILE* file; + + if (argc > 2) { + puts("Public domain SHA-1 implementation - by Steve Reid "); + puts("Modified for 16 bit environments 7/98 - by James H. Brown "); /* JHB */ + puts("Produces the SHA-1 hash of a file, or stdin if no file is specified."); + return(0); + } + if (argc < 2) { + file = stdin; + } + else { + if (!(file = fopen(argv[1], "rb"))) { + fputs("Unable to open file.", stderr); + return(-1); + } + } + SHA1_Init(&context); + while (!feof(file)) { /* note: what if ferror(file) */ + i = fread(buffer, 1, 16384, file); + SHA1_Update(&context, buffer, i); + } + SHA1_Final(&context, digest); + fclose(file); + for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) { + for (j = 0; j < 4; j++) { + printf("%02X", digest[i*4+j]); + } + putchar(' '); + } + putchar('\n'); + return(0); /* JHB */ +} +#endif + +/* self test */ + +#ifdef TEST + +static char *test_data[] = { + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "A million repetitions of 'a'"}; +static char *test_results[] = { + "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D", + "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1", + "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"}; + + +void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output) +{ + int i,j; + char *c = output; + + for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) { + for (j = 0; j < 4; j++) { + sprintf(c,"%02X", digest[i*4+j]); + c += 2; + } + sprintf(c, " "); + c += 1; + } + *(c - 1) = '\0'; +} + +int main(int argc, char** argv) +{ + int k; + SHA1_CTX context; + uint8_t digest[20]; + char output[80]; + + fprintf(stdout, "verifying SHA-1 implementation... "); + + for (k = 0; k < 2; k++){ + SHA1_Init(&context); + SHA1_Update(&context, (uint8_t*)test_data[k], strlen(test_data[k])); + SHA1_Final(&context, digest); + digest_to_hex(digest, output); + + if (strcmp(output, test_results[k])) { + fprintf(stdout, "FAIL\n"); + fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[k]); + fprintf(stderr,"\t%s returned\n", output); + fprintf(stderr,"\t%s is correct\n", test_results[k]); + return (1); + } + } + /* million 'a' vector we feed separately */ + SHA1_Init(&context); + for (k = 0; k < 1000000; k++) + SHA1_Update(&context, (uint8_t*)"a", 1); + SHA1_Final(&context, digest); + digest_to_hex(digest, output); + if (strcmp(output, test_results[2])) { + fprintf(stdout, "FAIL\n"); + fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[2]); + fprintf(stderr,"\t%s returned\n", output); + fprintf(stderr,"\t%s is correct\n", test_results[2]); + return (1); + } + + /* success */ + fprintf(stdout, "ok\n"); + return(0); +} +#endif /* TEST */ diff --git a/sha1.h b/sha1.h new file mode 100644 index 0000000..aff51bc --- /dev/null +++ b/sha1.h @@ -0,0 +1,29 @@ +/* public api for steve reid's public domain SHA-1 implementation */ +/* this file is in the public domain */ + +#ifndef __SHA1_H +#define __SHA1_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} SHA1_CTX; + +#define SHA1_DIGEST_SIZE 20 + +void SHA1_Init(SHA1_CTX* context); +void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len); +void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]); + +#ifdef __cplusplus +} +#endif + +#endif /* __SHA1_H */ diff --git a/tdlookup.c b/tdlookup.c index b760340..ade5e49 100644 --- a/tdlookup.c +++ b/tdlookup.c @@ -10,6 +10,9 @@ #include "response.h" #include "ip6.h" #include "clientloc.h" +#include "alloc.h" +#include "sha1.h" +#include "base32hex.h" static int want(const char *owner,const char type[2]) { @@ -34,7 +37,7 @@ static int want(const char *owner,const char type[2]) } static char *d1; - +static char *wantAddr; static char clientloc[2]; static struct tai now; static struct cdb c; @@ -44,10 +47,18 @@ static uint32 dlen; static unsigned int dpos; static char type[2]; static uint32 ttl; +static char *nsec3; +static char *cname = 0; +/* returns -1 on failure, + * returns 0 on not found + * returns 1 when found + * returns 2 when flagwild is true and no wildcard match has been found but + * a direct match exists. This is for RFC-1034 section 4.3.3 compatibility. + */ static int find(char *d,int flagwild) { - int r; + int r, direct=0; char ch; struct tai cutoff; char ttd[8]; @@ -57,7 +68,9 @@ static int find(char *d,int flagwild) for (;;) { r = cdb_findnext(&c,d,dns_domain_length(d)); - if (r <= 0) return r; + if (r < 0) return r; /* -1 */ + if (r == 0) { return flagwild ? direct ? 2 : 0 + : 0; } dlen = cdb_datalen(&c); if (dlen > sizeof data) return -1; if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return -1; @@ -68,6 +81,7 @@ static int find(char *d,int flagwild) dpos = dns_packet_copy(data,dlen,dpos,recordloc,2); if (!dpos) return -1; if (byte_diff(recordloc,2,clientloc)) continue; } + direct = direct || (ch != '*'); if (flagwild != (ch == '*')) continue; dpos = dns_packet_copy(data,dlen,dpos,ttlstr,4); if (!dpos) return -1; uint32_unpack_big(ttlstr,&ttl); @@ -105,6 +119,123 @@ static int doname(void) return response_addname(d1); } +static int addNSEC3(char *hashName) +{ +int r; + + cdb_findstart(&c); + while (r = find(hashName,0)) { + if (r == -1) return 0; + if (byte_equal(type,2,DNS_T_NSEC3)) { + if (!response_rstart(hashName,DNS_T_NSEC3,ttl)) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + } + else if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen > dpos+18 + && byte_equal(data+dpos,2,DNS_T_NSEC3)) { + if (!response_rstart(hashName,DNS_T_RRSIG,ttl)) return 0; + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + } + } + return 1; +} + +static int addNSEC3Cover(char *name, char *control, int wild) +{ +SHA1_CTX ctx; +int algo = 0, flags = 0, saltlen = 0, r; +uint16 iterations = 0; +char salt[255]; +uint8_t digest[SHA1_DIGEST_SIZE]; + + /* Search NSEC3PARAM to find hash parameters */ + cdb_findstart(&c); + while (r = find(control,0)) { + if (r == -1) return 0; + if (byte_equal(type,2,DNS_T_NSEC3PARAM) && dlen - dpos > 5) { + algo = data[dpos]; + flags = data[dpos+1]; + uint16_unpack_big(data + dpos + 2, &iterations); + saltlen = data[dpos+4]; + if (algo != 1 || flags || dlen - dpos - 5 < saltlen) { + algo = 0; + } else { + byte_copy(salt,saltlen, data + dpos + 5); + break; + } + } + } + if (algo != 1) return 0; /* not found or unsupported algorithm / flags */ + + /* Compute hash value */ + case_lowerb(name,dns_domain_length(name)); + SHA1_Init(&ctx); + if (wild) SHA1_Update(&ctx, "\1*", 2); + SHA1_Update(&ctx, name, dns_domain_length(name)); + SHA1_Update(&ctx, salt, saltlen); + SHA1_Final(&ctx, digest); + while (iterations-- > 0) { + SHA1_Init(&ctx); + SHA1_Update(&ctx, digest, SHA1_DIGEST_SIZE); + SHA1_Update(&ctx, salt, saltlen); + SHA1_Final(&ctx, digest); + } + + /* Find covering hash */ + char nibble = ((digest[0] >> 4) & 0xf) + '0'; + if (nibble > '9') { nibble += 'a' - '9' - 1; } + salt[0] = 1; + salt[1] = nibble; + byte_copy(salt+2, dns_domain_length(control), control); + cdb_findstart(&c); + while (r = find(salt,0)) { + if (r == -1) return 0; + if (byte_equal(type,2,DNS_T_HASHLIST) && dlen - dpos >= SHA1_DIGEST_SIZE) { + int hpos = dpos + SHA1_DIGEST_SIZE; + while (byte_diff(digest,SHA1_DIGEST_SIZE,data+hpos) > 0 && hpos < dlen) hpos += SHA1_DIGEST_SIZE; + hpos -= SHA1_DIGEST_SIZE; + *salt = base32hex(salt+1,data+hpos,SHA1_DIGEST_SIZE); + byte_copy(salt + *salt + 1, dns_domain_length(control), control); + break; + } + } + if (*salt == 1) return 0; /* not found */ + return addNSEC3(salt); +} + +static int addClosestEncloserProof(char *name, char *control, int includeWild) +{ +char *q = name; +char *hashName = 0; +int r; + + while (*q) { + cdb_findstart(&c); + while (r = find(q,0)) { + if (r == -1) return 0; + if (byte_equal(type,2,DNS_T_HASHREF) && dlen > dpos) { + if (!dns_packet_getname(data,dlen,dpos,&hashName)) return 0; + break; + } + } + if (hashName) { + int rc = addNSEC3(hashName); + alloc_free(hashName); + if (!rc) return 0; + hashName = 0; + break; + } + name = q; + q += *q + 1; + } + if (!*q) return 0; + if (includeWild && !addNSEC3Cover(q, control, 1)) return 0; + return addNSEC3Cover(name, control, 0); +} + static int doit(char *q,char qtype[2]) { unsigned int bpos; @@ -118,6 +249,8 @@ static int doit(char *q,char qtype[2]) int r; int flagns; int flagauthoritative; + int flagsigned; + char *flagcname; char x[20]; uint16 u16; char addr[8][4]; @@ -132,18 +265,28 @@ static int doit(char *q,char qtype[2]) for (;;) { flagns = 0; flagauthoritative = 0; + flagsigned = 0; cdb_findstart(&c); while (r = find(control,0)) { if (r == -1) return 0; if (byte_equal(type,2,DNS_T_SOA)) flagauthoritative = 1; - if (byte_equal(type,2,DNS_T_NS)) flagns = 1; + else if (byte_equal(type,2,DNS_T_NS)) flagns = 1; + else if (byte_equal(type,2,DNS_T_DNSKEY)) flagsigned |= 1; + else if (byte_equal(type,2,DNS_T_RRSIG)) flagsigned |= 2; + else if (byte_equal(type,2,DNS_T_NSEC3PARAM)) flagsigned |= 4; } + flagsigned = (flagsigned == 7); if (flagns) break; - if (!*control) return 0; /* q is not within our bailiwick */ + if (!*control) { + if (!cname) return 0; /* q is not within our bailiwick */ + response[2] &= ~4; /* CNAME chain ends in external reference */ + return 1; + } control += *control; control += 1; } + wild = q; if (!flagauthoritative) { response[2] &= ~4; goto AUTHORITY; /* q is in a child zone */ @@ -152,7 +295,11 @@ static int doit(char *q,char qtype[2]) flaggavesoa = 0; flagfound = 0; - wild = q; + flagcname = 0; + if (nsec3) { + alloc_free(nsec3); + nsec3 = 0; + } for (;;) { addrnum = addr6num = 0; @@ -160,10 +307,29 @@ static int doit(char *q,char qtype[2]) cdb_findstart(&c); while (r = find(wild,wild != q)) { if (r == -1) return 0; + if (r == 2) break; flagfound = 1; if (flaggavesoa && byte_equal(type,2,DNS_T_SOA)) continue; - if (byte_diff(type,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(type,2,DNS_T_CNAME)) continue; - if (byte_equal(type,2,DNS_T_A) && (dlen - dpos == 4)) { + if (do_dnssec && byte_equal(type,2,DNS_T_HASHREF) && dlen > dpos) { + if (!dns_packet_getname(data,dlen,dpos,&nsec3)) return 0; + } + if (byte_diff(type,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(type,2,DNS_T_CNAME) + && (!do_dnssec || byte_diff(type,2,DNS_T_RRSIG))) continue; + if (byte_equal(type,2,DNS_T_HASHREF) || byte_equal(type,2,DNS_T_HASHLIST)) continue; + if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen - dpos > 18) { + char sigtype[2]; + struct tai valid; + uint32 validFrom, validUntil; + byte_copy(sigtype,2,data + dpos); + if (byte_diff(sigtype,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(sigtype,2,DNS_T_CNAME)) continue; + uint32_unpack_big(data + dpos + 12, &validFrom); + tai_unix(&valid, validFrom); + if (tai_less(&now, &valid)) continue; + uint32_unpack_big(data + dpos + 8, &validUntil); + tai_unix(&valid, validUntil); + if (tai_less(&valid, &now)) continue; + } + if (byte_equal(type,2,DNS_T_A) && (dlen - dpos == 4) && (!do_dnssec || addrnum < 8)) { addrttl = ttl; i = dns_random(addrnum + 1); if (i < 8) { @@ -174,7 +340,7 @@ static int doit(char *q,char qtype[2]) if (addrnum < 1000000) ++addrnum; continue; } - if (byte_equal(type,2,DNS_T_AAAA) && (dlen - dpos == 16)) { + if (byte_equal(type,2,DNS_T_AAAA) && (dlen - dpos == 16) && (!do_dnssec || addr6num < 8)) { addr6ttl = ttl; i = dns_random(addr6num + 1); if (i < 8) { @@ -188,6 +354,9 @@ static int doit(char *q,char qtype[2]) 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 (!doname()) return 0; + if (byte_equal(type,2,DNS_T_CNAME) && byte_diff(qtype,2,DNS_T_CNAME)) { + if (!dns_domain_copy(&flagcname,d1)) return 0; + } } else if (byte_equal(type,2,DNS_T_MX)) { if (!dobytes(2)) return 0; @@ -199,10 +368,18 @@ static int doit(char *q,char qtype[2]) if (!dobytes(20)) return 0; flaggavesoa = 1; } + else if (byte_equal(type,2,DNS_T_RRSIG) && dlen - dpos > 18) { + char sigtype[2]; + byte_copy(sigtype,2,data + dpos); + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + } else if (!response_addbytes(data + dpos,dlen - dpos)) return 0; response_rfinish(RESPONSE_ANSWER); } + if (r == 2) break; for (i = 0;i < addrnum;++i) if (i < 8) { if (!response_rstart(q,DNS_T_A,addrttl)) return 0; @@ -223,6 +400,16 @@ static int doit(char *q,char qtype[2]) wild += 1; } + if (flagcname) { + if (response[RESPONSE_ANSWER+1] >= 100) { + dns_domain_free(&flagcname); /* most likely a loop */ + return 0; + } + if (cname) dns_domain_free(&cname); + cname = flagcname; + return doit(cname, qtype); + } + if (!flagfound) response_nxdomain(); @@ -230,22 +417,49 @@ static int doit(char *q,char qtype[2]) AUTHORITY: aupos = response_len; - if (flagauthoritative && (aupos == anpos)) { - cdb_findstart(&c); - while (r = find(control,0)) { - if (r == -1) return 0; - if (byte_equal(type,2,DNS_T_SOA)) { - if (!response_rstart(control,DNS_T_SOA,ttl)) return 0; - if (!doname()) return 0; - if (!doname()) return 0; - if (!dobytes(20)) return 0; - response_rfinish(RESPONSE_AUTHORITY); - break; + if (flagauthoritative && (aupos == anpos)) { /* NODATA or NXDOMAIN */ + if (!flaggavesoa) { + cdb_findstart(&c); + while (r = find(control,0)) { + if (r == -1) return 0; + if (!flaggavesoa && byte_equal(type,2,DNS_T_SOA)) { + if (!response_rstart(control,DNS_T_SOA,ttl)) return 0; + if (!doname()) return 0; + if (!doname()) return 0; + if (!dobytes(20)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + flaggavesoa = 1; + } + else if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen > dpos+18 + && byte_equal(data+dpos,2,DNS_T_SOA)) { + if (!response_rstart(control,DNS_T_RRSIG,ttl)) return 0; + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + } + } + } + if (do_dnssec && flagsigned) { + if (flagfound && nsec3) { /* NODATA */ + if (!addNSEC3(nsec3)) return 0; + if (wild != q) { /* Wildcard NODATA */ + if (!addClosestEncloserProof(q, control, 0)) return 0; + } + } + else { /* NXDOMAIN, or query for NSEC3 owner name */ + if (!addClosestEncloserProof(q, control, 1)) return 0; } } } - else + else { + if (do_dnssec && wild != q && flagsigned) { /* Wildcard answer */ + char *nextCloser = q; + while (nextCloser + *nextCloser + 1 < wild) { nextCloser += *nextCloser + 1; } + if (!addNSEC3Cover(nextCloser, control, 0)) return 0; + } if (want(control,DNS_T_NS)) { + int have_ds = 0; cdb_findstart(&c); while (r = find(control,0)) { if (r == -1) return 0; @@ -254,10 +468,33 @@ static int doit(char *q,char qtype[2]) if (!doname()) return 0; response_rfinish(RESPONSE_AUTHORITY); } + else if (do_dnssec && byte_equal(type,2,DNS_T_DS)) { + if (!response_rstart(control,DNS_T_DS,ttl)) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + have_ds = 1; + } + else if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen > dpos+18 + && (byte_equal(data+dpos,2,DNS_T_NS) + || byte_equal(data+dpos,2,DNS_T_DS))) { + if (!response_rstart(control,DNS_T_RRSIG,ttl)) return 0; + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + } } + if (do_dnssec && !flagauthoritative && !have_ds) { addNSEC3(control); } } + } arpos = response_len; + if (do_dnssec) { + /* Add EDNS0 OPT RR */ + if (!response_rstart("",DNS_T_OPT,1 << 15)) return 0; + uint16_pack_big(response+arpos+3, 512); + response_rfinish(RESPONSE_ADDITIONAL); + } bpos = anpos; while (bpos < arpos) { @@ -265,25 +502,33 @@ static int doit(char *q,char qtype[2]) bpos = dns_packet_copy(response,arpos,bpos,x,10); if (!bpos) return 0; if (byte_equal(x,2,DNS_T_NS) || byte_equal(x,2,DNS_T_MX)) { if (byte_equal(x,2,DNS_T_NS)) { - if (!dns_packet_getname(response,arpos,bpos,&d1)) return 0; + if (!dns_packet_getname(response,arpos,bpos,&wantAddr)) return 0; } else - if (!dns_packet_getname(response,arpos,bpos + 2,&d1)) return 0; - case_lowerb(d1,dns_domain_length(d1)); - if (want(d1,DNS_T_A)) { + if (!dns_packet_getname(response,arpos,bpos + 2,&wantAddr)) return 0; + case_lowerb(wantAddr,dns_domain_length(wantAddr)); + if (want(wantAddr,DNS_T_A)) { cdb_findstart(&c); - while (r = find(d1,0)) { + while (r = find(wantAddr,0)) { if (r == -1) return 0; if (byte_equal(type,2,DNS_T_A)) { - if (!response_rstart(d1,DNS_T_A,ttl)) return 0; + if (!response_rstart(wantAddr,DNS_T_A,ttl)) return 0; if (!dobytes(4)) return 0; response_rfinish(RESPONSE_ADDITIONAL); } else if (byte_equal(type,2,DNS_T_AAAA)) { - if (!response_rstart(d1,DNS_T_AAAA,ttl)) return 0; + if (!response_rstart(wantAddr,DNS_T_AAAA,ttl)) return 0; if (!dobytes(16)) return 0; response_rfinish(RESPONSE_ADDITIONAL); } + else if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen > dpos+18 + && (byte_equal(data+dpos,2,DNS_T_A) || byte_equal(data+dpos,2,DNS_T_AAAA))) { + if (!response_rstart(wantAddr,DNS_T_RRSIG,ttl)) return 0; + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_ADDITIONAL); + } } } } @@ -291,10 +536,10 @@ static int doit(char *q,char qtype[2]) bpos += u16; } - if (flagauthoritative && (response_len > 512)) { + if (flagauthoritative && (response_len > max_response_len)) { byte_zero(response + RESPONSE_ADDITIONAL,2); response_len = arpos; - if (response_len > 512) { + if (!do_dnssec && response_len > max_response_len) { byte_zero(response + RESPONSE_AUTHORITY,2); response_len = aupos; } @@ -316,6 +561,9 @@ int respond(char *q,char qtype[2],char ip[16]) cdb_init(&c,fd); r = doit(q,qtype); + if (cname) { + dns_domain_free(&cname); + } cdb_free(&c); close(fd); diff --git a/tinydns-data.c b/tinydns-data.c index 4d66865..f35fb3b 100644 --- a/tinydns-data.c +++ b/tinydns-data.c @@ -436,7 +436,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); diff --git a/tinydns-get.c b/tinydns-get.c index 56edb13..d17305d 100644 --- a/tinydns-get.c +++ b/tinydns-get.c @@ -20,7 +20,7 @@ extern int respond(char *,char *,char *); void usage(void) { - strerr_die1x(100,"tinydns-get: usage: tinydns-get type name [ip]"); + strerr_die1x(100,"tinydns-get: usage: tinydns-get [-s | -S] type name [ip]"); } void oops(void) { @@ -40,6 +40,14 @@ int main(int argc,char **argv) if (!*argv) usage(); if (!*++argv) usage(); + + max_response_len = 512; + if ((*argv)[0] == '-') { + if ((*argv)[1] != 's' && (*argv)[1] != 'S' || (*argv)[2]) usage(); + do_dnssec = 1; + max_response_len = (*argv)[1] == 's' ? 1220 : 4000; + if (!*++argv) usage(); + } if (!parsetype(*argv,type)) usage(); if (!*++argv) usage(); -- cgit v1.2.3