dot

packages and services management
Log | Files | Refs | README

commit 8c46bbe06abb5fbe822e4cd538aa8140cb0d922b
parent 64432a276bf5aa51f72d72fbe654205f54c24fae
Author: Josuah Demangeon <mail@josuah.net>
Date:   Fri, 30 Nov 2018 08:05:35 +0100

pack/djbdns: apply dnssec patch

Diffstat:
Mpack/djbdns/build | 2+-
Apack/djbdns/djbdns-1.05-dnssec.patch | 1749+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apack/djbdns/tinydns-sign.pl | 1025+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 2775 insertions(+), 1 deletion(-)

diff --git a/pack/djbdns/build b/pack/djbdns/build @@ -1,6 +1,6 @@ #!/bin/sh -ex -! patch -N -p1 <$DEF/djbdns-1.05-ipv6.diff +! patch -N -p1 <$DEF/djbdns-1.05-dnssec.diff echo gcc -O2 -include /usr/include/errno.h >conf-cc echo "$PREFIX" >conf-home diff --git a/pack/djbdns/djbdns-1.05-dnssec.patch b/pack/djbdns/djbdns-1.05-dnssec.patch @@ -0,0 +1,1749 @@ +(C) 2012 Peter Conrad <conrad@quisquis.de> + +This patch 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 <http://www.gnu.org/licenses/>. + +diff -rNU3 djbdns-1.05.tds-base/axfrdns.c djbdns-1.05.tinydnssec/axfrdns.c +--- djbdns-1.05.tds-base/axfrdns.c 2012-12-06 22:45:38.000000000 +0100 ++++ djbdns-1.05.tinydnssec/axfrdns.c 2012-12-06 22:39:13.000000000 +0100 +@@ -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 @@ + 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 -rNU3 djbdns-1.05.tds-base/base32hex.c djbdns-1.05.tinydnssec/base32hex.c +--- djbdns-1.05.tds-base/base32hex.c 1970-01-01 01:00:00.000000000 +0100 ++++ djbdns-1.05.tinydnssec/base32hex.c 2012-12-06 22:39:13.000000000 +0100 +@@ -0,0 +1,79 @@ ++/* (C) 2012 Peter Conrad <conrad@quisquis.de> ++ * ++ * 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 <http://www.gnu.org/licenses/>. ++ */ ++ ++#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 <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++ ++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 -rNU3 djbdns-1.05.tds-base/base32hex.h djbdns-1.05.tinydnssec/base32hex.h +--- djbdns-1.05.tds-base/base32hex.h 1970-01-01 01:00:00.000000000 +0100 ++++ djbdns-1.05.tinydnssec/base32hex.h 2012-12-06 22:39:13.000000000 +0100 +@@ -0,0 +1,23 @@ ++/* (C) 2012 Peter Conrad <conrad@quisquis.de> ++ * ++ * 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 <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef _BASE32_HEX_H ++#define _BASE32_HEX_H ++ ++#include <stdint.h> ++ ++extern unsigned int base32hex(char *out, uint8_t *in, unsigned int len); ++ ++#endif +diff -rNU3 djbdns-1.05.tds-base/dns.h djbdns-1.05.tinydnssec/dns.h +--- djbdns-1.05.tds-base/dns.h 2012-12-06 22:45:38.000000000 +0100 ++++ djbdns-1.05.tinydnssec/dns.h 2012-12-06 22:39:13.000000000 +0100 +@@ -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 -rNU3 djbdns-1.05.tds-base/edns0.c djbdns-1.05.tinydnssec/edns0.c +--- djbdns-1.05.tds-base/edns0.c 1970-01-01 01:00:00.000000000 +0100 ++++ djbdns-1.05.tinydnssec/edns0.c 2012-12-06 22:39:13.000000000 +0100 +@@ -0,0 +1,45 @@ ++/* (C) 2012 Peter Conrad <conrad@quisquis.de> ++ * ++ * 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 <http://www.gnu.org/licenses/>. ++ */ ++ ++#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 -rNU3 djbdns-1.05.tds-base/edns0.h djbdns-1.05.tinydnssec/edns0.h +--- djbdns-1.05.tds-base/edns0.h 1970-01-01 01:00:00.000000000 +0100 ++++ djbdns-1.05.tinydnssec/edns0.h 2012-12-06 22:39:13.000000000 +0100 +@@ -0,0 +1,22 @@ ++/* (C) 2012 Peter Conrad <conrad@quisquis.de> ++ * ++ * 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 <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef _EDNS0_H ++ ++#define _EDNS0_H ++ ++extern unsigned int check_edns0(const char *, const char *, const int, unsigned int); ++ ++#endif +diff -rNU3 djbdns-1.05.tds-base/Makefile djbdns-1.05.tinydnssec/Makefile +--- djbdns-1.05.tds-base/Makefile 2012-12-06 22:45:45.000000000 +0100 ++++ djbdns-1.05.tinydnssec/Makefile 2012-12-07 11:07:23.000000000 +0100 +@@ -53,10 +53,10 @@ + 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 @@ + 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 @@ + + 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 @@ + + 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 @@ + + 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 @@ + 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 @@ + + 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 @@ + 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 @@ + + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + ./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 @@ + + 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 @@ + ./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 -rNU3 djbdns-1.05.tds-base/parsetype.c djbdns-1.05.tinydnssec/parsetype.c +--- djbdns-1.05.tds-base/parsetype.c 2001-02-11 22:11:45.000000000 +0100 ++++ djbdns-1.05.tinydnssec/parsetype.c 2012-12-06 22:39:13.000000000 +0100 +@@ -24,6 +24,8 @@ + 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 -rNU3 djbdns-1.05.tds-base/printpacket.c djbdns-1.05.tinydnssec/printpacket.c +--- djbdns-1.05.tds-base/printpacket.c 2001-02-11 22:11:45.000000000 +0100 ++++ djbdns-1.05.tinydnssec/printpacket.c 2012-12-06 22:39:13.000000000 +0100 +@@ -5,6 +5,7 @@ + #include "dns.h" + #include "printrecord.h" + #include "printpacket.h" ++#include "printtype.h" + + static char *d; + +@@ -67,8 +68,7 @@ + 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 -rNU3 djbdns-1.05.tds-base/printrecord.c djbdns-1.05.tinydnssec/printrecord.c +--- djbdns-1.05.tds-base/printrecord.c 2012-12-06 22:45:38.000000000 +0100 ++++ djbdns-1.05.tinydnssec/printrecord.c 2012-12-06 22:39:13.000000000 +0100 +@@ -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 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 @@ + + 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 @@ + 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 @@ + if (!stralloc_catb(out,misc,4)) return 0; + } + } +- } + + if (!stralloc_cats(out,"\n")) return 0; + if (pos != newpos) { errno = error_proto; return 0; } +diff -rNU3 djbdns-1.05.tds-base/printtype.c djbdns-1.05.tinydnssec/printtype.c +--- djbdns-1.05.tds-base/printtype.c 1970-01-01 01:00:00.000000000 +0100 ++++ djbdns-1.05.tinydnssec/printtype.c 2012-12-06 22:39:13.000000000 +0100 +@@ -0,0 +1,47 @@ ++/* (C) 2012 Peter Conrad <conrad@quisquis.de> ++ * ++ * 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 <http://www.gnu.org/licenses/>. ++ */ ++ ++#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 -rNU3 djbdns-1.05.tds-base/printtype.h djbdns-1.05.tinydnssec/printtype.h +--- djbdns-1.05.tds-base/printtype.h 1970-01-01 01:00:00.000000000 +0100 ++++ djbdns-1.05.tinydnssec/printtype.h 2012-12-06 22:39:13.000000000 +0100 +@@ -0,0 +1,23 @@ ++/* (C) 2012 Peter Conrad <conrad@quisquis.de> ++ * ++ * 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 <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef _PRINTTYPE_H ++#define _PRINTTYPE_H ++ ++#include "stralloc.h" ++ ++extern int printtype(stralloc *, const char *); ++ ++#endif +diff -rNU3 djbdns-1.05.tds-base/response.c djbdns-1.05.tinydnssec/response.c +--- djbdns-1.05.tds-base/response.c 2001-02-11 22:11:45.000000000 +0100 ++++ djbdns-1.05.tinydnssec/response.c 2012-12-06 22:39:13.000000000 +0100 +@@ -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 -rNU3 djbdns-1.05.tds-base/response.h djbdns-1.05.tinydnssec/response.h +--- djbdns-1.05.tds-base/response.h 2001-02-11 22:11:45.000000000 +0100 ++++ djbdns-1.05.tinydnssec/response.h 2012-12-06 22:39:13.000000000 +0100 +@@ -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 -rNU3 djbdns-1.05.tds-base/server.c djbdns-1.05.tinydnssec/server.c +--- djbdns-1.05.tds-base/server.c 2012-12-06 22:45:38.000000000 +0100 ++++ djbdns-1.05.tinydnssec/server.c 2012-12-06 22:39:13.000000000 +0100 +@@ -1,3 +1,4 @@ ++#include "edns0.h" + #include "byte.h" + #include "case.h" + #include "env.h" +@@ -63,6 +64,9 @@ + 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 @@ + 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 -rNU3 djbdns-1.05.tds-base/sha1.c djbdns-1.05.tinydnssec/sha1.c +--- djbdns-1.05.tds-base/sha1.c 1970-01-01 01:00:00.000000000 +0100 ++++ djbdns-1.05.tinydnssec/sha1.c 2012-12-06 22:39:13.000000000 +0100 +@@ -0,0 +1,385 @@ ++/* ++SHA-1 in C ++By Steve Reid <sreid@sea-to-sky.net> ++100% Public Domain ++ ++----------------- ++Modified 7/98 ++By James H. Brown <jbrown@burgoyne.com> ++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 <process.h> 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 <sreid@sea-to-sky.net> ++Still 100% public domain ++ ++1- Removed #include <process.h> 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 <Saul.Kravitz@celera.com> ++Still 100% PD ++Modified to run on Compaq Alpha hardware. ++ ++----------------- ++Modified 07/2002 ++By Ralph Giles <giles@ghostscript.com> ++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 <conrad@quisquis.de> ++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 <endian.h> ++#include <stdio.h> ++#include <string.h> ++ ++#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 <sreid@sea-to-sky.net>"); ++ puts("Modified for 16 bit environments 7/98 - by James H. Brown <jbrown@burgoyne.com>"); /* 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 -rNU3 djbdns-1.05.tds-base/sha1.h djbdns-1.05.tinydnssec/sha1.h +--- djbdns-1.05.tds-base/sha1.h 1970-01-01 01:00:00.000000000 +0100 ++++ djbdns-1.05.tinydnssec/sha1.h 2012-12-06 22:39:13.000000000 +0100 +@@ -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 <stdint.h> ++ ++#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 -rNU3 djbdns-1.05.tds-base/TARGETS djbdns-1.05.tinydnssec/TARGETS +--- djbdns-1.05.tds-base/TARGETS 2012-12-06 22:45:38.000000000 +0100 ++++ djbdns-1.05.tinydnssec/TARGETS 2012-12-06 22:39:13.000000000 +0100 +@@ -240,3 +240,8 @@ + socket_accept6.o + socket_connect6.o + socket_tcp6.o ++base32hex.o ++sha1.o ++base32hex.o ++printtype.o ++edns0.o +diff -rNU3 djbdns-1.05.tds-base/tdlookup.c djbdns-1.05.tinydnssec/tdlookup.c +--- djbdns-1.05.tds-base/tdlookup.c 2012-12-06 22:45:38.000000000 +0100 ++++ djbdns-1.05.tinydnssec/tdlookup.c 2012-12-07 11:01:39.000000000 +0100 +@@ -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 char *d1; +- ++static char *wantAddr; + static char clientloc[2]; + static struct tai now; + static struct cdb c; +@@ -44,10 +47,18 @@ + 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 @@ + + 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 @@ + 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 @@ + 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 @@ + int r; + int flagns; + int flagauthoritative; ++ int flagsigned; ++ char *flagcname; + char x[20]; + uint16 u16; + char addr[8][4]; +@@ -132,18 +265,28 @@ + 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 @@ + + flaggavesoa = 0; + flagfound = 0; +- wild = q; ++ flagcname = 0; ++ if (nsec3) { ++ alloc_free(nsec3); ++ nsec3 = 0; ++ } + + for (;;) { + addrnum = addr6num = 0; +@@ -160,10 +307,29 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + cdb_init(&c,fd); + + r = doit(q,qtype); ++ if (cname) { ++ dns_domain_free(&cname); ++ } + + cdb_free(&c); + close(fd); +diff -rNU3 djbdns-1.05.tds-base/tinydns-data.c djbdns-1.05.tinydnssec/tinydns-data.c +--- djbdns-1.05.tds-base/tinydns-data.c 2012-12-06 22:45:38.000000000 +0100 ++++ djbdns-1.05.tinydnssec/tinydns-data.c 2012-12-06 22:39:13.000000000 +0100 +@@ -436,7 +436,7 @@ + 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 -rNU3 djbdns-1.05.tds-base/tinydns-get.c djbdns-1.05.tinydnssec/tinydns-get.c +--- djbdns-1.05.tds-base/tinydns-get.c 2001-02-11 22:11:45.000000000 +0100 ++++ djbdns-1.05.tinydnssec/tinydns-get.c 2012-12-06 22:39:13.000000000 +0100 +@@ -19,7 +19,7 @@ + + 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) + { +@@ -39,6 +39,14 @@ + 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(); diff --git a/pack/djbdns/tinydns-sign.pl b/pack/djbdns/tinydns-sign.pl @@ -0,0 +1,1025 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2012,2015 Peter Conrad <conrad@quisquis.de> + +# 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 <http://www.gnu.org/licenses/>. + +use strict; +use Fcntl; +use Digest::SHA1 qw(sha1 sha1_hex); +use Crypt::OpenSSL::RSA; +use MIME::Base64; + +$main::ttl = 432000; +%main::keys = (); +%main::names = (); +@main::zones = (); +%main::locs = (); +$main::catchAllLoc = undef; + +while ($#ARGV >= 0) { + if ($ARGV[0] eq '-h' || $ARGV[0] eq '--help') { + &usage(); + # usage does not return + } elsif ($ARGV[0] eq '-t') { + if ($#ARGV < 1) { &usage(); } + $main::ttl = $ARGV[1]; + shift @ARGV; + } elsif ($ARGV[0] eq '-g') { + if ($#ARGV != 5) { &usage(); } + &genkey($ARGV[1], $ARGV[2], $ARGV[3], $ARGV[4], $ARGV[5]); + # genkey does not return + exit(0); + } else { # keyfile + &addKey($ARGV[0]); + } + shift @ARGV; +} + +my $now = time(); +while ($_ = <STDIN>) { + s/\s+$//s; + if (/^:[^:]*:(43|46|48|50|51|6528[12]):/) { next; } + if (/^$/ || /^-/ || /^#[^KDP]/) { + print "$_\n"; + next; + } + if (/^\./) { + print "# Was: $_\n"; + while (! /^\.([^:]*):([0-9.]*):([^:]+):(\d*):(\d*):([^:]*)$/) { $_ .= ':'; } + my ($dom,$ip,$ns,$ttl,$ts,$lo) = /^\.([^:]*):([0-9.]*):([^:]+):(\d*):(\d*):([^:]*)$/; + # &fqdn:ip:x:ttl:timestamp:lo + print "\&$dom:$ip:$ns:$ttl:$ts:$lo\n"; + $dom =~ tr/A-Z/a-z/; + $ns =~ tr/A-Z/a-z/; + if ($ns !~ /\./) { $ns .= ".ns.$dom"; } + my $rec = &getOrCreateRRs($dom); + $rec->addNS($ns, $ttl, $ts, $lo); + &addA($ns, $ip, $ttl || 259200, $ts, $lo); + if (exists($rec->{byType}->{6})) { next; } # don't create more than one SOA per zone + # Zfqdn:mname:rname:ser:ref:ret:exp:min:ttl:timestamp:lo + $_ = "Z$dom:$ns:hostmaster.${dom}:::::$ttl:$ts:$lo\n"; + } + if (/^Z/) { + my @fields = split /:/; + if ($#fields < 3 || $fields[3] !~ /^00/) { + $fields[3] = $now; + $_ = join(":", @fields); + } + } + print "$_\n"; + if (/^%(\w\w?):?/) { + $main::locs{$1} = $'; + if ($' eq "") { + $main::catchAllLoc = $1; + } + } elsif (/^#[KDP]/) { + if (/^#K([^:]*):(\d+):(\d*):(\d+):([^:]*):(\d*):(\d*):([^:]*)$/) { + # C<#Kname:flags:proto:algorithm:key:ttl:timestamp:lo> + my ($dom,$flags,$proto,$alg,$key,$ttl,$ts,$lo) = ($1, $2, $3, $4, $5, $6, $7, $8); + $dom =~ tr/A-Z/a-z/; + my $pubkey = decode_base64($key); + my $rdata = &htons($flags).chr($proto).chr($alg).$pubkey; + &makeGenericRecord($dom, 48, $rdata, $ttl, $ts, $lo); + my $rrs = &getOrCreateRRs($dom); + $rrs->addKey($flags, $alg, $pubkey); + $rrs->addRecord(48, $rdata, $ttl, $ts, $lo); + } elsif (/^#D([^:]*):(\d+):(\d+):(\d+):([0-9a-fA-F]*):(\d*):(\d*):([^:]*)$/) { + # C<#Dname:tag:algorithm:digest:fingerprint:ttl:timestamp:lo> + my ($dom,$tag,$alg,$dig,$fp,$ttl,$ts,$lo) = ($1, $2, $3, $4, $5, $6, $7, $8); + $dom =~ tr/A-Z/a-z/; + my $rdata = &htons($tag).chr($alg).chr($dig).pack("H*", $fp); + &makeGenericRecord($dom, 43, $rdata, $ttl, $ts, $lo); + &getOrCreateRRs($dom)->addRecord(43, $rdata, $ttl, $ts, $lo); + } elsif (/^#P([^:]*):(\d+):(\d+):(\d+):(\d*):([0-9a-fA-F]*):(\d*):(\d*):([^:]*)$/) { + # C<#Pname:algorithm:flags:iter:len:salt:ttl:timestamp:lo> + my ($dom,$alg,$flag,$iter,$len,$salt,$ttl,$ts,$lo) = ($1, $2, $3, $4, $5, $6, $7, $8, $9); + $dom =~ tr/A-Z/a-z/; + if ($salt eq "") { + if ($len eq "") { $len = 4; } + open(RANDOM, "</dev/urandom") or die("Failed to open /dev/urandom"); + sysread(RANDOM, $salt, $len); + close(RANDOM); + $salt = unpack("H*", $salt); + } else { + $len = length(pack("H*", $salt)); + } + my $rdata = chr($alg).chr($flag).&htons($iter) + .chr($len).pack("H*", $salt); + &makeGenericRecord($dom, 51, $rdata, $ttl, $ts, $lo); + my $rrs = &getOrCreateRRs($dom); + $rrs->addNS3P($alg, $flag, $iter, $salt, $ttl, $ts, $lo); + $rrs->addRecord(51, $rdata, $ttl, $ts, $lo); + } else { + print STDERR "Warning: ignored incomplete pseudo record '$_'!\n"; + } + next; + } + my $type = substr($_, 0, 1); + if ($type !~ /^[.\&=+\@'^CZ:36]/) { + print STDERR "Warning: ignored unknown record type '$type'\n"; + next; + } + my @stuff = split(/:/, substr($_, 1)); + if ($#stuff < 0) { + print STDERR "Warning: ignored empty record '$_'\n"; + next; + } + my $dom = shift @stuff; + $dom =~ tr/A-Z/a-z/; + my $rec = &getOrCreateRRs($dom); + if ($type eq '&') { + # &fqdn:ip:x:ttl:timestamp:lo + my $ns = $stuff[1]; + if ($ns !~ /\./) { $ns .= ".ns.$dom"; } + $rec->addNS($ns, $stuff[2], $stuff[3], $stuff[4]); + &addA($ns, $stuff[0], $stuff[2] || 259200, $stuff[3], $stuff[4]); + } elsif ($type eq '=') { + # =fqdn:ip:ttl:timestamp:lo + $rec->addA($stuff[0], $stuff[1], $stuff[2], $stuff[3]); + &addPTR($stuff[0], $dom, $stuff[1], $stuff[2], $stuff[3]); + } elsif ($type eq '+') { + # +fqdn:ip:ttl:timestamp:lo + $rec->addA($stuff[0], $stuff[1], $stuff[2], $stuff[3]); + } elsif ($type eq '6') { + # =fqdn:ip6:ttl:timestamp:lo + $rec->addAAAA($stuff[0], $stuff[1], $stuff[2], $stuff[3]); + &addPTR($stuff[0], $dom, $stuff[1], $stuff[2], $stuff[3]); + } elsif ($type eq '3') { + # +fqdn:ip6:ttl:timestamp:lo + $rec->addAAAA($stuff[0], $stuff[1], $stuff[2], $stuff[3]); + } elsif ($type eq '@') { + # @fqdn:ip:x:dist:ttl:timestamp:lo + my $mx = $stuff[1]; + if ($mx !~ /\./) { $mx .= ".mx.$dom"; } + $rec->addMX($mx, $stuff[2], $stuff[3], $stuff[4], $stuff[5]); + &addA($mx, $stuff[0], $stuff[3], $stuff[4], $stuff[5]); + } elsif ($type eq "'") { + # 'fqdn:s:ttl:timestamp:lo + $rec->addTXT(&parseData($stuff[0]), $stuff[1], $stuff[2], $stuff[3]); + } elsif ($type eq '^') { + # ^fqdn:p:ttl:timestamp:lo + $rec->addPTR($stuff[0], $stuff[1], $stuff[2], $stuff[3]); + } elsif ($type eq 'C') { + # Cfqdn:p:ttl:timestamp:lo + $rec->addCNAME($stuff[0], $stuff[1], $stuff[2], $stuff[3]); + } elsif ($type eq 'Z') { + # Zfqdn:mname:rname:ser:ref:ret:exp:min:ttl:timestamp:lo + $rec->addSOA($stuff[0], $stuff[1], $stuff[2], $stuff[3], $stuff[4], $stuff[5], $stuff[6], $stuff[7], $stuff[8], $stuff[9]); + } elsif ($type eq ':') { + # :fqdn:n:rdata:ttl:timestamp:lo + $rec->addRecord($stuff[0], &parseData($stuff[1]), $stuff[2], $stuff[3], $stuff[4]); + } +} + +foreach my $rec (values %main::names) { + my $dom = $rec->{name}; + if (exists($rec->{byType}->{48})) { + if (!exists($rec->{byType}->{6})) { + print STDERR "Warning: useless DNSKEY for $dom without SOA!\n"; + } elsif (!exists($rec->{byType}->{51})) { + print STDERR "ERROR: DNSKEY for $dom without NSEC3PARAM!\n"; + exit(1); + } + } elsif (exists($rec->{byType}->{51})) { + print STDERR "Warning: useless NSEC3PARAM for $dom without DNSKEY!\n"; + } + &findControl($rec); +} + +foreach my $zone (@main::zones) { + my $rec = $main::names{$zone}; + if (!exists($rec->{keys})) { + print STDERR "Info: ignore unsigned zone $zone\n"; + foreach my $dom (keys %{$rec->{zone}}) { + delete $main::names{$dom}; + } + next; + } + if (!exists($rec->{nsec3p})) { + next; + } + + # + # Add NSEC3 RRs to zone + my %hashes = (); + my $n3p = $rec->{nsec3p}->[0]; + # Generate hashes + foreach my $name (keys %{$rec->{zone}}) { + my $rr; + LOOP: { do { + $rr = &getOrCreateRRs($name); + if (exists($rr->{hash})) { last; } + $rec->{zone}->{$name} = $rr; + my $hash = &nsec3hash($name, $n3p->{iterations}, $n3p->{salt}); + if (exists($hashes{$hash})) { + print STDERR "ERROR: hash collision on $name!?\n"; + exit(1); + } + $hashes{$hash} = { hash => $hash, name => &base32hex($hash).".".$zone, + generator => $name, zone => $zone }; + $rr->{hash} = $hash; + if ($name eq $zone) { last; } + if ($name =~ /^[^.]*\./) { $name = $'; } + } while ($rr->{name} ne $zone); } + } + # Find next hash in hash sort order + my ($first, $prev); + my @byNibble = (); + my $prevNib = -1; + foreach my $hash (sort keys %hashes) { + my $nib = ord($hash) >> 4; + if (!defined($byNibble[$nib])) { + while ($prevNib < $nib) { + $byNibble[++$prevNib] = defined($prev) ? [$prev->{hash}] : []; + } + } + push @{$byNibble[$nib]}, $hash; + if (!$first) { + $first = $prev = $hashes{$hash}; + next; + } + $prev->{next} = $hash; + $prev = $hashes{$hash}; + } + $prev->{next} = $first->{hash}; + unshift @{$byNibble[0]}, $prev->{hash}; + for (my $nib = 1; $#{$byNibble[$nib]} < 0; $nib++) { + unshift @{$byNibble[$nib]}, $prev->{hash}; + } + for (my $nib = $#byNibble + 1; $nib < 16; $nib++) { + $byNibble[$nib] = [$prev->{hash}]; + } + # Generate records + foreach my $hash (values %hashes) { + my $rRec = &getOrCreateRRs($hash->{name}); + my $gRec = &getOrCreateRRs($hash->{generator}); + $rec->{zone}->{$hash->{name}} = $rRec; + my $rdata = chr($n3p->{alg}).chr($n3p->{flags}) + .&htons($n3p->{iterations}) + .chr(length($n3p->{salt})).$n3p->{salt} + .chr(length($hash->{next})).$hash->{next} + .&genTypeBitmaps(keys %{$gRec->{byType}}); + print "# NSEC3: ".$hash->{name}." - ".&base32hex($hash->{next})." (" + .join(" ", sort keys %{$gRec->{byType}}).")\n"; + $rRec->addRecord(50, $rdata, $n3p->{ttl}, $n3p->{ts}, $n3p->{lo}); + &makeGenericRecord($hash->{name}, 50, $rdata, $n3p->{ttl}, $n3p->{ts}, $n3p->{lo}); + print "# H(".$hash->{generator}.") -> ".$hash->{name}."\n"; + #$gRec->addRecord(65281, &wireName($hash->{name}), $n3p->{ttl}, $n3p->{ts}, $n3p->{lo}); + &makeGenericRecord($hash->{generator}, 65281, &wireName($hash->{name}), $n3p->{ttl}, $n3p->{ts}, $n3p->{lo}); + } + for (my $nib = 0; $nib < 16; $nib++) { + my $name = sprintf("%x", $nib).".$zone"; + my $rec = &getOrCreateRRs($name); + $rec->addRecord(65282, join("", @{$byNibble[$nib]}), $n3p->{ttl}, $n3p->{ts}, $n3p->{lo}); + &makeGenericRecord($name, 65282, join("", @{$byNibble[$nib]}), $n3p->{ttl}, $n3p->{ts}, $n3p->{lo}); + } + + # Add RRSIGs to zone + my (@zsks, @ksks); + if (!$rec->{haveSEP} || !$rec->{haveZSK}) { + @zsks = @ksks = @{$rec->{keys}}; + } else { + @ksks = grep { $_->{flags} & 1 } @{$rec->{keys}}; + @zsks = grep { !($_->{flags} & 1) } @{$rec->{keys}}; + } + my $validFrom = $now - 3600; + my $validUntil = $now + $main::ttl; + foreach my $owner (values %{$rec->{zone}}) { + my $name = $owner->{name}; + my $isCutpoint = exists($owner->{byType}->{2}) && $name ne $zone; + foreach my $type (keys %{$owner->{byType}}) { + if ($isCutpoint && $type != 43 && $type != 50) { next; } + my $labels = $name; + $labels =~ s/^\*\.//; + $labels =~ s/[^.]+//g; + $labels = length($labels) + 1; + my $ttl = $owner->{byType}->{$type}->[0]->{ttl} || 86400; + foreach my $key ($type == 48 ? @ksks : @zsks) { + my $keytag = $key->{key}->{basetag} + $key->{alg} + $key->{flags}; + $keytag = ($keytag + ($keytag >> 16)) & 0xffff; + my $rdata = &htons($type).chr($key->{alg}).chr($labels) + .&htonl($ttl).&htonl($validUntil) + .&htonl($validFrom) + .&htons($keytag).&wireName($zone); + my $toSign = $rdata; + foreach my $rr (sort { $a->{rdata} cmp $b->{rdata} } @{$owner->{byType}->{$type}}) { + $toSign .= &wireName($name).&htons($type)."\0\1".&htonl($ttl) + .&htons(length($rr->{rdata})).$rr->{rdata}; + } + $key->{key}->{key}->use_pkcs1_padding(); + if ($key->{alg} == 7) { + $key->{key}->{key}->use_sha1_hash(); + } elsif ($key->{alg} == 8) { + $key->{key}->{key}->use_sha256_hash(); + } elsif ($key->{alg} == 10) { + $key->{key}->{key}->use_sha512_hash(); + } else { + print STDERR "Unsupported key type ".$key->{alg}."\n"; + exit 1; + } + my $sig = $key->{key}->{key}->sign($toSign); + if (!$sig || !$key->{key}->{key}->verify($toSign, $sig)) { + print STDERR "Failed to sign $name ($type)!?\n"; + exit 1; + } + #$key->{key}->{key}->use_no_padding(); + #print "# ".unpack("H*", $key->{key}->{key}->public_decrypt($sig))."\n"; + $rdata .= $sig; + print "# RRSIG $type ".$key->{alg}." $labels $ttl $validUntil $validFrom " + ."$keytag $name\n"; + &makeGenericRecord($name, 46, $rdata, $ttl, "", ""); + } + } + } +} + +exit(0); + +package Records; + +sub new { + my ($class, $dom) = @_; + $dom =~ tr/A-Z/a-z/; + my $self = { name => $dom, byType => {}, haveSEP => 0, haveZSK => 0, + locs => {}, zone => {} }; + $self->{zone}->{$dom} = $self; + bless $self, $class; + return $self; +} + +sub addKey { +my ($self, $flags, $alg, $key) = @_; + + if (!($flags & 0x100)) { return; } + if (!exists($main::keys{$key})) { + print STDERR "ERROR: encountered DNSKEY pseudo record without a matching key:\n"; + print STDERR "$_\n"; + exit 1; + } + if ($alg != 7 && $alg != 8 && $alg != 10) { + print STDERR "Warning: ignoring DNSKEY with unsupported algorithm $alg\n"; + return; + } + my $entry = { flags => $flags, alg => $alg, pubkey => $key, + key => $main::keys{$key} }; + if (exists($self->{keys})) { + foreach my $other (@{$self->{keys}}) { + if ($other->{flags} == $flags && $other->{alg} == $alg + && $other->{pubkey} eq $key) { + return; + } + } + push @{$self->{keys}}, $entry; + } else { + $self->{keys} = [$entry]; + } + if ($flags & 1) { + $self->{haveSEP} = 1; + } else { + $self->{haveZSK} = 1; + } +} + +sub addNS3P { +my ($self, $alg, $flag, $iter, $salt, $ttl, $ts, $lo) = @_; + + if ($flag ne "0") { + print STDERR "Warning: don't know about NSEC3 flags values != 0, ignoring\n"; + } + if ($iter > 150) { + print STDERR "Warning: large iteration count $iter may lead to problems\n"; + } + if ($alg ne "1") { + print STDERR "ERROR: NSEC3 algorithm $alg unknown. Please use algorithm 1.\n"; + exit 1; + } + + my $entry = { flags => $flag, alg => $alg, iterations => $iter, + salt => pack("H*", $salt), ttl => $ttl, ts => $ts, lo => $lo }; + if (exists($self->{nsec3p})) { + print STDERR "Warning: ignoring additional NSEC3PARAM for ".$self->{name}."\n"; +# foreach my $other (@$self->{nsec3p}) { +# if ($other->{flags} == $flag && $other->{alg} == $alg +# && $other->{iterations} == $iter +# && $other->{salt} eq $entry->{salt}) { +# return; +# } +# } +# push @$self->{nsec3p}, $entry; + } else { + $self->{nsec3p} = [$entry]; + } +} + +sub addRecord { +my ($self, $type, $data, $ttl, $ts, $lo) = @_; + + if (!defined($lo)) { $lo = ""; } + if (!defined($ttl) || $ttl eq "") { $ttl = 86400; } + $self->{locs}->{$lo} = 1; + my $entry = { type => $type, rdata => $data, ttl => $ttl, ts => $ts, lo => $lo }; + if (!exists($self->{byType}->{$type})) { + $self->{byType}->{$type} = [$entry]; + return; + } + foreach my $other (@{$self->{byType}->{$type}}) { + if ($other->{rdata} eq $data + && ($other->{lo} eq $lo || $lo eq "" || $other->{lo} eq "") + && (($other->{ttl} eq "0") != ($ttl eq "0"))) { + print STDERR "Warning: duplicate record ".$self->{name}.":$type:...\n"; + return; + } + if (($other->{lo} eq $lo || $lo eq "" || $other->{lo} eq "") + && (($other->{ttl} eq "0") == ($ttl eq "0")) + && $other->{ttl} ne $ttl) { + print STDERR "Warning: ttl mismatch for ".$self->{name}.":$type:...\n"; + return; + } + } + push @{$self->{byType}->{$type}}, $entry; +} + +sub addA { +my ($self, $ip, $ttl, $ts, $lo) = @_; + + if ($ip !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { + print STDERR "Warning: ignoring unparsable IPv4 address '$ip'\n"; + return; + } + $self->addRecord(1, pack("C*", $1, $2, $3, $4), $ttl, $ts, $lo); +} + +sub addNS { +my ($self, $ns, $ttl, $ts, $lo) = @_; + + $self->addRecord(2, &toDomain($ns), $ttl || 259200, $ts, $lo); +} + +sub addCNAME { +my ($self, $nm, $ttl, $ts, $lo) = @_; + + $self->addRecord(5, &toDomain($nm), $ttl, $ts, $lo); +} + +sub addSOA { +my ($self, $ns, $hm, $ser, $ref, $ret, $exp, $min, $ttl, $ts, $lo) = @_; + + $self->addRecord(6, &toDomain($ns).&toDomain($hm) + .pack("N*", $ser || 1, $ref || 16384, $ret || 2048, + $exp || 1048576, $min || 2560), + $ttl || 2560, $ts, $lo); +} + +sub addPTR { +my ($self, $nm, $ttl, $ts, $lo) = @_; + + $self->addRecord(12, &toDomain($nm), $ttl, $ts, $lo); +} + +sub addMX { +my ($self, $mx, $dist, $ttl, $ts, $lo) = @_; + + $self->addRecord(15, pack("n", $dist || 0).&toDomain($mx), $ttl, $ts, $lo); +} + +sub addTXT { +my ($self, $txt, $ttl, $ts, $lo) = @_; + + my $cstr = ""; + while ((my $l = length($txt)) > 0) { + if ($l > 255) { $l = 255; } + $cstr .= chr($l).substr($txt, 0, $l); + $txt = substr($txt, $l); + } + $self->addRecord(16, $cstr, $ttl, $ts, $lo); +} + +sub addAAAA { +my ($self, $ip, $ttl, $ts, $lo) = @_; + + if ($ip !~ /^[0-9a-f]{32}$/i) { + print STDERR "Warning: ignoring unparsable IPv6 address '$ip'\n"; + return; + } + $self->addRecord(28, pack("H*", $ip), $ttl, $ts, $lo); +} + +sub toDomain { +my $name = shift; + + $name =~ tr/A-Z/a-z/; + my $res = ""; + while ($name =~ /^([^.]*)\./) { + $name = $'; + $res .= chr(length($1)).$1; + } + return $res.chr(length($name)).$name."\0"; +} + +package main; + +sub usage { + print STDERR "$0 -g <bits> <flags> <algorithm> <domain> <keyfile>\n"; + print STDERR " or\n"; + print STDERR "$0 [-t <ttl>] [<keyfile> ...] <input >output\n"; + exit(1); +} + +sub findControl { +my $rec = shift; + + if (exists($rec->{control})) { return; } + + my $dom = $rec->{name}; + my $soa = exists($rec->{byType}->{6}); + if ($soa) { + $rec->{control} = $dom; + $rec->{zone}->{$dom} = $rec; + push @main::zones, $dom; + return; + } + + my $anc = $dom; + while ($anc =~ /^[^.]*\./) { + $anc = $'; + if ($dom !~ /^\*\./ && exists($main::names{"*.$anc"}) + && !exists($main::names{"*.$dom"})) { + print STDERR "Warning: wildcard *.$anc shadowed by $dom (see RFC-1034 sect. 4.3.3)!\n"; + } + if (exists($main::names{$anc})) { + my $ctl_rec = $main::names{$anc}; + &findControl($ctl_rec); + if (!exists($ctl_rec->{control})) { + if (!$soa) { print STDERR "Warning: Out-of-bailiwick name '$dom' (oob parent)\n"; } + return; + } + my $control = $ctl_rec->{control}; + if ($ctl_rec->{byType}->{2}) { $control = $anc; } + $ctl_rec = $main::names{$control}; + if (!$ctl_rec->{byType}->{6}) { + # Most likely glue... + if (!exists($rec->{byType}->{1}) + && !exists($rec->{byType}->{28})) { + print STDERR "Warning: Out-of-bailiwick name '$dom' (below subdelegation)\n"; + } + } else { + $rec->{control} = $control; + $ctl_rec->{zone}->{$dom} = $rec; + } + return; + } + } + print STDERR "Warning: Out-of-bailiwick name '$dom' (no parent)\n"; +} + +sub parseData { +my $in = shift; + + my $res = ""; + while ($in =~ /^(.*?)\\([0-7]{1,3})/) { + $in = $'; + $res .= $1.chr(oct($2)); + } + return $res.$in; +} + +sub makeGenericRecord { +my ($dom, $type, $rdata, $ttl, $ts, $lo) = @_; + + print ":$dom:$type:"; + while (length($rdata)) { + my $char = substr($rdata, 0, 1); + $rdata = substr($rdata, 1); + if ($char =~ /[0-9a-zA-Z +*.,\/=<>\@\$-]/) { + print $char; + } else { + printf "\\%03o", ord($char); + } + } + print ":$ttl:$ts:$lo\n"; +} + +sub getOrCreateRRs { +my $dom = shift; + + $dom =~ tr/A-Z/a-z/; + if (!exists($main::names{$dom})) { + $main::names{$dom} = new Records($dom); + } + return $main::names{$dom}; +} + +sub htons { +my $n = shift; + + return chr($n >> 8).chr($n & 0xff); +} + +sub htonl { +my $n = shift; + + return &htons($n >> 16).&htons($n & 0xffff); +} + +sub wireName { +my $name = shift; + + my $wire = ""; + while ($name =~ /^([^.]+)/) { + $wire .= chr(length($1)).$1; + $name = ($name eq $1) ? "" : substr($name, length($1) + 1); + } + return $wire."\0"; +} + +sub nsec3hash { +my ($name, $iter, $salt) = @_; + + my $dig = &wireName($name); + while ($iter-- >= 0) { + $dig = sha1($dig.$salt); + } + return $dig; +} + +sub base32hex { +my $data = shift; + + my $buf = 0; + my $bits = 0; + my $res = ""; + while (length($data) > 0) { + $buf = ($buf << 8) | ord($data); + $data = substr($data, 1); + $bits += 8; + while ($bits >= 5) { + my $dig = ($buf >> ($bits-5)) & 0x1f; + $bits -= 5; + if ($dig < 10) { + $res .= $dig; + } else { + $res .= chr($dig + 87); + } + } + } + if ($bits > 0) { + $buf <<= (5 - $bits); + $buf &= 0x1f; + if ($buf < 10) { + $res .= $buf; + } else { + $res .= chr($buf + 87); + } + $bits += 3; + while ($bits > 0) { + if ($bits < 5) { $bits += 8; } + else { $res .= "="; $bits -= 5; } + } + } + return $res; +} + +sub tai2unix { +my $ts = shift; + + $ts =~ s/^40*//; + return "0x$ts" - 10; +} + +sub genTypeBitmaps { + my %windows = (); + if ($#_ > 0 || $#_ == 0 && $_[0] != 2) { + # The type bitmaps includes all types except those contributed by NSEC3 + # itself, including the signature for the NSEC3. I. e. if there's at + # least one record here (except for a single NS delegation), there'll + # also be an RRSIG here, eventually. + push @_, 46; + @_ = sort @_; + } + foreach my $type (@_) { + my $window = $type >> 8; + $type &= 0xff; + if (exists($windows{$window})) { + push @{$windows{$window}}, $type; + } else { + $windows{$window} = [$type]; + } + } + my $tbm = ""; + foreach my $window (sort keys %windows) { + my $wbm = "\0" x 32; + foreach my $type (@{$windows{$window}}) { + substr($wbm, $type >> 3, 1) = chr(ord(substr($wbm, $type >> 3, 1)) | 1 << (7 - ($type & 7))); + } + $wbm =~ s/\0+$//; + $tbm .= chr($window).chr(length($wbm)).$wbm; + } + return $tbm; +} + +sub gen_pubkey_data { +my ($n, $e, $flags, $alg, $dom) = @_; + + my $e_bin = $e->to_bin(); + $e_bin =~ s/^\0+//; + my $n_bin = $n->to_bin(); + $n_bin =~ s/^\0+//; + + my $pubkey = chr(length($e_bin)).$e_bin.$n_bin; + my $keytag = $flags + $alg + 3 * 256; # protocol is always 3 + for (my $i = length($pubkey) - 1; $i >= 0; $i--) { + $keytag += ord(substr($pubkey, $i)) * (($i & 1) ? 1 : 256); + } + if ($alg > 0) { + $keytag += $keytag >> 16; + $keytag &= 0xffff; + } + $dom =~ tr/A-Z/a-z/; + my $digest = sha1_hex(&wireName($dom).&htons($flags).chr(3).chr($alg).$pubkey); + return ($keytag, $pubkey, $digest); +} + +sub addA { +my ($dom, $ip, $ttl, $ts, $lo) = @_; + + if ($ip eq "") { return; } + my $rec = &getOrCreateRRs($dom); + $rec->addA($ip, $ttl, $ts, $lo); +} + +sub addPTR { +my ($ip, $dom, $ttl, $ts, $lo) = @_; + + my $arpa; + if ($ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { + $arpa = "$4.$3.$2.$1.in-addr.arpa"; + } elsif ($ip =~ /^[0-9a-f]{32}$/i) { + $ip =~ tr/A-Z/a-z/; + $arpa = "ip6.arpa"; + while ($ip =~ /^./) { + $ip = $'; + $arpa = "$&.$arpa"; + } + } else { + print STDERR "Warning: ignoring unparsable IP address '$ip'\n"; + return; + } + my $rec = getOrCreateRRs($arpa); + $rec->addPTR($dom, $ttl, $ts, $lo); +} + +sub addKey { +my ($file) = @_; + + open(KEYFILE, "<$file") or die("Can't read $file either:"); + my $rsa = Crypt::OpenSSL::RSA->new_private_key(join("", <KEYFILE>)); + close KEYFILE; + if (!$rsa) { + print STDERR "Failed to read key from $file!\n"; + exit(1); + } + my ($n, $e, $x1, $x2, $x3, $x4, $x5, $x6) = $rsa->get_key_parameters(); + $x1 = $x2 = $x3 = $x4 = $x5 = $x6 = 0; # get rid of private stuff + my ($keytag, $keydata, $fp) = &gen_pubkey_data($n, $e, 0, 0, ""); + $main::keys{$keydata} = { key => $rsa, basetag => $keytag }; +} + +sub genkey { +my ($bits, $flags, $alg, $dom, $file) = @_; + + if ($bits < 1024 || $bits > 4096) { + print STDERR "ERROR: Keys of less than 1024 or more than 4096 bits are not supported!\n"; + exit 1; + } + + if ($alg != 7 && $alg != 8 && $alg != 10) { + print STDERR "ERROR: $0 only supports algorithms 7 (RSA-SHA1), 8 (RSA-SHA256)\nand 10 (RSA-SHA512).\n"; + exit 1; + } + + if (sysopen(KEYFILE, $file, O_CREAT|O_EXCL|O_WRONLY, 0600)) { + my $rsa = Crypt::OpenSSL::RSA->generate_key($bits); + print KEYFILE $rsa->get_private_key_string(); + my ($n, $e, $x1, $x2, $x3, $x4, $x5, $x6) = $rsa->get_key_parameters(); + $x1 = $x2 = $x3 = $x4 = $x5 = $x6 = 0; # get rid of private stuff + my ($keytag, $keydata, $fp) = &gen_pubkey_data($n, $e, $flags, $alg, $dom); + print KEYFILE "#K$dom:$flags:3:$alg:".encode_base64($keydata, "").":::\n"; + print KEYFILE "#D$dom:$keytag:$alg:1:${fp}:::\n"; + close KEYFILE; + } else { + print STDERR "Warning: couldn't create $file: $!\n"; + print STDERR "Attempting to read key...\n"; + open(KEYFILE, "<$file") or die("Can't read $file either:"); + my $rsa = Crypt::OpenSSL::RSA->new_private_key(join("", <KEYFILE>)); + close KEYFILE; + if (!$rsa) { + print STDERR "Failed to read key from $file!\n"; + exit(1); + } + my ($n, $e, $x1, $x2, $x3, $x4, $x5, $x6) = $rsa->get_key_parameters(); + $x1 = $x2 = $x3 = $x4 = $x5 = $x6 = 0; # get rid of private stuff + my ($keytag, $keydata, $fp) = &gen_pubkey_data($n, $e, $flags, $alg, $dom); + print "#K$dom:$flags:3:$alg:".encode_base64($keydata, "").":::\n"; + print "#D$dom:$keytag:$alg:1:${fp}:::\n"; + } + exit 0; +} + +=pod + +=head1 NAME + +tinydns-sign - Signs records in L<tinydns-data(8)> files + +=head1 SYNOPSIS + + tinydns-sign -g bits flags algorithm domain keyfile + + tinydns-sign [-t ttl] [keyfile ...] <infile >outfile + +=head1 DESCRIPTION + +The first form is used to generate a public/private RSA key pair with a +modulus of length I<bits>. If F<keyfile> exists, tinydns-sign will try to +read a private key from the file and print DS and DNSKEY pseudo-records for +the corresponding public key on stdout. If F<keyfile> does not exist, +tinydns-sign will generate a new key pair and write the key plus the +corresponding pseudo-records to F<keyfile>. + +In the second form, tinydns-sign reads key pairs from each given F<keyfile>. +It then reads a L<tinydns-data(8)> file from STDIN and writes the same +file to STDOUT, with the following modifications: + +=over + +=item * It will delete all generic records with RRTYPE DS (43), RRSIG (46), +DNSKEY (48), NSEC3(50), NSEC3PARAM(51) and private types 65281 and 65282. + +=item * It will turn each . record into a Z record and a & record. + +=item * It will adjust the serial number of all Z records to the current time, +unless the serial number begins with two zeroes. Note that an SOA must have a +fixed serial for generating a matching RRSIG record. + +=item * It will create new DS, DNSKEY and NSEC3PARAM records from each +corresponding pseudo record (see below) present in the file. + +=item * It will create NSEC3 records for all names in all zones that have at +least one DNSKEY and NSEC3PARAM in the file. + +=item * It will create a generic record with type 65281 for each name +(including empty non-terminals) containing the owner of its matching NSEC3 RR. + +=item * It will create generic records with type 65282 for each hex digit (i. +e. 0-9a-f) below the zone apex containing all NSEC3 hashes starting with that +digit. + +=item * It will create RRSIG records for all RR-sets in all zones that have at +least one DNSKEY in the file. If both DNSKEYS with and without the SEP flag set +are present, then those with the SEP flag will be used only for RRSIGs on +DNSKEY RRs and those without the SEP flag will be used for the remaining +RR-sets. Otherwise, RRSIGs will be created using all DNSKEYs. + +RRSIGs will be valid beginning one hour in the past and ending at (now + I<ttl>) +seconds. I<ttl> defaults to 432000 (5 days). + +=back + +=head2 Pseudo-Records + +Pseudo-records are records defined in a syntax that's only understood by +tinydns-sign. To L<tinydns-data(8)> they look like comments, i. e. they are +ignored. + +tinydns-sign will create one or more generic records for each pseudo-record. +All generic records with an RR-type for which a pseudo-record can be defined +are deleted from the input. (Otherwise, removing a pseudo-record would not +result in removal of the corresponding generic record.) + +In contrast to standard tinydns-data behaviour, trailing colons in +pseudo-records are B<not> optional. + +Currently, pseudo-records are defined for the following RR-types: + +=over + +=item * #Kname:flags:proto:algorithm:key:ttl:timestamp:lo + +This generates a DNSKEY record for I<name>. I<flags>. I<proto> and I<algorithm> +are decimal numbers. At the time of writing, I<proto> must be 3. tinydns-sign +only supports I<algorithm>s 7 (RSA-SHA1), 8 (RSA-SHA256) and 10 (RSA-SHA512). +I<key> is base-64 encoded key material, depending on the selected +I<algorithm>. I<ttl>, I<timestamp> and I<lo> are as usual. + +It is an error to have a DNSKEY pseudo-record in the input without a +corresponding F<keyfile> containing the matching private key. + +=item * #Dname:tag:algorithm:digest:fingerprint:ttl:timestamp:lo + +This generates a DS record for I<name>. I<tag> is the key tag, I<algorithm> +specifies the algorithm of the referenced key and I<digest> is the digest type +(all in decimal). I<fingerprint> is the hex-encoded actual digest value +(omitting leading/trailing zeroes is not permitted!). I<ttl>, I<timestamp> and +I<lo> are as usual. + +=item * #Pname:algorithm:flags:iter:len:salt:ttl:timestamp:lo + +This generates an NSEC3PARAM record for I<name> with the given I<algorithm>, +I<flags>, I<iter>ation count, salt I<len>gth and I<salt>. If I<salt> is empty, +a new random salt with the given salt I<len>gth (4 bytes if I<len> is empty) +will be generated. If I<salt> is non-empty, it must be a string of hex digits +with even length. The salt length is derived from the given salt value, i. e. +I<len> is ignored in that case. I<ttl>, I<timestamp> and I<lo> are as usual. + +tinydns-sign currently only supports I<algorithm> 1 (SHA-1). At the time of +writing, I<flags> is defined to be 0, and the I<iter>ation count is limited +depending on the key length (see L<RFC-5155>). + +=back + +=head1 EXIT STATUS + +tinydns-sign will exit with status 0 if it thinks all went well. Warning +messages will not trigger a nonzero exit status. + +tinydns-sign will exit with nonzero status if an error occurred. In this case, +the output is most likely incomplete and should not be used to replace an +input file. + +=head1 SEE ALSO + +L<tinydns-data(8)>, +L<RFC-4034|http://tools.ietf.org/html/rfc4034>, +L<RFC-4035|http://tools.ietf.org/html/rfc4035>, +L<RFC-5155|http://tools.ietf.org/html/rfc5155> + +=head1 LIMITATIONS + +=over + +=item * Location code handling is incomplete in that location codes must be +present for all RRs in a zone, or for none at all. + +=item * Timestamps are currently mostly ignored, i. e. signatures will happily +outlive the RR-sets which they sign. + +=item * It is currently not possible to protect the private keys with a +passphrase. + +=item * It is not possible to have a signed zone and a signed child zone in +the same data file. + +=item * NSEC3 RRs with Opt-Out child zones are not supported. + +=item * The pseudo-RRs with type 65282 contain a list of hash values. The list +cannot grow bigger than 65kBytes (about 3270 hashes). This is not a problem +for a typical domain, but it would be a problem if tinydns were to serve the +.de zone, for example. Also, the list is searched sequentially, which can +cause a performance impact long before this limit is reached. + +=back + +=head1 CAVEATS + +=over + +=item * The system clock should be reasonably close to UTC (i. e. within a few minutes). + +=item * Stock tinydns/axfrdns will happily work with signed data.cdb files, +but they will not produce correct DNSSEC responses! + +=item * If a zone contains both keys with and without the SEP flag, you must +make sure that both key sets cover the same set of algorithms. This is due to +a requirement in RFC-4035 section 2.2. + +=back + +=head1 AUTHOR + +(C) 2012 Peter Conrad L<mailto:conrad@quisquis.de> + +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 <http://www.gnu.org/licenses/>. + +=cut +