diff options
32 files changed, 7156 insertions, 4361 deletions
diff --git a/usr.sbin/nsd/Makefile.bsd-wrapper b/usr.sbin/nsd/Makefile.bsd-wrapper index 2f3641e295c..e8a85475c96 100644 --- a/usr.sbin/nsd/Makefile.bsd-wrapper +++ b/usr.sbin/nsd/Makefile.bsd-wrapper @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.bsd-wrapper,v 1.8 2013/03/29 12:13:12 sthen Exp $ +# $OpenBSD: Makefile.bsd-wrapper,v 1.9 2013/11/26 12:53:57 sthen Exp $ .include <bsd.own.mk> @@ -13,20 +13,19 @@ CONFIGURE_OPTS= --prefix=/usr \ --sysconfdir=/etc \ --with-ssl=/usr \ --with-user=${USER} \ - --with-nsd-conf-file=/etc/nsd.conf \ --with-chroot=${CHROOTDIR} \ + --with-configdir=${CHROOTDIR}/etc \ --with-pidfile=${CHROOTDIR}/run/nsd.pid \ --with-zonesdir=${CHROOTDIR}/zones \ --with-dbfile=${CHROOTDIR}/db/nsd.db \ - --with-difffile=${CHROOTDIR}/run/ixfr.db \ + --with-xfrdir=${CHROOTDIR}/run/xfr \ --with-xfrdfile=${CHROOTDIR}/run/xfrd.state \ --enable-ratelimit \ --enable-root-server -PROG= nsd nsd-zonec nsd-notify nsd-checkconf nsd-patch nsd-xfer +PROG= nsd nsd-checkconf nsd-control -MAN= nsd.8 nsdc.8 nsd-zonec.8 nsd-notify.8 nsd-checkconf.8 nsd-patch.8 \ - nsd-xfer.8 nsd.conf.5 +MAN= nsd.8 nsd-checkconf.8 nsd-control.8 nsd.conf.5 all: gnu @@ -67,7 +66,7 @@ install: maninstall .endfor ${INSTALL} ${INSTALL_COPY} \ -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ - nsdc.sh ${DESTDIR}${BINDIR}/nsdc + nsd-control-setup.sh ${DESTDIR}${BINDIR}/nsd-control-setup BEFOREMAN= config.status diff --git a/usr.sbin/nsd/Makefile.in b/usr.sbin/nsd/Makefile.in index bb4d10fadb8..61d1041e628 100644 --- a/usr.sbin/nsd/Makefile.in +++ b/usr.sbin/nsd/Makefile.in @@ -1,7 +1,7 @@ # # Makefile -- one file to make them all, nsd(8) # -# Copyright (c) 2001-2011, NLnet Labs. All rights reserved. +# Copyright (c) 2001-2006, NLnet Labs. All rights reserved. # # See LICENSE for the license. # @@ -21,21 +21,25 @@ piddir = @piddir@ dbdir = @dbdir@ pidfile = @pidfile@ logfile = @logfile@ -zonestatsfile = @zonestatsfile@ dbfile = @dbfile@ -difffile = @difffile@ +xfrdir = @xfrdir@ xfrdfile = @xfrdfile@ +zonelistfile = @zonelistfile@ nsdconfigfile = @nsd_conf_file@ zonesdir = @zonesdir@ +chrootdir= @chrootdir@ user = @user@ -spriority = @start_priority@ -kpriority = @kill_priority@ + +# override $U variable which is used by autotools for deansification (for +# K&R C compilers), but causes problems if $U is defined in the env). +U= CC = @CC@ -CPPFLAGS = @CPPFLAGS@ -I. -I$(srcdir)# @DEFS@ contains -DHAVE_CONFIG_H +CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ +SSL_LIBS = @SSL_LIBS@ LIBOBJS = @LIBOBJS@ INSTALL = $(srcdir)/install-sh -c INSTALL_PROGRAM = $(INSTALL) @@ -52,287 +56,37 @@ EDIT = sed \ -e 's,@sbindir\@,$(sbindir),g' \ -e 's,@configdir\@,$(configdir),g' \ -e 's,@zonesdir\@,$(zonesdir),g' \ + -e 's,@chrootdir\@,$(chrootdir),g' \ -e 's,@pidfile\@,$(pidfile),g' \ -e 's,@logfile\@,$(logfile),g' \ - -e 's,@zonestatsfile\@,$(zonestatsfile),g' \ -e 's,@dbfile\@,$(dbfile),g' \ - -e 's,@difffile\@,$(difffile),g' \ + -e 's,@xfrdir\@,$(xfrdir),g' \ -e 's,@xfrdfile\@,$(xfrdfile),g' \ + -e 's,@zonelistfile\@,$(zonelistfile),g' \ -e 's,@nsdconfigfile\@,$(nsdconfigfile),g' \ -e 's,@shell\@,$(SHELL),g' \ - -e 's,@user\@,$(user),g' \ - -e 's,@spriority\@,$(spriority),g' \ - -e 's,@kpriority\@,$(kpriority),g' - -TARGETS = nsd nsd-zonec nsd-notify nsd-xfer nsdc.sh nsd-checkconf nsd-patch nsd.conf.sample \ - nsd.8 nsd-zonec.8 nsd-notify.8 nsd-xfer.8 nsdc.8 \ - nsd-checkconf.8 nsd-patch.8 nsd.conf.5 - -ALL_OBJECTS = \ - answer.o \ - axfr.o \ - buffer.o \ - configlexer.o \ - configparser.o \ - dbaccess.o \ - dbcreate.o \ - difffile.o \ - dname.o \ - dns.o \ - edns.o \ - ipc.o \ - iterated_hash.o \ - lookup3.o \ - namedb.o \ - netio.o \ - nsd-checkconf.o \ - nsd-notify.o \ - nsd.o \ - nsd-patch.o \ - nsd-xfer.o \ - nsec3.o \ - options.o \ - packet.o \ - query.o \ - rbtree.o \ - rdata.o \ - region-allocator.o \ - rrl.o \ - server.o \ - tsig.o \ - tsig-openssl.o \ - util.o \ - xfrd-disk.o \ - xfrd-notify.o \ - xfrd.o \ - xfrd-tcp.o \ - zlexer.o \ - zonec.o \ - zparser.o - -NSD_OBJECTS = \ - answer.o \ - axfr.o \ - buffer.o \ - configlexer.o \ - configparser.o \ - options.o \ - dbaccess.o \ - difffile.o \ - dname.o \ - dns.o \ - edns.o \ - ipc.o \ - iterated_hash.o \ - lookup3.o \ - namedb.o \ - netio.o \ - nsd.o \ - nsec3.o \ - packet.o \ - query.o \ - rbtree.o \ - rdata.o \ - region-allocator.o \ - rrl.o \ - server.o \ - tsig.o \ - tsig-openssl.o \ - util.o \ - xfrd-disk.o \ - xfrd-notify.o \ - xfrd-tcp.o \ - xfrd.o - -NSD_ZONEC_OBJECTS = \ - answer.o \ - axfr.o \ - buffer.o \ - configlexer.o \ - configparser.o \ - dbaccess.o \ - difffile.o \ - dbcreate.o \ - dname.o \ - dns.o \ - edns.o \ - iterated_hash.o \ - lookup3.o \ - namedb.o \ - nsec3.o \ - options.o \ - packet.o \ - query.o \ - rbtree.o \ - rdata.o \ - region-allocator.o \ - rrl.o \ - tsig.o \ - tsig-openssl.o \ - util.o \ - zlexer.o \ - zonec.o \ - zparser.o - -NSD_NOTIFY_OBJECTS = \ - answer.o \ - axfr.o \ - buffer.o \ - configlexer.o \ - configparser.o \ - dbaccess.o \ - dname.o \ - dns.o \ - edns.o \ - iterated_hash.o \ - lookup3.o \ - namedb.o \ - nsd-notify.o \ - nsec3.o \ - options.o \ - packet.o \ - query.o \ - rbtree.o \ - rdata.o \ - region-allocator.o \ - rrl.o \ - tsig.o \ - tsig-openssl.o \ - util.o - -NSD_XFER_OBJECTS = \ - answer.o \ - axfr.o \ - buffer.o \ - configlexer.o \ - configparser.o \ - dbaccess.o \ - dname.o \ - dns.o \ - edns.o \ - iterated_hash.o \ - lookup3.o \ - namedb.o \ - nsd-xfer.o \ - nsec3.o \ - options.o \ - packet.o \ - query.o \ - rbtree.o \ - rdata.o \ - region-allocator.o \ - rrl.o \ - tsig.o \ - tsig-openssl.o \ - util.o - -NSD_CHECKCONF_OBJECTS = \ - answer.o \ - axfr.o \ - buffer.o \ - configlexer.o \ - configparser.o \ - dbaccess.o \ - dname.o \ - dns.o \ - edns.o \ - iterated_hash.o \ - lookup3.o \ - namedb.o \ - nsd-checkconf.o \ - nsec3.o \ - options.o \ - packet.o \ - query.o \ - rbtree.o \ - rdata.o \ - region-allocator.o \ - rrl.o \ - tsig.o \ - tsig-openssl.o \ - util.o - -NSD_PATCH_OBJECTS = \ - answer.o \ - axfr.o \ - buffer.o \ - configlexer.o \ - configparser.o \ - dbcreate.o \ - dbaccess.o \ - difffile.o \ - dname.o \ - dns.o \ - edns.o \ - iterated_hash.o \ - lookup3.o \ - namedb.o \ - nsd-patch.o \ - nsec3.o \ - options.o \ - packet.o \ - query.o \ - rbtree.o \ - rdata.o \ - region-allocator.o \ - rrl.o \ - tsig.o \ - tsig-openssl.o \ - util.o - -CUTEST_OBJECTS = \ - answer.o \ - axfr.o \ - buffer.o \ - configlexer.o \ - configparser.o \ - options.o \ - dbaccess.o \ - dbcreate.o \ - difffile.o \ - dname.o \ - dns.o \ - edns.o \ - ipc.o \ - iterated_hash.o \ - lookup3.o \ - namedb.o \ - netio.o \ - nsec3.o \ - packet.o \ - query.o \ - rbtree.o \ - rdata.o \ - region-allocator.o \ - rrl.o \ - server.o \ - tsig.o \ - tsig-openssl.o \ - util.o \ - xfrd-disk.o \ - xfrd-notify.o \ - xfrd-tcp.o \ - xfrd.o \ - cutest_dname.o \ - cutest_dns.o \ - cutest_iterated_hash.o \ - cutest_run.o \ - cutest_rbtree.o \ - cutest_options.o \ - cutest_region.o \ - cutest_rrl.o \ - cutest_util.o \ - cutest.o - -all: $(TARGETS) - -$(ALL_OBJECTS): + -e 's,@user\@,$(user),g' + +TARGETS=nsd nsd-checkconf nsd-control nsd.conf.sample nsd-control-setup.sh +MANUALS=nsd.8 nsd-checkconf.8 nsd-control.8 nsd.conf.5 + +COMMON_OBJ=answer.o axfr.o buffer.o configlexer.o configparser.o dname.o dns.o edns.o iterated_hash.o lookup3.o namedb.o nsec3.o options.o packet.o query.o rbtree.o radtree.o rdata.o region-allocator.o rrl.o tsig.o tsig-openssl.o udb.o udbradtree.o udbzone.o util.o +XFRD_OBJ=xfrd-disk.o xfrd-notify.o xfrd-tcp.o xfrd.o remote.o +NSD_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) difffile.o ipc.o mini_event.o netio.o nsd.o server.o dbaccess.o dbcreate.o zlexer.o zonec.o zparser.o +ALL_OBJ=$(NSD_OBJ) nsd-checkconf.o nsd-control.o nsd-mem.o +NSD_CHECKCONF_OBJ=$(COMMON_OBJ) nsd-checkconf.o +NSD_CONTROL_OBJ=$(COMMON_OBJ) nsd-control.o +CUTEST_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o server.o zonec.o zparser.o zlexer.o cutest_dname.o cutest_dns.o cutest_iterated_hash.o cutest_run.o cutest_radtree.o cutest_rbtree.o cutest_namedb.o cutest_options.o cutest_region.o cutest_rrl.o cutest_udb.o cutest_udbrad.o cutest_util.o cutest.o qtest.o +NSD_MEM_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o server.o zonec.o zparser.o zlexer.o nsd-mem.o +all: $(TARGETS) $(MANUALS) + +$(ALL_OBJ): $(COMPILE) -c $< -nsdc.sh: $(srcdir)/nsdc.sh.in config.h - rm -f nsdc.sh - $(EDIT) $(srcdir)/nsdc.sh.in > nsdc.sh - chmod +x nsdc.sh +nsd-control-setup.sh: $(srcdir)/nsd-control-setup.sh.in config.h + rm -f nsd-control-setup.sh + $(EDIT) $(srcdir)/nsd-control-setup.sh.in > nsd-control-setup.sh + chmod +x nsd-control-setup.sh nsd.conf.sample: $(srcdir)/nsd.conf.sample.in config.h rm -f nsd.conf.sample @@ -346,29 +100,13 @@ nsd.8: $(srcdir)/nsd.8.in config.h rm -f nsd.8 $(EDIT) $(srcdir)/nsd.8.in > nsd.8 -nsdc.8: $(srcdir)/nsdc.8.in config.h - rm -f nsdc.8 - $(EDIT) $(srcdir)/nsdc.8.in > nsdc.8 - -nsd-zonec.8: $(srcdir)/zonec.8.in config.h - rm -f nsd-zonec.8 - $(EDIT) $(srcdir)/zonec.8.in > nsd-zonec.8 - -nsd-notify.8: $(srcdir)/nsd-notify.8.in config.h - rm -f nsd-notify.8 - $(EDIT) $(srcdir)/nsd-notify.8.in > nsd-notify.8 - -nsd-xfer.8: $(srcdir)/nsd-xfer.8.in config.h - rm -f nsd-xfer.8 - $(EDIT) $(srcdir)/nsd-xfer.8.in > nsd-xfer.8 - nsd-checkconf.8: $(srcdir)/nsd-checkconf.8.in config.h rm -f nsd-checkconf.8 $(EDIT) $(srcdir)/nsd-checkconf.8.in > nsd-checkconf.8 -nsd-patch.8: $(srcdir)/nsd-patch.8.in config.h - rm -f nsd-patch.8 - $(EDIT) $(srcdir)/nsd-patch.8.in > nsd-patch.8 +nsd-control.8: $(srcdir)/nsd-control.8.in config.h + rm -f nsd-control.8 + $(EDIT) $(srcdir)/nsd-control.8.in > nsd-control.8 install: @@ -376,62 +114,52 @@ orig-install: all $(INSTALL) -d $(DESTDIR)$(sbindir) $(INSTALL) -d $(DESTDIR)$(configdir) $(INSTALL) -d $(DESTDIR)$(piddir) + $(INSTALL) -d $(DESTDIR)$(xfrdir) $(INSTALL) -d $(DESTDIR)$(dbdir) $(INSTALL) -d $(DESTDIR)$(mandir) $(INSTALL) -d $(DESTDIR)$(mandir)/man8 $(INSTALL) -d $(DESTDIR)$(mandir)/man5 $(INSTALL) nsd $(DESTDIR)$(sbindir)/nsd - $(INSTALL) nsd-zonec $(DESTDIR)$(sbindir)/nsd-zonec - $(INSTALL) nsdc.sh $(DESTDIR)$(sbindir)/nsdc - $(INSTALL) nsd-notify $(DESTDIR)$(sbindir)/nsd-notify + $(INSTALL) nsd-control-setup.sh $(DESTDIR)$(sbindir)/nsd-control-setup $(INSTALL) nsd-checkconf $(DESTDIR)$(sbindir)/nsd-checkconf - $(INSTALL) nsd-patch $(DESTDIR)$(sbindir)/nsd-patch - $(INSTALL) nsd-xfer $(DESTDIR)$(sbindir)/nsd-xfer + $(INSTALL) nsd-control $(DESTDIR)$(sbindir)/nsd-control $(INSTALL_DATA) $(srcdir)/nsd.8 $(DESTDIR)$(mandir)/man8 - $(INSTALL_DATA) $(srcdir)/nsdc.8 $(DESTDIR)$(mandir)/man8 - $(INSTALL_DATA) $(srcdir)/nsd-zonec.8 $(DESTDIR)$(mandir)/man8/nsd-zonec.8 - $(INSTALL_DATA) $(srcdir)/nsd-notify.8 $(DESTDIR)$(mandir)/man8/nsd-notify.8 $(INSTALL_DATA) $(srcdir)/nsd-checkconf.8 $(DESTDIR)$(mandir)/man8/nsd-checkconf.8 - $(INSTALL_DATA) $(srcdir)/nsd-patch.8 $(DESTDIR)$(mandir)/man8/nsd-patch.8 - $(INSTALL_DATA) $(srcdir)/nsd-xfer.8 $(DESTDIR)$(mandir)/man8/nsd-xfer.8 + $(INSTALL_DATA) $(srcdir)/nsd-control.8 $(DESTDIR)$(mandir)/man8/nsd-control.8 $(INSTALL_DATA) $(srcdir)/nsd.conf.5 $(DESTDIR)$(mandir)/man5/nsd.conf.5 $(INSTALL_DATA) nsd.conf.sample $(DESTDIR)$(nsdconfigfile).sample uninstall: @echo - rm -f -- $(DESTDIR)$(sbindir)/nsd $(DESTDIR)$(sbindir)/nsd-zonec $(DESTDIR)$(sbindir)/nsdc $(DESTDIR)$(sbindir)/nsd-notify $(DESTDIR)$(sbindir)/nsd-xfer $(DESTDIR)$(sbindir)/nsd-checkconf $(DESTDIR)$(sbindir)/nsd-patch - rm -f -- $(DESTDIR)$(mandir)/man8/nsd.8 $(DESTDIR)$(mandir)/man8/nsdc.8 $(DESTDIR)$(mandir)/man8/nsd-zonec.8 $(DESTDIR)$(mandir)/man8/nsd-notify.8 $(DESTDIR)$(mandir)/man8/nsd-xfer.8 $(DESTDIR)$(mandir)/man5/nsd.conf.5 - rm -f -- $(DESTDIR)$(mandir)/man8/nsd-checkconf.8 $(DESTDIR)$(mandir)/man8/nsd-patch.8 - rm -f -- $(DESTDIR)$(pidfile) $(DESTDIR)$(dbfile) + rm -f -- $(DESTDIR)$(sbindir)/nsd $(DESTDIR)$(sbindir)/nsd-control-setup $(DESTDIR)$(sbindir)/nsd-checkconf $(DESTDIR)$(sbindir)/nsd-control + rm -f -- $(DESTDIR)$(mandir)/man8/nsd.8 $(DESTDIR)$(mandir)/man5/nsd.conf.5 + rm -f -- $(DESTDIR)$(mandir)/man8/nsd-checkconf.8 $(DESTDIR)$(mandir)/man8/nsd-control.8 + rm -f -- $(DESTDIR)$(pidfile) @echo - @echo "You still need to remove $(DESTDIR)$(configdir), $(DESTDIR)$(piddir), $(DESTDIR)$(dbdir) directory by hand." + @echo "You still need to remove $(DESTDIR)$(configdir), $(DESTDIR)$(piddir), $(DESTDIR)$(dbfile) directory by hand." test: -nsd: $(NSD_OBJECTS) $(LIBOBJS) - $(LINK) -o $@ $(NSD_OBJECTS) $(LIBOBJS) $(LIBS) - -nsd-zonec: $(NSD_ZONEC_OBJECTS) $(LIBOBJS) - $(LINK) -o $@ $(NSD_ZONEC_OBJECTS) $(LIBOBJS) $(LIBS) +nsd: $(NSD_OBJ) $(LIBOBJS) + $(LINK) -o $@ $(NSD_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS) -nsd-notify: $(NSD_NOTIFY_OBJECTS) $(LIBOBJS) - $(LINK) -o $@ $(NSD_NOTIFY_OBJECTS) $(LIBOBJS) $(LIBS) +nsd-checkconf: $(NSD_CHECKCONF_OBJ) $(LIBOBJS) + $(LINK) -o $@ $(NSD_CHECKCONF_OBJ) $(LIBOBJS) $(LIBS) -nsd-checkconf: $(NSD_CHECKCONF_OBJECTS) $(LIBOBJS) - $(LINK) -o $@ $(NSD_CHECKCONF_OBJECTS) $(LIBOBJS) $(LIBS) +nsd-control: $(NSD_CONTROL_OBJ) $(LIBOBJS) + $(LINK) -o $@ $(NSD_CONTROL_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS) -nsd-xfer: $(NSD_XFER_OBJECTS) $(LIBOBJS) - $(LINK) -o $@ $(NSD_XFER_OBJECTS) $(LIBOBJS) $(LIBS) +nsd-mem: $(NSD_MEM_OBJ) $(LIBOBJS) + $(LINK) -o $@ $(NSD_MEM_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS) -nsd-patch: $(NSD_PATCH_OBJECTS) $(LIBOBJS) - $(LINK) -o $@ $(NSD_PATCH_OBJECTS) $(LIBOBJS) $(LIBS) +cutest: $(CUTEST_OBJ) $(LIBOBJS) + $(LINK) -o $@ $(CUTEST_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS) -cutest: $(CUTEST_OBJECTS) - $(LINK) -o $@ $(CUTEST_OBJECTS) $(LIBOBJS) $(LIBS) +udb-inspect: udb-inspect.o $(COMMON_OBJ) $(LIBOBJS) + $(LINK) -o $@ udb-inspect.o $(COMMON_OBJ) $(LIBOBJS) $(LIBS) clean: - rm -f *.o *.so y.* *.core *.gmon tags TAGS - rm -f $(TARGETS) + rm -f *.o $(TARGETS) $(MANUALS) cutest udb-inspect nsd-mem realclean: clean rm -f Makefile config.h config.log config.status @@ -443,98 +171,118 @@ devclean: realclean rm -f config.h.in configure basename.o: $(srcdir)/compat/basename.c - $(COMPILE) -c $(srcdir)/compat/basename.c -o $@ + $(COMPILE) -c $(srcdir)/compat/basename.c inet_pton.o: $(srcdir)/compat/inet_pton.c - $(COMPILE) -c $(srcdir)/compat/inet_pton.c -o $@ + $(COMPILE) -c $(srcdir)/compat/inet_pton.c inet_ntop.o: $(srcdir)/compat/inet_ntop.c - $(COMPILE) -c $(srcdir)/compat/inet_ntop.c -o $@ + $(COMPILE) -c $(srcdir)/compat/inet_ntop.c inet_aton.o: $(srcdir)/compat/inet_aton.c - $(COMPILE) -c $(srcdir)/compat/inet_aton.c -o $@ + $(COMPILE) -c $(srcdir)/compat/inet_aton.c b64_pton.o: $(srcdir)/compat/b64_pton.c - $(COMPILE) -c $(srcdir)/compat/b64_pton.c -o $@ + $(COMPILE) -c $(srcdir)/compat/b64_pton.c b64_ntop.o: $(srcdir)/compat/b64_ntop.c - $(COMPILE) -c $(srcdir)/compat/b64_ntop.c -o $@ + $(COMPILE) -c $(srcdir)/compat/b64_ntop.c memcmp.o: $(srcdir)/compat/memcmp.c - $(COMPILE) -c $(srcdir)/compat/memcmp.c -o $@ + $(COMPILE) -c $(srcdir)/compat/memcmp.c memmove.o: $(srcdir)/compat/memmove.c - $(COMPILE) -c $(srcdir)/compat/memmove.c -o $@ + $(COMPILE) -c $(srcdir)/compat/memmove.c snprintf.o: $(srcdir)/compat/snprintf.c - $(COMPILE) -c $(srcdir)/compat/snprintf.c -o $@ + $(COMPILE) -c $(srcdir)/compat/snprintf.c strlcat.o: $(srcdir)/compat/strlcat.c - $(COMPILE) -c $(srcdir)/compat/strlcat.c -o $@ + $(COMPILE) -c $(srcdir)/compat/strlcat.c strlcpy.o: $(srcdir)/compat/strlcpy.c - $(COMPILE) -c $(srcdir)/compat/strlcpy.c -o $@ + $(COMPILE) -c $(srcdir)/compat/strlcpy.c strptime.o: $(srcdir)/compat/strptime.c - $(COMPILE) -c $(srcdir)/compat/strptime.c -o $@ + $(COMPILE) -c $(srcdir)/compat/strptime.c vsnprintf.o: $(srcdir)/compat/vsnprintf.c - $(COMPILE) -c $(srcdir)/compat/vsnprintf.c -o $@ + $(COMPILE) -c $(srcdir)/compat/vsnprintf.c timegm.o: $(srcdir)/compat/timegm.c - $(COMPILE) -c $(srcdir)/compat/timegm.c -o $@ + $(COMPILE) -c $(srcdir)/compat/timegm.c malloc.o: $(srcdir)/compat/malloc.c - $(COMPILE) -c $(srcdir)/compat/malloc.c -o $@ + $(COMPILE) -c $(srcdir)/compat/malloc.c pselect.o: $(srcdir)/compat/pselect.c - $(COMPILE) -c $(srcdir)/compat/pselect.c -o $@ + $(COMPILE) -c $(srcdir)/compat/pselect.c fake-rfc2553.o: $(srcdir)/compat/fake-rfc2553.c - $(COMPILE) -c $(srcdir)/compat/fake-rfc2553.c -o $@ + $(COMPILE) -c $(srcdir)/compat/fake-rfc2553.c cutest_dname.o: $(srcdir)/tpkg/cutest/cutest_dname.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_dname.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_dname.c cutest_dns.o: $(srcdir)/tpkg/cutest/cutest_dns.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_dns.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_dns.c cutest_iterated_hash.o: $(srcdir)/tpkg/cutest/cutest_iterated_hash.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_iterated_hash.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_iterated_hash.c cutest_run.o: $(srcdir)/tpkg/cutest/cutest_run.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_run.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_run.c cutest_rbtree.o: $(srcdir)/tpkg/cutest/cutest_rbtree.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_rbtree.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_rbtree.c -cutest_rrl.o: $(srcdir)/tpkg/cutest/cutest_rrl.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_rrl.c -o $@ +cutest_radtree.o: $(srcdir)/tpkg/cutest/cutest_radtree.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_radtree.c + +cutest_namedb.o: $(srcdir)/tpkg/cutest/cutest_namedb.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_namedb.c cutest_options.o: $(srcdir)/tpkg/cutest/cutest_options.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_options.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_options.c cutest_region.o: $(srcdir)/tpkg/cutest/cutest_region.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_region.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_region.c + +cutest_rrl.o: $(srcdir)/tpkg/cutest/cutest_rrl.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_rrl.c + +cutest_udb.o: $(srcdir)/tpkg/cutest/cutest_udb.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_udb.c + +cutest_udbrad.o: $(srcdir)/tpkg/cutest/cutest_udbrad.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_udbrad.c cutest_util.o: $(srcdir)/tpkg/cutest/cutest_util.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_util.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_util.c cutest.o: $(srcdir)/tpkg/cutest/cutest.c - $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest.c -o $@ + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest.c + +qtest.o: $(srcdir)/tpkg/cutest/qtest.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/qtest.c + +udb-inspect.o: $(srcdir)/tpkg/cutest/udb-inspect.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/udb-inspect.c zlexer.c: $(srcdir)/zlexer.lex - rm -f $@ - echo '#include "config.h"' > $@ - $(LEX) -i -t $(srcdir)/zlexer.lex >> $@ + if test "$(LEX)" != ":"; then rm -f $@ ;\ + echo '#include "config.h"' > $@ ;\ + $(LEX) -i -t $(srcdir)/zlexer.lex >> $@ ;\ + fi zparser.c zparser.h: $(srcdir)/zparser.y $(YACC) -d -o zparser.c $(srcdir)/zparser.y configlexer.c: $(srcdir)/configlexer.lex - rm -f $@ - echo '#include "configyyrename.h"' > $@ - $(LEX) -i -t $(srcdir)/configlexer.lex >> $@ + if test "$(LEX)" != ":"; then rm -f $@ ;\ + echo '#include "configyyrename.h"' > $@ ;\ + $(LEX) -i -t $(srcdir)/configlexer.lex >> $@ ;\ + fi configparser.c configparser.h: $(srcdir)/configparser.y $(YACC) -d -o configparser.c $(srcdir)/configparser.y @@ -557,13 +305,13 @@ DEPEND_TARGET2=Makefile.in depend: (cd $(srcdir) ; $(CC) -MM $(CPPFLAGS) *.c compat/*.c tpkg/cutest/*.c) | \ sed -e 's? *\([^ ]*\.[ch]\)? $$(srcdir)/\1?g' | \ - sed -e 's?$$(srcdir)/config.h?config.h?' \ - -e 's?$$(srcdir)/configlexer.c?configlexer.c?' \ - -e 's?$$(srcdir)/configparser.c?configparser.c?' \ - -e 's?$$(srcdir)/configparser.h?configparser.h?' \ - -e 's?$$(srcdir)/zlexer.c?zlexer.c?' \ - -e 's?$$(srcdir)/zparser.c?zparser.c?' \ - -e 's?$$(srcdir)/zparser.h?zparser.h?' \ + sed -e 's?$$(srcdir)/config.h?config.h?g' \ + -e 's?$$(srcdir)/configlexer.c?configlexer.c?g' \ + -e 's?$$(srcdir)/configparser.c?configparser.c?g' \ + -e 's?$$(srcdir)/configparser.h?configparser.h?g' \ + -e 's?$$(srcdir)/zlexer.c?zlexer.c?g' \ + -e 's?$$(srcdir)/zparser.c?zparser.c?g' \ + -e 's?$$(srcdir)/zparser.h?zparser.h?g' \ > $(DEPEND_TMP) cp $(DEPEND_TARGET) $(DEPEND_TMP2) head -`egrep -n "# Dependencies" $(DEPEND_TARGET) | tail -1 | sed -e 's/:.*$$//'` $(DEPEND_TMP2) > $(DEPEND_TARGET) @@ -579,100 +327,111 @@ depend: # Dependencies answer.o: $(srcdir)/answer.c config.h $(srcdir)/answer.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/rbtree.h $(srcdir)/packet.h $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/tsig.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/packet.h $(srcdir)/query.h $(srcdir)/nsd.h \ + $(srcdir)/edns.h $(srcdir)/tsig.h axfr.o: $(srcdir)/axfr.c config.h $(srcdir)/axfr.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/rbtree.h $(srcdir)/packet.h \ - $(srcdir)/tsig.h $(srcdir)/options.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \ + $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/options.h buffer.o: $(srcdir)/buffer.c config.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h configlexer.o: configlexer.c $(srcdir)/configyyrename.h config.h $(srcdir)/options.h \ $(srcdir)/region-allocator.h $(srcdir)/rbtree.h configparser.h configparser.o: configparser.c config.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ - $(srcdir)/rbtree.h $(srcdir)/util.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/dns.h $(srcdir)/nsd.h \ - $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/configyyrename.h + $(srcdir)/rbtree.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/tsig.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dns.h \ + $(srcdir)/radtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/configyyrename.h dbaccess.o: $(srcdir)/dbaccess.c config.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/rbtree.h $(srcdir)/options.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/options.h $(srcdir)/rdata.h $(srcdir)/udb.h \ + $(srcdir)/udbradtree.h $(srcdir)/udbzone.h $(srcdir)/zonec.h $(srcdir)/nsec3.h $(srcdir)/difffile.h dbcreate.o: $(srcdir)/dbcreate.c config.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h config.h $(srcdir)/dns.h $(srcdir)/rbtree.h -difffile.o: $(srcdir)/difffile.c config.h $(srcdir)/difffile.h $(srcdir)/rbtree.h \ - $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/options.h \ - $(srcdir)/packet.h $(srcdir)/rdata.h $(srcdir)/nsec3.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/udb.h $(srcdir)/udbradtree.h \ + $(srcdir)/udbzone.h $(srcdir)/options.h +difffile.o: $(srcdir)/difffile.c config.h $(srcdir)/difffile.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h \ + $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/udb.h \ + $(srcdir)/xfrd-disk.h $(srcdir)/packet.h $(srcdir)/rdata.h $(srcdir)/udbzone.h $(srcdir)/udbradtree.h $(srcdir)/nsec3.h $(srcdir)/nsd.h $(srcdir)/edns.h \ + $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/tsig.h dname.o: $(srcdir)/dname.c config.h $(srcdir)/dns.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h \ - $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h + $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h dns.o: $(srcdir)/dns.c config.h $(srcdir)/dns.h $(srcdir)/zonec.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/rbtree.h zparser.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h zparser.h edns.o: $(srcdir)/edns.c config.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h ipc.o: $(srcdir)/ipc.c config.h $(srcdir)/ipc.h $(srcdir)/netio.h $(srcdir)/region-allocator.h $(srcdir)/buffer.h $(srcdir)/util.h \ - $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/dns.h $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/nsd.h \ - $(srcdir)/edns.h $(srcdir)/xfrd-notify.h + $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/options.h \ + $(srcdir)/tsig.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/xfrd-notify.h $(srcdir)/difffile.h $(srcdir)/udb.h iterated_hash.o: $(srcdir)/iterated_hash.c config.h $(srcdir)/iterated_hash.h lookup3.o: $(srcdir)/lookup3.c config.h $(srcdir)/lookup3.h +mini_event.o: $(srcdir)/mini_event.c config.h namedb.o: $(srcdir)/namedb.c config.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h \ - $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/rbtree.h + $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsec3.h netio.o: $(srcdir)/netio.c config.h $(srcdir)/netio.h $(srcdir)/region-allocator.h $(srcdir)/util.h nsd.o: $(srcdir)/nsd.c config.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h \ - $(srcdir)/util.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/rbtree.h $(srcdir)/options.h $(srcdir)/tsig.h + $(srcdir)/util.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/tsig.h $(srcdir)/dname.h $(srcdir)/remote.h nsd-checkconf.o: $(srcdir)/nsd-checkconf.c config.h $(srcdir)/tsig.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h config.h $(srcdir)/dname.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/rrl.h \ - $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h -nsd-notify.o: $(srcdir)/nsd-notify.c config.h $(srcdir)/tsig.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h \ - $(srcdir)/util.h config.h $(srcdir)/dname.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/dns.h $(srcdir)/rbtree.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/buffer.h $(srcdir)/packet.h $(srcdir)/tsig.h -nsd-patch.o: $(srcdir)/nsd-patch.c config.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ - $(srcdir)/rbtree.h $(srcdir)/difffile.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h -nsd-xfer.o: $(srcdir)/nsd-xfer.c config.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h \ - $(srcdir)/util.h config.h $(srcdir)/dns.h $(srcdir)/packet.h $(srcdir)/namedb.h $(srcdir)/rbtree.h $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h \ - $(srcdir)/tsig.h $(srcdir)/tsig-openssl.h -nsec3.o: $(srcdir)/nsec3.c config.h $(srcdir)/nsec3.h $(srcdir)/iterated_hash.h $(srcdir)/namedb.h \ - $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h \ - $(srcdir)/answer.h $(srcdir)/packet.h $(srcdir)/query.h $(srcdir)/tsig.h -options.o: $(srcdir)/options.c config.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ - $(srcdir)/rbtree.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/nsd.h $(srcdir)/edns.h \ - $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/rrl.h $(srcdir)/configyyrename.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/rrl.h $(srcdir)/query.h \ + $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h +nsd-control.o: $(srcdir)/nsd-control.c config.h $(srcdir)/util.h $(srcdir)/tsig.h $(srcdir)/buffer.h \ + $(srcdir)/region-allocator.h $(srcdir)/dname.h $(srcdir)/options.h $(srcdir)/rbtree.h +nsd-mem.o: $(srcdir)/nsd-mem.c config.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \ + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/tsig.h $(srcdir)/dname.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/namedb.h \ + $(srcdir)/radtree.h $(srcdir)/udb.h $(srcdir)/udbzone.h $(srcdir)/udbradtree.h +nsec3.o: $(srcdir)/nsec3.c config.h $(srcdir)/nsec3.h $(srcdir)/iterated_hash.h $(srcdir)/namedb.h $(srcdir)/dname.h \ + $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h \ + $(srcdir)/answer.h $(srcdir)/packet.h $(srcdir)/query.h $(srcdir)/tsig.h $(srcdir)/udbzone.h $(srcdir)/udb.h $(srcdir)/udbradtree.h +options.o: $(srcdir)/options.c config.h $(srcdir)/options.h $(srcdir)/region-allocator.h $(srcdir)/rbtree.h \ + $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/nsd.h $(srcdir)/edns.h \ + $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/rrl.h $(srcdir)/configyyrename.h configparser.h packet.o: $(srcdir)/packet.c config.h $(srcdir)/packet.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h config.h $(srcdir)/rbtree.h $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/tsig.h \ + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/tsig.h \ $(srcdir)/rdata.h query.o: $(srcdir)/query.c config.h $(srcdir)/answer.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/rbtree.h $(srcdir)/packet.h $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/tsig.h \ - $(srcdir)/axfr.h $(srcdir)/options.h $(srcdir)/nsec3.h config.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/packet.h $(srcdir)/query.h $(srcdir)/nsd.h \ + $(srcdir)/edns.h $(srcdir)/tsig.h $(srcdir)/axfr.h $(srcdir)/options.h $(srcdir)/nsec3.h +radtree.o: $(srcdir)/radtree.c config.h $(srcdir)/radtree.h $(srcdir)/util.h $(srcdir)/region-allocator.h rbtree.o: $(srcdir)/rbtree.c config.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h rdata.o: $(srcdir)/rdata.c config.h $(srcdir)/rdata.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/rbtree.h $(srcdir)/zonec.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/zonec.h region-allocator.o: $(srcdir)/region-allocator.c config.h $(srcdir)/region-allocator.h $(srcdir)/util.h +remote.o: $(srcdir)/remote.c config.h $(srcdir)/remote.h $(srcdir)/util.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h \ + $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/options.h \ + $(srcdir)/tsig.h $(srcdir)/xfrd-notify.h $(srcdir)/xfrd-tcp.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h \ + $(srcdir)/netio.h rrl.o: $(srcdir)/rrl.c config.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h \ - $(srcdir)/lookup3.h $(srcdir)/options.h -rrl-orig.o: $(srcdir)/rrl-orig.c config.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h \ - $(srcdir)/lookup3.h $(srcdir)/options.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h \ + $(srcdir)/tsig.h $(srcdir)/lookup3.h $(srcdir)/options.h server.o: $(srcdir)/server.c config.h $(srcdir)/axfr.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/rbtree.h $(srcdir)/packet.h \ - $(srcdir)/tsig.h $(srcdir)/netio.h $(srcdir)/xfrd.h $(srcdir)/options.h $(srcdir)/xfrd-tcp.h $(srcdir)/difffile.h $(srcdir)/nsec3.h config.h \ - $(srcdir)/ipc.h $(srcdir)/lookup3.h $(srcdir)/rrl.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \ + $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/netio.h $(srcdir)/xfrd.h $(srcdir)/options.h $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd-disk.h \ + $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/nsec3.h $(srcdir)/ipc.h $(srcdir)/remote.h $(srcdir)/lookup3.h $(srcdir)/rrl.h tsig.o: $(srcdir)/tsig.c config.h $(srcdir)/tsig.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dname.h \ - $(srcdir)/tsig-openssl.h $(srcdir)/dns.h $(srcdir)/packet.h $(srcdir)/namedb.h $(srcdir)/rbtree.h + $(srcdir)/tsig-openssl.h $(srcdir)/dns.h $(srcdir)/packet.h $(srcdir)/namedb.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/query.h $(srcdir)/nsd.h \ + $(srcdir)/edns.h tsig-openssl.o: $(srcdir)/tsig-openssl.c config.h $(srcdir)/tsig-openssl.h $(srcdir)/region-allocator.h \ $(srcdir)/tsig.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dname.h +udb.o: $(srcdir)/udb.c config.h $(srcdir)/udb.h $(srcdir)/lookup3.h $(srcdir)/util.h +udbradtree.o: $(srcdir)/udbradtree.c config.h $(srcdir)/udbradtree.h $(srcdir)/udb.h $(srcdir)/radtree.h +udbzone.o: $(srcdir)/udbzone.c config.h $(srcdir)/udbzone.h $(srcdir)/udb.h $(srcdir)/dns.h $(srcdir)/udbradtree.h $(srcdir)/util.h \ + $(srcdir)/iterated_hash.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/difffile.h $(srcdir)/rbtree.h \ + $(srcdir)/namedb.h $(srcdir)/radtree.h $(srcdir)/options.h util.o: $(srcdir)/util.c config.h $(srcdir)/util.h $(srcdir)/region-allocator.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/rbtree.h $(srcdir)/rdata.h -xfrd.o: $(srcdir)/xfrd.c config.h $(srcdir)/xfrd.h $(srcdir)/netio.h $(srcdir)/region-allocator.h $(srcdir)/rbtree.h \ - $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/xfrd-tcp.h \ - $(srcdir)/xfrd-disk.h $(srcdir)/xfrd-notify.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/difffile.h $(srcdir)/ipc.h -xfrd-disk.o: $(srcdir)/xfrd-disk.c config.h $(srcdir)/xfrd-disk.h $(srcdir)/xfrd.h $(srcdir)/netio.h \ - $(srcdir)/region-allocator.h $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h \ + $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/rdata.h $(srcdir)/zonec.h +xfrd.o: $(srcdir)/xfrd.c config.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h $(srcdir)/namedb.h \ + $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/xfrd-tcp.h \ + $(srcdir)/xfrd-disk.h $(srcdir)/xfrd-notify.h $(srcdir)/netio.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/rdata.h \ + $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/remote.h +xfrd-disk.o: $(srcdir)/xfrd-disk.c config.h $(srcdir)/xfrd-disk.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h \ + $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h \ $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/nsd.h $(srcdir)/edns.h xfrd-notify.o: $(srcdir)/xfrd-notify.c config.h $(srcdir)/xfrd-notify.h $(srcdir)/tsig.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/netio.h $(srcdir)/rbtree.h $(srcdir)/xfrd.h $(srcdir)/namedb.h $(srcdir)/dns.h \ - $(srcdir)/options.h $(srcdir)/xfrd-tcp.h $(srcdir)/packet.h -xfrd-tcp.o: $(srcdir)/xfrd-tcp.c config.h $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd.h $(srcdir)/netio.h \ - $(srcdir)/region-allocator.h $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h \ + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/rbtree.h $(srcdir)/xfrd.h $(srcdir)/namedb.h $(srcdir)/dns.h \ + $(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/xfrd-tcp.h $(srcdir)/packet.h +xfrd-tcp.o: $(srcdir)/xfrd-tcp.c config.h $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h \ + $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h \ $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/packet.h zlexer.o: zlexer.c config.h $(srcdir)/zonec.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/rbtree.h zparser.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h zparser.h zonec.o: $(srcdir)/zonec.c config.h $(srcdir)/zonec.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h config.h $(srcdir)/dns.h $(srcdir)/rbtree.h $(srcdir)/rdata.h $(srcdir)/options.h \ - $(srcdir)/nsec3.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/rdata.h zparser.h \ + $(srcdir)/options.h $(srcdir)/nsec3.h zparser.o: zparser.c config.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h \ - $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/rbtree.h $(srcdir)/zonec.h + $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/zonec.h b64_ntop.o: $(srcdir)/compat/b64_ntop.c config.h b64_pton.o: $(srcdir)/compat/b64_pton.c config.h basename.o: $(srcdir)/compat/basename.c @@ -688,25 +447,44 @@ snprintf.o: $(srcdir)/compat/snprintf.c config.h strlcat.o: $(srcdir)/compat/strlcat.c config.h strlcpy.o: $(srcdir)/compat/strlcpy.c config.h strptime.o: $(srcdir)/compat/strptime.c -cutest.o: $(srcdir)/tpkg/cutest/cutest.c $(srcdir)/tpkg/cutest/cutest.h +cutest.o: $(srcdir)/tpkg/cutest/cutest.c config.h $(srcdir)/tpkg/cutest/cutest.h cutest_dname.o: $(srcdir)/tpkg/cutest/cutest_dname.c config.h $(srcdir)/tpkg/cutest/cutest.h \ - $(srcdir)/region-allocator.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h config.h + $(srcdir)/region-allocator.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h cutest_dns.o: $(srcdir)/tpkg/cutest/cutest_dns.c config.h $(srcdir)/tpkg/cutest/cutest.h \ $(srcdir)/region-allocator.h $(srcdir)/dns.h cutest_iterated_hash.o: $(srcdir)/tpkg/cutest/cutest_iterated_hash.c config.h \ - $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/util.h config.h $(srcdir)/iterated_hash.h \ - $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h + $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/iterated_hash.h $(srcdir)/dname.h \ + $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h +cutest_namedb.o: $(srcdir)/tpkg/cutest/cutest_namedb.c config.h \ + $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ + $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/nsec3.h $(srcdir)/udb.h \ + $(srcdir)/udbzone.h $(srcdir)/udb.h $(srcdir)/udbradtree.h $(srcdir)/difffile.h $(srcdir)/namedb.h $(srcdir)/options.h $(srcdir)/zonec.h cutest_options.o: $(srcdir)/tpkg/cutest/cutest_options.c config.h \ - $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/options.h config.h \ - $(srcdir)/region-allocator.h $(srcdir)/rbtree.h $(srcdir)/util.h + $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ + $(srcdir)/rbtree.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h +cutest_radtree.o: $(srcdir)/tpkg/cutest/cutest_radtree.c config.h \ + $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/radtree.h $(srcdir)/region-allocator.h $(srcdir)/util.h cutest_rbtree.o: $(srcdir)/tpkg/cutest/cutest_rbtree.c config.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h cutest_region.o: $(srcdir)/tpkg/cutest/cutest_region.c config.h \ - $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/util.h config.h $(srcdir)/rbtree.h \ + $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/rbtree.h \ $(srcdir)/region-allocator.h cutest_rrl.o: $(srcdir)/tpkg/cutest/cutest_rrl.c config.h $(srcdir)/tpkg/cutest/cutest.h \ - $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h \ - config.h $(srcdir)/dns.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h -cutest_run.o: $(srcdir)/tpkg/cutest/cutest_run.c config.h $(srcdir)/tpkg/cutest/cutest.h + $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h \ + $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h +cutest_run.o: $(srcdir)/tpkg/cutest/cutest_run.c config.h $(srcdir)/tpkg/cutest/cutest.h \ + $(srcdir)/tpkg/cutest/qtest.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h +cutest_udb.o: $(srcdir)/tpkg/cutest/cutest_udb.c config.h $(srcdir)/tpkg/cutest/cutest.h \ + $(srcdir)/udb.h +cutest_udbrad.o: $(srcdir)/tpkg/cutest/cutest_udbrad.c config.h \ + $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/udbradtree.h $(srcdir)/udb.h cutest_util.o: $(srcdir)/tpkg/cutest/cutest_util.c config.h $(srcdir)/tpkg/cutest/cutest.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h config.h + $(srcdir)/region-allocator.h $(srcdir)/util.h +qtest.o: $(srcdir)/tpkg/cutest/qtest.c config.h $(srcdir)/tpkg/cutest/qtest.h $(srcdir)/buffer.h \ + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/dns.h \ + $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/namedb.h $(srcdir)/util.h $(srcdir)/nsec3.h \ + $(srcdir)/options.h $(srcdir)/packet.h $(srcdir)/dname.h $(srcdir)/rdata.h +udb-inspect.o: $(srcdir)/tpkg/cutest/udb-inspect.c config.h $(srcdir)/udb.h $(srcdir)/udbradtree.h \ + $(srcdir)/udb.h $(srcdir)/udbzone.h $(srcdir)/dns.h $(srcdir)/udbradtree.h $(srcdir)/util.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h \ + $(srcdir)/util.h $(srcdir)/packet.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/rdata.h \ + $(srcdir)/namedb.h $(srcdir)/difffile.h $(srcdir)/options.h diff --git a/usr.sbin/nsd/axfr.c b/usr.sbin/nsd/axfr.c index 0657fda84a7..6ed41cab61a 100644 --- a/usr.sbin/nsd/axfr.c +++ b/usr.sbin/nsd/axfr.c @@ -1,7 +1,7 @@ /* * axfr.c -- generating AXFR responses. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -42,32 +42,34 @@ query_axfr(struct nsd *nsd, struct query *query) } if (query->axfr_zone == NULL) { + domain_type* qdomain; /* Start AXFR. */ + STATUP(nsd, raxfr); exact = namedb_lookup(nsd->db, query->qname, &closest_match, &closest_encloser); - query->domain = closest_encloser; + qdomain = closest_encloser; query->axfr_zone = domain_find_zone(closest_encloser); if (!exact || query->axfr_zone == NULL - || query->axfr_zone->apex != query->domain) + || query->axfr_zone->apex != qdomain) { /* No SOA no transfer */ RCODE_SET(query->packet, RCODE_NOTAUTH); return QUERY_PROCESSED; } - query->axfr_current_domain = query->domain; + query->axfr_current_domain = qdomain; query->axfr_current_rrset = NULL; query->axfr_current_rr = 0; if(query->tsig.status == TSIG_OK) { query->tsig_sign_it = 1; /* sign first packet in stream */ } - query_add_compression_domain(query, query->domain, QHEADERSZ); + query_add_compression_domain(query, qdomain, QHEADERSZ); assert(query->axfr_zone->soa_rrset->rr_count == 1); added = packet_encode_rr(query, @@ -120,12 +122,11 @@ query_axfr(struct nsd *nsd, struct query *query) } assert(query->axfr_current_domain); query->axfr_current_domain - = (domain_type *) rbtree_next((rbnode_t *) query->axfr_current_domain); + = domain_next(query->axfr_current_domain); } - while ((rbnode_t *) query->axfr_current_domain != RBTREE_NULL - && dname_is_subdomain( - domain_dname(query->axfr_current_domain), - domain_dname(query->axfr_zone->apex))); + while (query->axfr_current_domain != NULL && + domain_is_subdomain(query->axfr_current_domain, + query->axfr_zone->apex)); /* Add terminating SOA RR. */ assert(query->axfr_zone->soa_rrset->rr_count == 1); @@ -165,19 +166,16 @@ answer_axfr_ixfr(struct nsd *nsd, struct query *q) switch (q->qtype) { case TYPE_AXFR: if (q->tcp) { - zone_options_t* zone_opt = zone_options_find(nsd->options, q->qname); + zone_options_t* zone_opt; + zone_opt = zone_options_find(nsd->options, q->qname); if(!zone_opt || - acl_check_incoming(zone_opt->provide_xfr, q, &acl)==-1) + acl_check_incoming(zone_opt->pattern->provide_xfr, q, &acl)==-1) { if (verbosity > 0) { - char address[128]; - if (addr2ip(q->addr, address, sizeof(address))) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, - "addr2ip failed")); - strlcpy(address, "[unknown]", sizeof(address)); - } + char a[128]; + addr2str(&q->addr, a, sizeof(a)); VERBOSITY(1, (LOG_INFO, "axfr for zone %s from client %s refused, %s", - dname_to_string(q->qname, NULL), address, acl?"blocked":"no acl matches")); + dname_to_string(q->qname, NULL), a, acl?"blocked":"no acl matches")); } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s", acl?"blocked":"no acl matches")); diff --git a/usr.sbin/nsd/compat/snprintf.c b/usr.sbin/nsd/compat/snprintf.c index 52429edf06d..65959309c9c 100644 --- a/usr.sbin/nsd/compat/snprintf.c +++ b/usr.sbin/nsd/compat/snprintf.c @@ -1,770 +1,1036 @@ -#include <config.h> - -#ifndef HAVE_SNPRINTF +/* snprintf - compatibility implementation of snprintf, vsnprintf + * + * Copyright (c) 2013, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include <stdio.h> #include <ctype.h> -#include <sys/types.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif -/* Define this as a fall through, HAVE_STDARG_H is probably already set */ +/* for test */ +/* #define SNPRINTF_TEST 1 */ +#ifdef SNPRINTF_TEST +#define snprintf my_snprintf +#define vsnprintf my_vsnprintf +#endif /* SNPRINTF_TEST */ -#define HAVE_VARARGS_H +int snprintf(char* str, size_t size, const char* format, ...); +int vsnprintf(char* str, size_t size, const char* format, va_list arg); -/************************************************************** - * Original: - * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 - * A bombproof version of doprnt (dopr) included. - * Sigh. This sort of thing is always nasty do deal with. Note that - * the version here does not include floating point... - * - * snprintf() is used instead of sprintf() as it does limit checks - * for string length. This covers a nasty loophole. +/** + * Very portable snprintf implementation, limited in functionality, + * esp. for %[capital] %[nonportable] and so on. Reduced float functionality, + * mostly in formatting and range (e+-16), for %f and %g. * - * The other functions are there to prevent NULL pointers from - * causing nast effects. - * - * More Recently: - * Brandon Long (blong@fiction.net) 9/15/96 for mutt 0.43 - * This was ugly. It is still ugly. I opted out of floating point - * numbers, but the formatter understands just about everything - * from the normal C string format, at least as far as I can tell from - * the Solaris 2.5 printf(3S) man page. - * - * Brandon Long (blong@fiction.net) 10/22/97 for mutt 0.87.1 - * Ok, added some minimal floating point support, which means this - * probably requires libm on most operating systems. Don't yet - * support the exponent (e,E) and sigfig (g,G). Also, fmtint() - * was pretty badly broken, it just wasn't being exercised in ways - * which showed it, so that's been fixed. Also, formated the code - * to mutt conventions, and removed dead code left over from the - * original. Also, there is now a builtin-test, just compile with: - * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm - * and run snprintf for results. - * - **************************************************************/ + * %s, %d, %u, %i, %x, %c, %n and %% are fully supported. + * This includes width, precision, flags 0- +, and *(arg for wid,prec). + * %f, %g, %m, %p have reduced support, support for wid,prec,flags,*, but + * less floating point range, no %e formatting for %g. + */ +int snprintf(char* str, size_t size, const char* format, ...) +{ + int r; + va_list args; + va_start(args, format); + r = vsnprintf(str, size, format, args); + va_end(args); + return r; +} +/** add padding to string */ +static void +print_pad(char** at, size_t* left, int* ret, char p, int num) +{ + while(num--) { + if(*left > 1) { + *(*at)++ = p; + (*left)--; + } + (*ret)++; + } +} -/* varargs declarations: */ +/** get negative symbol, 0 if none */ +static char +get_negsign(int negative, int plus, int space) +{ + if(negative) + return '-'; + if(plus) + return '+'; + if(space) + return ' '; + return 0; +} -#if defined(HAVE_STDARG_H) -# include <stdarg.h> -# define HAVE_STDARGS /* let's hope that works everywhere (mj) */ -# define VA_LOCAL_DECL va_list ap -# define VA_START(f) va_start(ap, f) -# define VA_SHIFT(v,t) ; /* no-op for ANSI */ -# define VA_END va_end(ap) -#else -# if defined(HAVE_VARARGS_H) -# include <varargs.h> -# undef HAVE_STDARGS -# define VA_LOCAL_DECL va_list ap -# define VA_START(f) va_start(ap) /* f is ignored! */ -# define VA_SHIFT(v,t) v = va_arg(ap,t) -# define VA_END va_end(ap) -# else -/*XX ** NO VARARGS ** XX*/ -# endif -#endif +#define PRINT_DEC_BUFSZ 32 /* 20 is enough for 64 bit decimals */ +/** print decimal into buffer, returns length */ +static int +print_dec(char* buf, int max, unsigned int value) +{ + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = '0' + value % 10; + value /= 10; + } + return i; +} + +/** print long decimal into buffer, returns length */ +static int +print_dec_l(char* buf, int max, unsigned long value) +{ + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = '0' + value % 10; + value /= 10; + } + return i; +} + +/** print long decimal into buffer, returns length */ +static int +print_dec_ll(char* buf, int max, unsigned long long value) +{ + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = '0' + value % 10; + value /= 10; + } + return i; +} + +/** print hex into buffer, returns length */ +static int +print_hex(char* buf, int max, unsigned int value) +{ + const char* h = "0123456789abcdef"; + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = h[value & 0x0f]; + value >>= 4; + } + return i; +} + +/** print long hex into buffer, returns length */ +static int +print_hex_l(char* buf, int max, unsigned long value) +{ + const char* h = "0123456789abcdef"; + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = h[value & 0x0f]; + value >>= 4; + } + return i; +} + +/** print long long hex into buffer, returns length */ +static int +print_hex_ll(char* buf, int max, unsigned long long value) +{ + const char* h = "0123456789abcdef"; + int i = 0; + if(value == 0) { + if(max > 0) { + buf[0] = '0'; + i = 1; + } + } else while(value && i < max) { + buf[i++] = h[value & 0x0f]; + value >>= 4; + } + return i; +} + +/** copy string into result, reversed */ +static void +spool_str_rev(char** at, size_t* left, int* ret, const char* buf, int len) +{ + int i = len; + while(i) { + if(*left > 1) { + *(*at)++ = buf[--i]; + (*left)--; + } else --i; + (*ret)++; + } +} + +/** copy string into result */ +static void +spool_str(char** at, size_t* left, int* ret, const char* buf, int len) +{ + int i; + for(i=0; i<len; i++) { + if(*left > 1) { + *(*at)++ = buf[i]; + (*left)--; + } + (*ret)++; + } +} + +/** print number formatted */ +static void +print_num(char** at, size_t* left, int* ret, int minw, int precision, + int prgiven, int zeropad, int minus, int plus, int space, + int zero, int negative, char* buf, int len) +{ + int w = len; /* excludes minus sign */ + char s = get_negsign(negative, plus, space); + if(minus) { + /* left adjust the number into the field, space padding */ + /* calc numw = [sign][zeroes][number] */ + int numw = w; + if(precision == 0 && zero) numw = 0; + if(numw < precision) numw = precision; + if(s) numw++; + + /* sign */ + if(s) print_pad(at, left, ret, s, 1); + + /* number */ + if(precision == 0 && zero) { + /* "" for the number */ + } else { + if(w < precision) + print_pad(at, left, ret, '0', precision - w); + spool_str_rev(at, left, ret, buf, len); + } + /* spaces */ + if(numw < minw) + print_pad(at, left, ret, ' ', minw - numw); + } else { + /* pad on the left of the number */ + /* calculate numw has width of [sign][zeroes][number] */ + int numw = w; + if(precision == 0 && zero) numw = 0; + if(numw < precision) numw = precision; + if(!prgiven && zeropad && numw < minw) numw = minw; + else if(s) numw++; + + /* pad with spaces */ + if(numw < minw) + print_pad(at, left, ret, ' ', minw - numw); + /* print sign (and one less zeropad if so) */ + if(s) { + print_pad(at, left, ret, s, 1); + numw--; + } + /* pad with zeroes */ + if(w < numw) + print_pad(at, left, ret, '0', numw - w); + if(precision == 0 && zero) + return; + /* print the characters for the value */ + spool_str_rev(at, left, ret, buf, len); + } +} + +/** print %d and %i */ +static void +print_num_d(char** at, size_t* left, int* ret, int value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = (value < 0); + int zero = (value == 0); + int len = print_dec(buf, (int)sizeof(buf), + (unsigned int)(negative?-value:value)); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %ld and %li */ +static void +print_num_ld(char** at, size_t* left, int* ret, long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = (value < 0); + int zero = (value == 0); + int len = print_dec_l(buf, (int)sizeof(buf), + (unsigned long)(negative?-value:value)); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} -int snprintf (char *str, size_t count, const char *fmt, ...); -int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); +/** print %lld and %lli */ +static void +print_num_lld(char** at, size_t* left, int* ret, long long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = (value < 0); + int zero = (value == 0); + int len = print_dec_ll(buf, (int)sizeof(buf), + (unsigned long long)(negative?-value:value)); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} -static void dopr (char *buffer, size_t maxlen, const char *format, - va_list args); -static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, - char *value, int flags, int min, int max); -static void fmtint (char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags); -static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, - long double fvalue, int min, int max, int flags); -static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); +/** print %u */ +static void +print_num_u(char** at, size_t* left, int* ret, unsigned int value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_dec(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} -int vsnprintf (char *str, size_t count, const char *fmt, va_list args) +/** print %lu */ +static void +print_num_lu(char** at, size_t* left, int* ret, unsigned long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) { - str[0] = 0; - dopr(str, count, fmt, args); - return(strlen(str)); + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_dec_l(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); } -/* VARARGS3 */ -#ifdef HAVE_STDARGS -int snprintf (char *str,size_t count,const char *fmt,...) +/** print %llu */ +static void +print_num_llu(char** at, size_t* left, int* ret, unsigned long long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_dec_ll(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %x */ +static void +print_num_x(char** at, size_t* left, int* ret, unsigned int value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_hex(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %lx */ +static void +print_num_lx(char** at, size_t* left, int* ret, unsigned long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_hex_l(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %llx */ +static void +print_num_llx(char** at, size_t* left, int* ret, unsigned long long value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); + int len = print_hex_ll(buf, (int)sizeof(buf), value); + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +/** print %llp */ +static void +print_num_llp(char** at, size_t* left, int* ret, void* value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_DEC_BUFSZ]; + int negative = 0; + int zero = (value == 0); +#if defined(UINTPTR_MAX) && defined(UINT32_MAX) && (UINTPTR_MAX == UINT32_MAX) + /* avoid warning about upcast on 32bit systems */ + unsigned long long llvalue = (unsigned long)value; #else -int snprintf (va_alist) va_dcl + unsigned long long llvalue = (unsigned long long)value; #endif + int len = print_hex_ll(buf, (int)sizeof(buf), llvalue); + if(zero) { + buf[0]=')'; + buf[1]='l'; + buf[2]='i'; + buf[3]='n'; + buf[4]='('; + len = 5; + } else { + /* put '0x' in front of the (reversed) buffer result */ + if(len < PRINT_DEC_BUFSZ) + buf[len++] = 'x'; + if(len < PRINT_DEC_BUFSZ) + buf[len++] = '0'; + } + print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, + plus, space, zero, negative, buf, len); +} + +#define PRINT_FLOAT_BUFSZ 64 /* xx.yy with 20.20 about the max */ +/** spool remainder after the decimal point to buffer, in reverse */ +static int +print_remainder(char* buf, int max, double r, int prec) { -#ifndef HAVE_STDARGS - char *str; - size_t count; - char *fmt; -#endif - VA_LOCAL_DECL; - - VA_START (fmt); - VA_SHIFT (str, char *); - VA_SHIFT (count, size_t ); - VA_SHIFT (fmt, char *); - (void) vsnprintf(str, count, fmt, ap); - VA_END; - return(strlen(str)); -} - -/* - * dopr(): poor man's version of doprintf - */ + unsigned long long cap = 1; + unsigned long long value; + int len, i; + if(prec > 19) prec = 19; /* max we can do */ + if(max < prec) return 0; + for(i=0; i<prec; i++) { + cap *= 10; + } + r *= (double)cap; + value = (unsigned long long)r; + /* see if we need to round up */ + if(((unsigned long long)((r - (double)value)*10.0)) >= 5) { + value++; + /* that might carry to numbers before the comma, if so, + * just ignore that rounding. failure because 64bitprintout */ + if(value >= cap) + value = cap-1; + } + len = print_dec_ll(buf, max, value); + while(len < prec) { /* pad with zeroes, e.g. if 0.0012 */ + buf[len++] = '0'; + } + if(len < max) + buf[len++] = '.'; + return len; +} -/* format read states */ -#define DP_S_DEFAULT 0 -#define DP_S_FLAGS 1 -#define DP_S_MIN 2 -#define DP_S_DOT 3 -#define DP_S_MAX 4 -#define DP_S_MOD 5 -#define DP_S_CONV 6 -#define DP_S_DONE 7 - -/* format flags - Bits */ -#define DP_F_MINUS 1 -#define DP_F_PLUS 2 -#define DP_F_SPACE 4 -#define DP_F_NUM 8 -#define DP_F_ZERO 16 -#define DP_F_UP 32 - -/* Conversion Flags */ -#define DP_C_SHORT 1 -#define DP_C_LONG 2 -#define DP_C_LDOUBLE 3 - -#define char_to_int(p) (p - '0') -#define MAX(p,q) ((p >= q) ? p : q) - -static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) -{ - char ch; - long value; - long double fvalue; - char *strvalue; - int min; - int max; - int state; - int flags; - int cflags; - size_t currlen; - - state = DP_S_DEFAULT; - currlen = flags = cflags = min = 0; - max = -1; - ch = *format++; - - while (state != DP_S_DONE) - { - if ((ch == '\0') || (currlen >= maxlen)) - state = DP_S_DONE; - - switch(state) - { - case DP_S_DEFAULT: - if (ch == '%') - state = DP_S_FLAGS; - else - dopr_outch (buffer, &currlen, maxlen, ch); - ch = *format++; - break; - case DP_S_FLAGS: - switch (ch) - { - case '-': - flags |= DP_F_MINUS; - ch = *format++; - break; - case '+': - flags |= DP_F_PLUS; - ch = *format++; - break; - case ' ': - flags |= DP_F_SPACE; - ch = *format++; - break; - case '#': - flags |= DP_F_NUM; - ch = *format++; - break; - case '0': - flags |= DP_F_ZERO; - ch = *format++; - break; - default: - state = DP_S_MIN; - break; - } - break; - case DP_S_MIN: - if (isdigit(ch)) - { - min = 10*min + char_to_int (ch); - ch = *format++; - } - else if (ch == '*') - { - min = va_arg (args, int); - ch = *format++; - state = DP_S_DOT; - } - else - state = DP_S_DOT; - break; - case DP_S_DOT: - if (ch == '.') - { - state = DP_S_MAX; - ch = *format++; - } - else - state = DP_S_MOD; - break; - case DP_S_MAX: - if (isdigit(ch)) - { - if (max < 0) - max = 0; - max = 10*max + char_to_int (ch); - ch = *format++; - } - else if (ch == '*') - { - max = va_arg (args, int); - ch = *format++; - state = DP_S_MOD; - } - else - state = DP_S_MOD; - break; - case DP_S_MOD: - /* Currently, we don't support Long Long, bummer */ - switch (ch) - { - case 'h': - cflags = DP_C_SHORT; - ch = *format++; - break; - case 'l': - cflags = DP_C_LONG; - ch = *format++; - break; - case 'L': - cflags = DP_C_LDOUBLE; - ch = *format++; - break; - default: - break; - } - state = DP_S_CONV; - break; - case DP_S_CONV: - switch (ch) - { - case 'd': - case 'i': - if (cflags == DP_C_SHORT) - value = va_arg (args, int); - else if (cflags == DP_C_LONG) - value = va_arg (args, long int); - else - value = va_arg (args, int); - fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); - break; - case 'o': - flags &= ~DP_F_PLUS; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); - break; - case 'u': - flags &= ~DP_F_PLUS; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); - break; - case 'X': - flags |= DP_F_UP; - case 'x': - flags &= ~DP_F_PLUS; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); - break; - case 'f': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, long double); - else - fvalue = va_arg (args, double); - /* um, floating point? */ - fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); - break; - case 'E': - flags |= DP_F_UP; - case 'e': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, long double); - else - fvalue = va_arg (args, double); - break; - case 'G': - flags |= DP_F_UP; - case 'g': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, long double); - else - fvalue = va_arg (args, double); - break; - case 'c': - dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); - break; - case 's': - strvalue = va_arg (args, char *); - if (max < 0) - max = maxlen; /* ie, no max */ - fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); - break; - case 'p': - strvalue = va_arg (args, void *); - fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); - break; - case 'n': - if (cflags == DP_C_SHORT) - { - short int *num; - num = va_arg (args, short int *); - *num = currlen; - } - else if (cflags == DP_C_LONG) - { - long int *num; - num = va_arg (args, long int *); - *num = currlen; - } - else - { - int *num; - num = va_arg (args, int *); - *num = currlen; - } - break; - case '%': - dopr_outch (buffer, &currlen, maxlen, ch); - break; - case 'w': - /* not supported yet, treat as next char */ - ch = *format++; - break; - default: - /* Unknown, skip */ - break; - } - ch = *format++; - state = DP_S_DEFAULT; - flags = cflags = min = 0; - max = -1; - break; - case DP_S_DONE: - break; - default: - /* hmm? */ - break; /* some picky compilers need this */ - } - } - if (currlen < maxlen - 1) - buffer[currlen] = '\0'; - else - buffer[maxlen - 1] = '\0'; -} - -static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, - char *value, int flags, int min, int max) -{ - int padlen, strln; /* amount to pad */ - int cnt = 0; - - if (value == 0) - { - value = "<NULL>"; - } - - for (strln = 0; value[strln]; ++strln); /* strlen */ - padlen = min - strln; - if (padlen < 0) - padlen = 0; - if (flags & DP_F_MINUS) - padlen = -padlen; /* Left Justify */ - - while ((padlen > 0) && (cnt < max)) - { - dopr_outch (buffer, currlen, maxlen, ' '); - --padlen; - ++cnt; - } - while (*value && (cnt < max)) - { - dopr_outch (buffer, currlen, maxlen, *value++); - ++cnt; - } - while ((padlen < 0) && (cnt < max)) - { - dopr_outch (buffer, currlen, maxlen, ' '); - ++padlen; - ++cnt; - } -} - -/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ - -static void fmtint (char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags) -{ - int signvalue = 0; - unsigned long uvalue; - char convert[20]; - int place = 0; - int spadlen = 0; /* amount to space pad */ - int zpadlen = 0; /* amount to zero pad */ - int caps = 0; - - if (max < 0) - max = 0; - - uvalue = value; - if( value < 0 ) { - signvalue = '-'; - uvalue = -value; - } - else - if (flags & DP_F_PLUS) /* Do a sign (+/i) */ - signvalue = '+'; - else - if (flags & DP_F_SPACE) - signvalue = ' '; - - if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ - - do { - convert[place++] = - (caps? "0123456789ABCDEF":"0123456789abcdef") - [uvalue % (unsigned)base ]; - uvalue = (uvalue / (unsigned)base ); - } while(uvalue && (place < 20)); - if (place == 20) place--; - convert[place] = 0; - - zpadlen = max - place; - spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); - if (zpadlen < 0) zpadlen = 0; - if (spadlen < 0) spadlen = 0; - if (flags & DP_F_ZERO) - { - zpadlen = MAX(zpadlen, spadlen); - spadlen = 0; - } - if (flags & DP_F_MINUS) - spadlen = -spadlen; /* Left Justifty */ - -#ifdef DEBUG_SNPRINTF - dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", - zpadlen, spadlen, min, max, place)); -#endif +/** spool floating point to buffer */ +static int +print_float(char* buf, int max, double value, int prec) +{ + /* as xxx.xxx if prec==0, no '.', with prec decimals after . */ + /* no conversion for NAN and INF, because we do not want to require + linking with -lm. */ + /* Thus, the conversions use 64bit integers to convert the numbers, + * which makes 19 digits before and after the decimal point the max */ + unsigned long long whole = (unsigned long long)value; + double remain = value - (double)whole; + int len = 0; + if(prec != 0) + len = print_remainder(buf, max, remain, prec); + len += print_dec_ll(buf+len, max-len, whole); + return len; +} - /* Spaces */ - while (spadlen > 0) - { - dopr_outch (buffer, currlen, maxlen, ' '); - --spadlen; - } - - /* Sign */ - if (signvalue) - dopr_outch (buffer, currlen, maxlen, signvalue); - - /* Zeros */ - if (zpadlen > 0) - { - while (zpadlen > 0) - { - dopr_outch (buffer, currlen, maxlen, '0'); - --zpadlen; - } - } - - /* Digits */ - while (place > 0) - dopr_outch (buffer, currlen, maxlen, convert[--place]); - - /* Left Justified spaces */ - while (spadlen < 0) { - dopr_outch (buffer, currlen, maxlen, ' '); - ++spadlen; - } -} - -static long double abs_val (long double value) -{ - long double result = value; - - if (value < 0) - result = -value; - - return result; -} - -static long double pow10 (int exp) -{ - long double result = 1; - - while (exp) - { - result *= 10; - exp--; - } - - return result; -} - -static long round (long double value) -{ - long intpart; - - intpart = value; - value = value - intpart; - if (value >= 0.5) - intpart++; - - return intpart; -} - -static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, - long double fvalue, int min, int max, int flags) -{ - int signvalue = 0; - long double ufvalue; - char iconvert[20]; - char fconvert[20]; - int iplace = 0; - int fplace = 0; - int padlen = 0; /* amount to pad */ - int zpadlen = 0; - int caps = 0; - long intpart; - long fracpart; - - /* - * AIX manpage says the default is 0, but Solaris says the default - * is 6, and sprintf on AIX defaults to 6 - */ - if (max < 0) - max = 6; - - ufvalue = abs_val (fvalue); - - if (fvalue < 0) - signvalue = '-'; - else - if (flags & DP_F_PLUS) /* Do a sign (+/i) */ - signvalue = '+'; - else - if (flags & DP_F_SPACE) - signvalue = ' '; - -#if 0 - if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ -#endif +/** print %f */ +static void +print_num_f(char** at, size_t* left, int* ret, double value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_FLOAT_BUFSZ]; + int negative = (value < 0); + int zero = 0; + int len; + if(!prgiven) precision = 6; + len = print_float(buf, (int)sizeof(buf), negative?-value:value, + precision); + print_num(at, left, ret, minw, 1, 0, zeropad, minus, + plus, space, zero, negative, buf, len); +} - intpart = ufvalue; +/* rudimentary %g support */ +static int +print_float_g(char* buf, int max, double value, int prec) +{ + unsigned long long whole = (unsigned long long)value; + double remain = value - (double)whole; + int before = 0; + int len = 0; + + /* number of digits before the decimal point */ + while(whole > 0) { + before++; + whole /= 10; + } + whole = (unsigned long long)value; + + if(prec > before && remain != 0.0) { + /* see if the last decimals are zero, if so, skip them */ + len = print_remainder(buf, max, remain, prec-before); + while(len > 0 && buf[0]=='0') { + memmove(buf, buf+1, --len); + } + } + len += print_dec_ll(buf+len, max-len, whole); + return len; +} - /* - * Sorry, we only support 9 digits past the decimal because of our - * conversion method - */ - if (max > 9) - max = 9; - /* We "cheat" by converting the fractional part to integer by - * multiplying by a factor of 10 - */ - fracpart = round ((pow10 (max)) * (ufvalue - intpart)); +/** print %g */ +static void +print_num_g(char** at, size_t* left, int* ret, double value, + int minw, int precision, int prgiven, int zeropad, int minus, + int plus, int space) +{ + char buf[PRINT_FLOAT_BUFSZ]; + int negative = (value < 0); + int zero = 0; + int len; + if(!prgiven) precision = 6; + if(precision == 0) precision = 1; + len = print_float_g(buf, (int)sizeof(buf), negative?-value:value, + precision); + print_num(at, left, ret, minw, 1, 0, zeropad, minus, + plus, space, zero, negative, buf, len); +} - if (fracpart >= pow10 (max)) - { - intpart++; - fracpart -= pow10 (max); - } -#ifdef DEBUG_SNPRINTF - dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); -#endif +/** strnlen (compat implementation) */ +static int +my_strnlen(const char* s, int max) +{ + int i; + for(i=0; i<max; i++) + if(s[i]==0) + return i; + return max; +} - /* Convert integer part */ - do { - iconvert[iplace++] = - (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; - intpart = (intpart / 10); - } while(intpart && (iplace < 20)); - if (iplace == 20) iplace--; - iconvert[iplace] = 0; - - /* Convert fractional part */ - do { - fconvert[fplace++] = - (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; - fracpart = (fracpart / 10); - } while(fracpart && (fplace < 20)); - if (fplace == 20) fplace--; - fconvert[fplace] = 0; - - /* -1 for decimal point, another -1 if we are printing a sign */ - padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); - zpadlen = max - fplace; - if (zpadlen < 0) - zpadlen = 0; - if (padlen < 0) - padlen = 0; - if (flags & DP_F_MINUS) - padlen = -padlen; /* Left Justifty */ - - if ((flags & DP_F_ZERO) && (padlen > 0)) - { - if (signvalue) - { - dopr_outch (buffer, currlen, maxlen, signvalue); - --padlen; - signvalue = 0; - } - while (padlen > 0) - { - dopr_outch (buffer, currlen, maxlen, '0'); - --padlen; - } - } - while (padlen > 0) - { - dopr_outch (buffer, currlen, maxlen, ' '); - --padlen; - } - if (signvalue) - dopr_outch (buffer, currlen, maxlen, signvalue); - - while (iplace > 0) - dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); - - /* - * Decimal point. This should probably use locale to find the correct - * char to print out. - */ - dopr_outch (buffer, currlen, maxlen, '.'); - - while (zpadlen > 0) - { - dopr_outch (buffer, currlen, maxlen, '0'); - --zpadlen; - } - - while (fplace > 0) - dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); - - while (padlen < 0) - { - dopr_outch (buffer, currlen, maxlen, ' '); - ++padlen; - } -} - -static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) -{ - if (*currlen < maxlen) - buffer[(*currlen)++] = c; -} - -#ifdef TEST_SNPRINTF -#ifndef LONG_STRING -#define LONG_STRING 1024 -#endif -int main (void) -{ - char buf1[LONG_STRING]; - char buf2[LONG_STRING]; - char *fp_fmt[] = { - "%-1.5f", - "%1.5f", - "%123.9f", - "%10.5f", - "% 10.5f", - "%+22.9f", - "%+4.9f", - "%01.3f", - "%4f", - "%3.1f", - "%3.2f", - NULL - }; - double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, - 0.9996, 1.996, 4.136, 0}; - char *int_fmt[] = { - "%-1.5d", - "%1.5d", - "%123.9d", - "%5.5d", - "%10.5d", - "% 10.5d", - "%+22.33d", - "%01.3d", - "%4d", - NULL - }; - long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; - int x, y; - int fail = 0; - int num = 0; - - printf ("Testing snprintf format codes against system sprintf...\n"); - - for (x = 0; fp_fmt[x] != NULL ; x++) - for (y = 0; fp_nums[y] != 0 ; y++) - { - snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); - sprintf (buf2, fp_fmt[x], fp_nums[y]); - if (strcmp (buf1, buf2)) - { - printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", - fp_fmt[x], buf1, buf2); - fail++; - } - num++; - } - - for (x = 0; int_fmt[x] != NULL ; x++) - for (y = 0; int_nums[y] != 0 ; y++) - { - snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); - sprintf (buf2, int_fmt[x], int_nums[y]); - if (strcmp (buf1, buf2)) - { - printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", - int_fmt[x], buf1, buf2); - fail++; - } - num++; - } - printf ("%d tests failed out of %d.\n", fail, num); +/** print %s */ +static void +print_str(char** at, size_t* left, int* ret, char* s, + int minw, int precision, int prgiven, int minus) +{ + int w; + /* with prec: no more than x characters from this string, stop at 0 */ + if(prgiven) + w = my_strnlen(s, precision); + else w = (int)strlen(s); /* up to the nul */ + if(w < minw && !minus) + print_pad(at, left, ret, ' ', minw - w); + spool_str(at, left, ret, s, w); + if(w < minw && minus) + print_pad(at, left, ret, ' ', minw - w); +} + +/** print %c */ +static void +print_char(char** at, size_t* left, int* ret, int c, + int minw, int minus) +{ + if(1 < minw && !minus) + print_pad(at, left, ret, ' ', minw - 1); + print_pad(at, left, ret, c, 1); + if(1 < minw && minus) + print_pad(at, left, ret, ' ', minw - 1); +} + + +/** + * Print to string. + * str: string buffer for result. result will be null terminated. + * size: size of the buffer. null is put inside buffer. + * format: printf format string. + * arg: '...' arguments to print. + * returns number of characters. a null is printed after this. + * return number of bytes that would have been written + * if the buffer had been large enough. + * + * supported format specifiers: + * %s, %u, %d, %x, %i, %f, %g, %c, %p, %n. + * length: l, ll (for d, u, x). + * precision: 6.6d (for d, u, x) + * %f, %g precisions, 0.3f + * %20s, '.*s' + * and %%. + */ +int vsnprintf(char* str, size_t size, const char* format, va_list arg) +{ + char* at = str; + size_t left = size; + int ret = 0; + const char* fmt = format; + int conv, minw, precision, prgiven, zeropad, minus, plus, space, length; + while(*fmt) { + /* copy string before % */ + while(*fmt && *fmt!='%') { + if(left > 1) { + *at++ = *fmt++; + left--; + } else fmt++; + ret++; + } + + /* see if we are at end */ + if(!*fmt) break; + + /* fetch next argument % designation from format string */ + fmt++; /* skip the '%' */ + + /********************************/ + /* get the argument designation */ + /********************************/ + /* we must do this vararg stuff inside this function for + * portability. Hence, get_designation, and print_designation + * are not their own functions. */ + + /* printout designation: + * conversion specifier: x, d, u, s, c, n, m, p + * flags: # not supported + * 0 zeropad (on the left) + * - left adjust (right by default) + * ' ' printspace for positive number (in - position). + * + alwayssign + * fieldwidth: [1-9][0-9]* minimum field width. + * if this is * then type int next argument specifies the minwidth. + * if this is negative, the - flag is set (with positive width). + * precision: period[digits]*, %.2x. + * if this is * then type int next argument specifies the precision. + * just '.' or negative value means precision=0. + * this is mindigits to print for d, i, u, x + * this is aftercomma digits for f + * this is max number significant digits for g + * maxnumber characters to be printed for s + * length: 0-none (int), 1-l (long), 2-ll (long long) + * notsupported: hh (char), h (short), L (long double), q, j, z, t + * Does not support %m$ and *m$ argument designation as array indices. + * Does not support %#x + * + */ + minw = 0; + precision = 1; + prgiven = 0; + zeropad = 0; + minus = 0; + plus = 0; + space = 0; + length = 0; + + /* get flags in any order */ + for(;;) { + if(*fmt == '0') + zeropad = 1; + else if(*fmt == '-') + minus = 1; + else if(*fmt == '+') + plus = 1; + else if(*fmt == ' ') + space = 1; + else break; + fmt++; + } + + /* field width */ + if(*fmt == '*') { + fmt++; /* skip char */ + minw = va_arg(arg, int); + if(minw < 0) { + minus = 1; + minw = -minw; + } + } else while(*fmt >= '0' && *fmt <= '9') { + minw = minw*10 + (*fmt++)-'0'; + } + + /* precision */ + if(*fmt == '.') { + fmt++; /* skip period */ + prgiven = 1; + precision = 0; + if(*fmt == '*') { + fmt++; /* skip char */ + precision = va_arg(arg, int); + if(precision < 0) + precision = 0; + } else while(*fmt >= '0' && *fmt <= '9') { + precision = precision*10 + (*fmt++)-'0'; + } + } + + /* length */ + if(*fmt == 'l') { + fmt++; /* skip char */ + length = 1; + if(*fmt == 'l') { + fmt++; /* skip char */ + length = 2; + } + } + + /* get the conversion */ + if(!*fmt) conv = 0; + else conv = *fmt++; + + /***********************************/ + /* print that argument designation */ + /***********************************/ + switch(conv) { + case 'i': + case 'd': + if(length == 0) + print_num_d(&at, &left, &ret, va_arg(arg, int), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 1) + print_num_ld(&at, &left, &ret, va_arg(arg, long), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 2) + print_num_lld(&at, &left, &ret, + va_arg(arg, long long), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case 'u': + if(length == 0) + print_num_u(&at, &left, &ret, + va_arg(arg, unsigned int), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 1) + print_num_lu(&at, &left, &ret, + va_arg(arg, unsigned long), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 2) + print_num_llu(&at, &left, &ret, + va_arg(arg, unsigned long long), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case 'x': + if(length == 0) + print_num_x(&at, &left, &ret, + va_arg(arg, unsigned int), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 1) + print_num_lx(&at, &left, &ret, + va_arg(arg, unsigned long), + minw, precision, prgiven, zeropad, minus, plus, space); + else if(length == 2) + print_num_llx(&at, &left, &ret, + va_arg(arg, unsigned long long), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case 's': + print_str(&at, &left, &ret, va_arg(arg, char*), + minw, precision, prgiven, minus); + break; + case 'c': + print_char(&at, &left, &ret, va_arg(arg, int), + minw, minus); + break; + case 'n': + *va_arg(arg, int*) = ret; + break; + case 'm': + print_str(&at, &left, &ret, strerror(errno), + minw, precision, prgiven, minus); + break; + case 'p': + print_num_llp(&at, &left, &ret, va_arg(arg, void*), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case '%': + print_pad(&at, &left, &ret, '%', 1); + break; + case 'f': + print_num_f(&at, &left, &ret, va_arg(arg, double), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + case 'g': + print_num_g(&at, &left, &ret, va_arg(arg, double), + minw, precision, prgiven, zeropad, minus, plus, space); + break; + /* unknown */ + default: + case 0: break; + } + } + + /* zero terminate */ + if(left > 0) + *at = 0; + return ret; } -#endif /* SNPRINTF_TEST */ -#endif /* !HAVE_SNPRINTF */ +#ifdef SNPRINTF_TEST + +/** do tests */ +#undef snprintf +#define DOTEST(bufsz, result, retval, ...) do { \ + char buf[bufsz]; \ + printf("now test %s\n", #__VA_ARGS__); \ + int r=my_snprintf(buf, sizeof(buf), __VA_ARGS__); \ + if(r != retval || strcmp(buf, result) != 0) { \ + printf("error test(%s) was \"%s\":%d\n", \ + ""#bufsz", "#result", "#retval", "#__VA_ARGS__, \ + buf, r); \ + exit(1); \ + } \ + r=snprintf(buf, sizeof(buf), __VA_ARGS__); \ + if(r != retval || strcmp(buf, result) != 0) { \ + printf("error test(%s) differs with system, \"%s\":%d\n", \ + ""#bufsz", "#result", "#retval", "#__VA_ARGS__, \ + buf, r); \ + exit(1); \ + } \ + printf("test(\"%s\":%d) passed\n", buf, r); \ + } while(0); + +/** test program */ +int main(void) +{ + int x = 0; + + /* bufsize, expectedstring, expectedretval, snprintf arguments */ + DOTEST(1024, "hello", 5, "hello"); + DOTEST(1024, "h", 1, "h"); + /* warning from gcc for format string, but it does work + * DOTEST(1024, "", 0, ""); */ + + DOTEST(3, "he", 5, "hello"); + DOTEST(1, "", 7, "%d", 7823089); + + /* test positive numbers */ + DOTEST(1024, "0", 1, "%d", 0); + DOTEST(1024, "1", 1, "%d", 1); + DOTEST(1024, "9", 1, "%d", 9); + DOTEST(1024, "15", 2, "%d", 15); + DOTEST(1024, "ab15cd", 6, "ab%dcd", 15); + DOTEST(1024, "167", 3, "%d", 167); + DOTEST(1024, "7823089", 7, "%d", 7823089); + DOTEST(1024, " 12", 3, "%3d", 12); + DOTEST(1024, "012", 3, "%.3d", 12); + DOTEST(1024, "012", 3, "%3.3d", 12); + DOTEST(1024, "012", 3, "%03d", 12); + DOTEST(1024, " 012", 4, "%4.3d", 12); + DOTEST(1024, "", 0, "%.0d", 0); + + /* test negative numbers */ + DOTEST(1024, "-1", 2, "%d", -1); + DOTEST(1024, "-12", 3, "%3d", -12); + DOTEST(1024, " -2", 3, "%3d", -2); + DOTEST(1024, "-012", 4, "%.3d", -12); + DOTEST(1024, "-012", 4, "%3.3d", -12); + DOTEST(1024, "-012", 4, "%4.3d", -12); + DOTEST(1024, " -012", 5, "%5.3d", -12); + DOTEST(1024, "-12", 3, "%03d", -12); + DOTEST(1024, "-02", 3, "%03d", -2); + DOTEST(1024, "-15", 3, "%d", -15); + DOTEST(1024, "-7307", 5, "%d", -7307); + DOTEST(1024, "-12 ", 5, "%-5d", -12); + DOTEST(1024, "-00012", 6, "%-.5d", -12); + + /* test + and space flags */ + DOTEST(1024, "+12", 3, "%+d", 12); + DOTEST(1024, " 12", 3, "% d", 12); + + /* test %u */ + DOTEST(1024, "12", 2, "%u", 12); + DOTEST(1024, "0", 1, "%u", 0); + DOTEST(1024, "4294967295", 10, "%u", 0xffffffff); + + /* test %x */ + DOTEST(1024, "0", 1, "%x", 0); + DOTEST(1024, "c", 1, "%x", 12); + DOTEST(1024, "12ab34cd", 8, "%x", 0x12ab34cd); + + /* test %llu, %lld */ + DOTEST(1024, "18446744073709551615", 20, "%llu", + (long long)0xffffffffffffffff); + DOTEST(1024, "-9223372036854775808", 20, "%lld", + (long long)0x8000000000000000); + DOTEST(1024, "9223372036854775808", 19, "%llu", + (long long)0x8000000000000000); + + /* test %s */ + DOTEST(1024, "hello", 5, "%s", "hello"); + DOTEST(1024, " hello", 10, "%10s", "hello"); + DOTEST(1024, "hello ", 10, "%-10s", "hello"); + DOTEST(1024, "he", 2, "%.2s", "hello"); + DOTEST(1024, " he", 4, "%4.2s", "hello"); + DOTEST(1024, " h", 4, "%4.2s", "h"); + + /* test %c */ + DOTEST(1024, "a", 1, "%c", 'a'); + /* warning from gcc for format string, but it does work + DOTEST(1024, " a", 5, "%5c", 'a'); + DOTEST(1024, "a", 1, "%.0c", 'a'); */ + + /* test %n */ + DOTEST(1024, "hello", 5, "hello%n", &x); + if(x != 5) { printf("the %%n failed\n"); exit(1); } + + /* test %m */ + errno = 0; + DOTEST(1024, "Success", 7, "%m"); + + /* test %p */ + DOTEST(1024, "0x10", 4, "%p", (void*)0x10); + DOTEST(1024, "(nil)", 5, "%p", (void*)0x0); + + /* test %% */ + DOTEST(1024, "%", 1, "%%"); + + /* test %f */ + DOTEST(1024, "0.000000", 8, "%f", 0.0); + DOTEST(1024, "0.00", 4, "%.2f", 0.0); + /* differs, "-0.00" DOTEST(1024, "0.00", 4, "%.2f", -0.0); */ + DOTEST(1024, "234.00", 6, "%.2f", 234.005); + DOTEST(1024, "8973497.1246", 12, "%.4f", 8973497.12456); + DOTEST(1024, "-12.000000", 10, "%f", -12.0); + DOTEST(1024, "6", 1, "%.0f", 6.0); + + DOTEST(1024, "6", 1, "%g", 6.0); + DOTEST(1024, "6.1", 3, "%g", 6.1); + DOTEST(1024, "6.15", 4, "%g", 6.15); + + /* These format strings are from the code of NSD, Unbound, ldns */ + + DOTEST(1024, "abcdef", 6, "%s", "abcdef"); + DOTEST(1024, "005", 3, "%03u", 5); + DOTEST(1024, "12345", 5, "%03u", 12345); + DOTEST(1024, "5", 1, "%d", 5); + DOTEST(1024, "(nil)", 5, "%p", NULL); + DOTEST(1024, "12345", 5, "%ld", (long)12345); + DOTEST(1024, "12345", 5, "%lu", (long)12345); + DOTEST(1024, " 12345", 12, "%12u", (unsigned)12345); + DOTEST(1024, "12345", 5, "%u", (unsigned)12345); + DOTEST(1024, "12345", 5, "%llu", (unsigned long long)12345); + DOTEST(1024, "12345", 5, "%x", 0x12345); + DOTEST(1024, "12345", 5, "%llx", (long long)0x12345); + DOTEST(1024, "012345", 6, "%6.6d", 12345); + DOTEST(1024, "012345", 6, "%6.6u", 12345); + DOTEST(1024, "1234.54", 7, "%g", 1234.54); + DOTEST(1024, "123456789.54", 12, "%.12g", 123456789.54); + DOTEST(1024, "3456789123456.54", 16, "%.16g", 3456789123456.54); + /* %24g does not work with 24 digits, not enough accuracy, + * the first 16 digits are correct */ + DOTEST(1024, "12345", 5, "%3.3d", 12345); + DOTEST(1024, "000", 3, "%3.3d", 0); + DOTEST(1024, "001", 3, "%3.3d", 1); + DOTEST(1024, "012", 3, "%3.3d", 12); + DOTEST(1024, "-012", 4, "%3.3d", -12); + DOTEST(1024, "he", 2, "%.2s", "hello"); + DOTEST(1024, "helloworld", 10, "%s%s", "hello", "world"); + DOTEST(1024, "he", 2, "%.*s", 2, "hello"); + DOTEST(1024, " hello", 7, "%*s", 7, "hello"); + DOTEST(1024, "hello ", 7, "%*s", -7, "hello"); + DOTEST(1024, "0", 1, "%c", '0'); + DOTEST(1024, "A", 1, "%c", 'A'); + DOTEST(1024, "", 1, "%c", 0); + DOTEST(1024, "\010", 1, "%c", 8); + DOTEST(1024, "%", 1, "%%"); + DOTEST(1024, "0a", 2, "%02x", 0x0a); + DOTEST(1024, "bd", 2, "%02x", 0xbd); + DOTEST(1024, "12", 2, "%02ld", (long)12); + DOTEST(1024, "02", 2, "%02ld", (long)2); + DOTEST(1024, "02", 2, "%02u", (unsigned)2); + DOTEST(1024, "765432", 6, "%05u", (unsigned)765432); + DOTEST(1024, "10.234", 6, "%0.3f", 10.23421); + DOTEST(1024, "123456.234", 10, "%0.3f", 123456.23421); + DOTEST(1024, "123456789.234", 13, "%0.3f", 123456789.23421); + DOTEST(1024, "123456.23", 9, "%.2f", 123456.23421); + DOTEST(1024, "123456", 6, "%.0f", 123456.23421); + DOTEST(1024, "0123", 4, "%.4x", 0x0123); + DOTEST(1024, "00000123", 8, "%.8x", 0x0123); + DOTEST(1024, "ffeb0cde", 8, "%.8x", 0xffeb0cde); + DOTEST(1024, " 987654321", 10, "%10lu", (unsigned long)987654321); + DOTEST(1024, " 987654321", 12, "%12lu", (unsigned long)987654321); + DOTEST(1024, "987654321", 9, "%i", 987654321); + DOTEST(1024, "-87654321", 9, "%i", -87654321); + DOTEST(1024, "hello ", 16, "%-16s", "hello"); + DOTEST(1024, " ", 16, "%-16s", ""); + DOTEST(1024, "a ", 16, "%-16s", "a"); + DOTEST(1024, "foobarfoobar ", 16, "%-16s", "foobarfoobar"); + DOTEST(1024, "foobarfoobarfoobar", 18, "%-16s", "foobarfoobarfoobar"); + + /* combined expressions */ + DOTEST(1024, "foo 1.0 size 512 edns", 21, + "foo %s size %d %s%s", "1.0", 512, "", "edns"); + DOTEST(15, "foo 1.0 size 5", 21, + "foo %s size %d %s%s", "1.0", 512, "", "edns"); + DOTEST(1024, "packet 1203ceff id", 18, + "packet %2.2x%2.2x%2.2x%2.2x id", 0x12, 0x03, 0xce, 0xff); + DOTEST(1024, "/tmp/testbound_123abcd.tmp", 26, "/tmp/testbound_%u%s%s.tmp", 123, "ab", "cd"); + + return 0; +} +#endif /* SNPRINTF_TEST */ diff --git a/usr.sbin/nsd/config.h.in b/usr.sbin/nsd/config.h.in index 92ef3b2b09d..b2675b6d30c 100644 --- a/usr.sbin/nsd/config.h.in +++ b/usr.sbin/nsd/config.h.in @@ -6,6 +6,9 @@ /* NSD default chroot directory */ #undef CHROOTDIR +/* NSD config dir */ +#undef CONFIGDIR + /* Pathname to the NSD configuration file */ #undef CONFIGFILE @@ -16,9 +19,6 @@ /* Pathname to the NSD database */ #undef DBFILE -/* Pathname to the NSD diff transfer journal file. */ -#undef DIFFFILE - /* Define this to enable draft RRtypes. */ #undef DRAFT_RRTYPES @@ -28,9 +28,6 @@ /* Define to the default facility for syslog. */ #undef FACILITY -/* Define this to enable NSEC3 full prehashing. */ -#undef FULL_PREHASH - /* Define to 1 if you have the `alarm' function. */ #undef HAVE_ALARM @@ -73,12 +70,33 @@ /* Define to 1 if you have the `endpwent' function. */ #undef HAVE_ENDPWENT +/* Define to 1 if you have the `event_base_free' function. */ +#undef HAVE_EVENT_BASE_FREE + +/* Define to 1 if you have the `event_base_get_method' function. */ +#undef HAVE_EVENT_BASE_GET_METHOD + +/* Define to 1 if you have the `event_base_new' function. */ +#undef HAVE_EVENT_BASE_NEW + +/* Define to 1 if you have the `event_base_once' function. */ +#undef HAVE_EVENT_BASE_ONCE + +/* Define to 1 if you have the <event.h> header file. */ +#undef HAVE_EVENT_H + /* Define to 1 if you have the `EVP_sha1' function. */ #undef HAVE_EVP_SHA1 /* Define to 1 if you have the `EVP_sha256' function. */ #undef HAVE_EVP_SHA256 +/* Define to 1 if you have the `ev_default_loop' function. */ +#undef HAVE_EV_DEFAULT_LOOP + +/* Define to 1 if you have the `ev_loop' function. */ +#undef HAVE_EV_LOOP + /* Define to 1 if you have the <fcntl.h> header file. */ #undef HAVE_FCNTL_H @@ -158,12 +176,30 @@ /* Define to 1 if you have the <netinet/in.h> header file. */ #undef HAVE_NETINET_IN_H +/* Define to 1 if you have the <openssl/err.h> header file. */ +#undef HAVE_OPENSSL_ERR_H + +/* Define to 1 if you have the <openssl/rand.h> header file. */ +#undef HAVE_OPENSSL_RAND_H + +/* Define to 1 if you have the <openssl/ssl.h> header file. */ +#undef HAVE_OPENSSL_SSL_H + /* Define to 1 if you have the `pselect' function. */ #undef HAVE_PSELECT /* if sys/select.h provides pselect prototype */ #undef HAVE_PSELECT_PROTO +/* Define to 1 if you have the `pwrite' function. */ +#undef HAVE_PWRITE + +/* Define if recvmmsg is implemented */ +#undef HAVE_RECVMMSG + +/* Define if sendmmsg is implemented */ +#undef HAVE_SENDMMSG + /* Define to 1 if you have the `setregid' function. */ #undef HAVE_SETREGID @@ -320,9 +356,25 @@ /* Define this to enable response minimalization to reduce truncation. */ #undef MINIMAL_RESPONSES +/* Define if mkdir has one argument. */ +#undef MKDIR_HAS_ONE_ARG + /* Undefine this to enable internal runtime checks. */ #undef NDEBUG +/* Define if the network stack does not fully support nonblocking io (causes + lower performance). */ +#undef NONBLOCKING_IS_BROKEN + +/* Define to the default nsd-control port. */ +#undef NSD_CONTROL_PORT + +/* Define to nsd-control proto version. */ +#undef NSD_CONTROL_VERSION + +/* Pathname to start nsd from nsd-control */ +#undef NSD_START_PATH + /* Define this to enable NSEC3 support. */ #undef NSEC3 @@ -356,6 +408,12 @@ /* Define this to configure as a root server. */ #undef ROOT_SERVER +/* The size of `off_t', as computed by sizeof. */ +#undef SIZEOF_OFF_T + +/* The size of `void*', as computed by sizeof. */ +#undef SIZEOF_VOIDP + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS @@ -386,6 +444,9 @@ /* the user name to drop privileges to */ #undef USER +/* Define if you want to use internal select based events */ +#undef USE_MINI_EVENT + /* Define this to enable mmap instead of malloc. Experimental. */ #undef USE_MMAP_ALLOC @@ -411,24 +472,29 @@ #endif -/* Define this to enable zone statistics. */ -#undef USE_ZONE_STATS - /* Define to the NSD version to answer version.server query. */ #undef VERSION /* Pathname to the NSD xfrd zone timer state file. */ #undef XFRDFILE +/* Pathname to where the NSD transfer dir is created. */ +#undef XFRDIR + /* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a `char[]'. */ #undef YYTEXT_POINTER +/* Pathname to the NSD zone list file. */ +#undef ZONELISTFILE + /* NSD default location for zone files. Empty string or NULL to disable. */ #undef ZONESDIR -/* Pathname to the NSD statistics file */ -#undef ZONESTATSFILE +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS diff --git a/usr.sbin/nsd/configparser.y b/usr.sbin/nsd/configparser.y index b284c4f78cf..17dae8a64ef 100644 --- a/usr.sbin/nsd/configparser.y +++ b/usr.sbin/nsd/configparser.y @@ -1,7 +1,7 @@ /* * configparser.y -- yacc grammar for NSD configuration files * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -18,6 +18,8 @@ #include "options.h" #include "util.h" +#include "dname.h" +#include "tsig.h" #include "rrl.h" #include "configyyrename.h" int c_lex(void); @@ -29,7 +31,6 @@ extern "C" /* these need to be global, otherwise they cannot be used inside yacc */ extern config_parser_state_t* cfg_parser; -static int server_settings_seen = 0; #if 0 #define OUTYY(s) printf s /* used ONLY when debugging */ @@ -47,10 +48,9 @@ static int server_settings_seen = 0; %token VAR_SERVER VAR_NAME VAR_IP_ADDRESS VAR_IP_TRANSPARENT VAR_DEBUG_MODE %token VAR_IP4_ONLY VAR_IP6_ONLY VAR_DATABASE VAR_IDENTITY VAR_NSID VAR_LOGFILE %token VAR_SERVER_COUNT VAR_TCP_COUNT VAR_PIDFILE VAR_PORT VAR_STATISTICS -%token VAR_ZONESTATSFILE VAR_CHROOT VAR_USERNAME VAR_ZONESDIR -%token VAR_XFRDFILE VAR_DIFFFILE +%token VAR_CHROOT VAR_USERNAME VAR_ZONESDIR VAR_XFRDFILE VAR_DIFFFILE %token VAR_XFRD_RELOAD_TIMEOUT VAR_TCP_QUERY_COUNT VAR_TCP_TIMEOUT -%token VAR_IPV4_EDNS_SIZE VAR_IPV6_EDNS_SIZE +%token VAR_IPV4_EDNS_SIZE VAR_IPV6_EDNS_SIZE VAR_DO_IP4 VAR_DO_IP6 %token VAR_ZONEFILE %token VAR_ZONE %token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR @@ -59,35 +59,42 @@ static int server_settings_seen = 0; %token VAR_ALGORITHM VAR_SECRET %token VAR_AXFR VAR_UDP %token VAR_VERBOSITY VAR_HIDE_VERSION +%token VAR_PATTERN VAR_INCLUDEPATTERN VAR_ZONELISTFILE +%token VAR_REMOTE_CONTROL VAR_CONTROL_ENABLE VAR_CONTROL_INTERFACE +%token VAR_CONTROL_PORT VAR_SERVER_KEY_FILE VAR_SERVER_CERT_FILE +%token VAR_CONTROL_KEY_FILE VAR_CONTROL_CERT_FILE VAR_XFRDIR %token VAR_RRL_SIZE VAR_RRL_RATELIMIT VAR_RRL_SLIP %token VAR_RRL_IPV4_PREFIX_LENGTH VAR_RRL_IPV6_PREFIX_LENGTH %token VAR_RRL_WHITELIST_RATELIMIT VAR_RRL_WHITELIST +%token VAR_ZONEFILES_CHECK %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; toplevelvar: serverstart contents_server | zonestart contents_zone | - keystart contents_key; + keystart contents_key | patternstart contents_pattern | + rcstart contents_rc; /* server: declaration */ serverstart: VAR_SERVER { OUTYY(("\nP(server:)\n")); - if(server_settings_seen) { + if(cfg_parser->server_settings_seen) { yyerror("duplicate server: element."); } - server_settings_seen = 1; + cfg_parser->server_settings_seen = 1; } ; contents_server: contents_server content_server | ; content_server: server_ip_address | server_ip_transparent | server_debug_mode | server_ip4_only | server_ip6_only | server_database | server_identity | server_nsid | server_logfile | server_server_count | server_tcp_count | server_pidfile | server_port | - server_statistics | server_zonestatsfile | server_chroot | - server_username | server_zonesdir | + server_statistics | server_chroot | server_username | server_zonesdir | server_difffile | server_xfrdfile | server_xfrd_reload_timeout | server_tcp_query_count | server_tcp_timeout | server_ipv4_edns_size | server_ipv6_edns_size | server_verbosity | server_hide_version | + server_zonelistfile | server_xfrdir | server_rrl_size | server_rrl_ratelimit | server_rrl_slip | - server_rrl_ipv4_prefix_length | server_rrl_ipv6_prefix_length | server_rrl_whitelist_ratelimit; + server_rrl_ipv4_prefix_length | server_rrl_ipv6_prefix_length | server_rrl_whitelist_ratelimit | + server_zonefiles_check | server_do_ip4 | server_do_ip6 ; server_ip_address: VAR_IP_ADDRESS STRING { OUTYY(("P(server_ip_address:%s)\n", $2)); @@ -144,18 +151,42 @@ server_hide_version: VAR_HIDE_VERSION STRING ; server_ip4_only: VAR_IP4_ONLY STRING { + /* for backwards compatibility in config file with NSD3 */ OUTYY(("P(server_ip4_only:%s)\n", $2)); if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) yyerror("expected yes or no."); - else cfg_parser->opt->ip4_only = (strcmp($2, "yes")==0); + else if(strcmp($2, "yes")==0) { + cfg_parser->opt->do_ip4 = 1; + cfg_parser->opt->do_ip6 = 0; + } } ; server_ip6_only: VAR_IP6_ONLY STRING { + /* for backwards compatibility in config file with NSD3 */ OUTYY(("P(server_ip6_only:%s)\n", $2)); if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) yyerror("expected yes or no."); - else cfg_parser->opt->ip6_only = (strcmp($2, "yes")==0); + else if(strcmp($2, "yes")==0) { + cfg_parser->opt->do_ip6 = 1; + cfg_parser->opt->do_ip4 = 0; + } + } + ; +server_do_ip4: VAR_DO_IP4 STRING + { + OUTYY(("P(server_do_ip4:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->opt->do_ip4 = (strcmp($2, "yes")==0); + } + ; +server_do_ip6: VAR_DO_IP6 STRING + { + OUTYY(("P(server_do_ip6:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->opt->do_ip6 = (strcmp($2, "yes")==0); } ; server_database: VAR_DATABASE STRING @@ -232,12 +263,6 @@ server_statistics: VAR_STATISTICS STRING else cfg_parser->opt->statistics = atoi($2); } ; -server_zonestatsfile: VAR_ZONESTATSFILE STRING - { - OUTYY(("P(server_zonestatsfile:%s)\n", $2)); - cfg_parser->opt->zonestatsfile = region_strdup(cfg_parser->opt->region, $2); - } - ; server_chroot: VAR_CHROOT STRING { OUTYY(("P(server_chroot:%s)\n", $2)); @@ -256,10 +281,22 @@ server_zonesdir: VAR_ZONESDIR STRING cfg_parser->opt->zonesdir = region_strdup(cfg_parser->opt->region, $2); } ; +server_zonelistfile: VAR_ZONELISTFILE STRING + { + OUTYY(("P(server_zonelistfile:%s)\n", $2)); + cfg_parser->opt->zonelistfile = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_xfrdir: VAR_XFRDIR STRING + { + OUTYY(("P(server_xfrdir:%s)\n", $2)); + cfg_parser->opt->xfrdir = region_strdup(cfg_parser->opt->region, $2); + } + ; server_difffile: VAR_DIFFFILE STRING { OUTYY(("P(server_difffile:%s)\n", $2)); - cfg_parser->opt->difffile = region_strdup(cfg_parser->opt->region, $2); + /* ignore the value for backwards compatibility in config file*/ } ; server_xfrdfile: VAR_XFRDFILE STRING @@ -364,6 +401,139 @@ server_rrl_whitelist_ratelimit: VAR_RRL_WHITELIST_RATELIMIT STRING #endif } ; +server_zonefiles_check: VAR_ZONEFILES_CHECK STRING + { + OUTYY(("P(server_zonefiles_check:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->opt->zonefiles_check = (strcmp($2, "yes")==0); + } + ; + +rcstart: VAR_REMOTE_CONTROL + { + OUTYY(("\nP(remote-control:)\n")); + } + ; +contents_rc: contents_rc content_rc + | ; +content_rc: rc_control_enable | rc_control_interface | rc_control_port | + rc_server_key_file | rc_server_cert_file | rc_control_key_file | + rc_control_cert_file + ; +rc_control_enable: VAR_CONTROL_ENABLE STRING + { + OUTYY(("P(control_enable:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->opt->control_enable = (strcmp($2, "yes")==0); + } + ; +rc_control_port: VAR_CONTROL_PORT STRING + { + OUTYY(("P(control_port:%s)\n", $2)); + if(atoi($2) == 0) + yyerror("control port number expected"); + else cfg_parser->opt->control_port = atoi($2); + } + ; +rc_control_interface: VAR_CONTROL_INTERFACE STRING + { + ip_address_option_t* o = (ip_address_option_t*)region_alloc( + cfg_parser->opt->region, sizeof(ip_address_option_t)); + OUTYY(("P(control_interface:%s)\n", $2)); + o->next = cfg_parser->opt->control_interface; + cfg_parser->opt->control_interface = o; + o->address = region_strdup(cfg_parser->opt->region, $2); + } + ; +rc_server_key_file: VAR_SERVER_KEY_FILE STRING + { + OUTYY(("P(rc_server_key_file:%s)\n", $2)); + cfg_parser->opt->server_key_file = region_strdup(cfg_parser->opt->region, $2); + } + ; +rc_server_cert_file: VAR_SERVER_CERT_FILE STRING + { + OUTYY(("P(rc_server_cert_file:%s)\n", $2)); + cfg_parser->opt->server_cert_file = region_strdup(cfg_parser->opt->region, $2); + } + ; +rc_control_key_file: VAR_CONTROL_KEY_FILE STRING + { + OUTYY(("P(rc_control_key_file:%s)\n", $2)); + cfg_parser->opt->control_key_file = region_strdup(cfg_parser->opt->region, $2); + } + ; +rc_control_cert_file: VAR_CONTROL_CERT_FILE STRING + { + OUTYY(("P(rc_control_cert_file:%s)\n", $2)); + cfg_parser->opt->control_cert_file = region_strdup(cfg_parser->opt->region, $2); + } + ; + +/* pattern: declaration */ +patternstart: VAR_PATTERN + { + OUTYY(("\nP(pattern:)\n")); + if(cfg_parser->current_zone) { + if(!cfg_parser->current_zone->name) + c_error("previous zone has no name"); + else { + if(!nsd_options_insert_zone(cfg_parser->opt, + cfg_parser->current_zone)) + c_error("duplicate zone"); + } + if(!cfg_parser->current_zone->pattern) + c_error("previous zone has no pattern"); + cfg_parser->current_zone = NULL; + } + if(cfg_parser->current_pattern) { + if(!cfg_parser->current_pattern->pname) + c_error("previous pattern has no name"); + else { + if(!nsd_options_insert_pattern(cfg_parser->opt, + cfg_parser->current_pattern)) + c_error_msg("duplicate pattern %s", + cfg_parser->current_pattern->pname); + } + } + cfg_parser->current_pattern = pattern_options_create( + cfg_parser->opt->region); + cfg_parser->current_allow_notify = 0; + cfg_parser->current_request_xfr = 0; + cfg_parser->current_notify = 0; + cfg_parser->current_provide_xfr = 0; + cfg_parser->current_outgoing_interface = 0; + } + ; +contents_pattern: contents_pattern content_pattern | content_pattern; +content_pattern: pattern_name | zone_config_item; +zone_config_item: zone_zonefile | zone_allow_notify | zone_request_xfr | + zone_notify | zone_notify_retry | zone_provide_xfr | + zone_outgoing_interface | zone_allow_axfr_fallback | include_pattern | + zone_rrl_whitelist; +pattern_name: VAR_NAME STRING + { + OUTYY(("P(pattern_name:%s)\n", $2)); +#ifndef NDEBUG + assert(cfg_parser->current_pattern); +#endif + if(strchr($2, ' ')) + c_error_msg("space is not allowed in pattern name: " + "'%s'", $2); + cfg_parser->current_pattern->pname = region_strdup(cfg_parser->opt->region, $2); + } + ; +include_pattern: VAR_INCLUDEPATTERN STRING + { + OUTYY(("P(include-pattern:%s)\n", $2)); +#ifndef NDEBUG + assert(cfg_parser->current_pattern); +#endif + config_apply_pattern($2); + } + ; /* zone: declaration */ zonestart: VAR_ZONE @@ -377,10 +547,25 @@ zonestart: VAR_ZONE cfg_parser->current_zone)) c_error("duplicate zone"); } - if(!cfg_parser->current_zone->zonefile) - c_error("previous zone has no zonefile"); + if(!cfg_parser->current_zone->pattern) + c_error("previous zone has no pattern"); + } + if(cfg_parser->current_pattern) { + if(!cfg_parser->current_pattern->pname) + c_error("previous pattern has no name"); + else { + if(!nsd_options_insert_pattern(cfg_parser->opt, + cfg_parser->current_pattern)) + c_error_msg("duplicate pattern %s", + cfg_parser->current_pattern->pname); + } } cfg_parser->current_zone = zone_options_create(cfg_parser->opt->region); + cfg_parser->current_zone->part_of_config = 1; + cfg_parser->current_pattern = pattern_options_create( + cfg_parser->opt->region); + cfg_parser->current_pattern->implicit = 1; + cfg_parser->current_zone->pattern = cfg_parser->current_pattern; cfg_parser->current_allow_notify = 0; cfg_parser->current_request_xfr = 0; cfg_parser->current_notify = 0; @@ -389,35 +574,44 @@ zonestart: VAR_ZONE } ; contents_zone: contents_zone content_zone | content_zone; -content_zone: zone_name | zone_zonefile | zone_allow_notify | - zone_request_xfr | zone_notify | zone_notify_retry | zone_provide_xfr | - zone_outgoing_interface | zone_allow_axfr_fallback | zone_rrl_whitelist; +content_zone: zone_name | zone_config_item; zone_name: VAR_NAME STRING { + char* s; OUTYY(("P(zone_name:%s)\n", $2)); #ifndef NDEBUG assert(cfg_parser->current_zone); + assert(cfg_parser->current_pattern); #endif cfg_parser->current_zone->name = region_strdup(cfg_parser->opt->region, $2); + s = (char*)region_alloc(cfg_parser->opt->region, + strlen($2)+strlen(PATTERN_IMPLICIT_MARKER)+1); + memmove(s, PATTERN_IMPLICIT_MARKER, + strlen(PATTERN_IMPLICIT_MARKER)); + memmove(s+strlen(PATTERN_IMPLICIT_MARKER), $2, strlen($2)+1); + if(pattern_options_find(cfg_parser->opt, s)) + c_error_msg("zone %s cannot be created because " + "implicit pattern %s already exists", $2, s); + cfg_parser->current_pattern->pname = s; } ; zone_zonefile: VAR_ZONEFILE STRING { - OUTYY(("P(zone_zonefile:%s)\n", $2)); + OUTYY(("P(zonefile:%s)\n", $2)); #ifndef NDEBUG - assert(cfg_parser->current_zone); + assert(cfg_parser->current_pattern); #endif - cfg_parser->current_zone->zonefile = region_strdup(cfg_parser->opt->region, $2); + cfg_parser->current_pattern->zonefile = region_strdup(cfg_parser->opt->region, $2); } ; zone_allow_notify: VAR_ALLOW_NOTIFY STRING STRING { acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3); - OUTYY(("P(zone_allow_notify:%s %s)\n", $2, $3)); + OUTYY(("P(allow_notify:%s %s)\n", $2, $3)); if(cfg_parser->current_allow_notify) cfg_parser->current_allow_notify->next = acl; else - cfg_parser->current_zone->allow_notify = acl; + cfg_parser->current_pattern->allow_notify = acl; cfg_parser->current_allow_notify = acl; } ; @@ -428,99 +622,105 @@ zone_request_xfr: VAR_REQUEST_XFR zone_request_xfr_data zone_request_xfr_data: STRING STRING { acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $1, $2); - OUTYY(("P(zone_request_xfr:%s %s)\n", $1, $2)); + OUTYY(("P(request_xfr:%s %s)\n", $1, $2)); if(acl->blocked) c_error("blocked address used for request-xfr"); if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr"); if(cfg_parser->current_request_xfr) cfg_parser->current_request_xfr->next = acl; else - cfg_parser->current_zone->request_xfr = acl; + cfg_parser->current_pattern->request_xfr = acl; cfg_parser->current_request_xfr = acl; } | VAR_AXFR STRING STRING { acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3); acl->use_axfr_only = 1; - OUTYY(("P(zone_request_xfr:%s %s)\n", $2, $3)); + OUTYY(("P(request_xfr:%s %s)\n", $2, $3)); if(acl->blocked) c_error("blocked address used for request-xfr"); if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr"); if(cfg_parser->current_request_xfr) cfg_parser->current_request_xfr->next = acl; else - cfg_parser->current_zone->request_xfr = acl; + cfg_parser->current_pattern->request_xfr = acl; cfg_parser->current_request_xfr = acl; } | VAR_UDP STRING STRING { acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3); acl->allow_udp = 1; - OUTYY(("P(zone_request_xfr:%s %s)\n", $2, $3)); + OUTYY(("P(request_xfr:%s %s)\n", $2, $3)); if(acl->blocked) c_error("blocked address used for request-xfr"); if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr"); if(cfg_parser->current_request_xfr) cfg_parser->current_request_xfr->next = acl; else - cfg_parser->current_zone->request_xfr = acl; + cfg_parser->current_pattern->request_xfr = acl; cfg_parser->current_request_xfr = acl; } ; zone_notify: VAR_NOTIFY STRING STRING { acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3); - OUTYY(("P(zone_notify:%s %s)\n", $2, $3)); + OUTYY(("P(notify:%s %s)\n", $2, $3)); if(acl->blocked) c_error("blocked address used for notify"); if(acl->rangetype!=acl_range_single) c_error("address range used for notify"); if(cfg_parser->current_notify) cfg_parser->current_notify->next = acl; else - cfg_parser->current_zone->notify = acl; + cfg_parser->current_pattern->notify = acl; cfg_parser->current_notify = acl; } ; zone_notify_retry: VAR_NOTIFY_RETRY STRING { - OUTYY(("P(zone_notify_retry:%s)\n", $2)); + OUTYY(("P(notify_retry:%s)\n", $2)); if(atoi($2) == 0 && strcmp($2, "0") != 0) yyerror("number expected"); - else cfg_parser->current_zone->notify_retry = atoi($2); + else { + cfg_parser->current_pattern->notify_retry = atoi($2); + cfg_parser->current_pattern->notify_retry_is_default=0; + } } ; zone_provide_xfr: VAR_PROVIDE_XFR STRING STRING { acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3); - OUTYY(("P(zone_provide_xfr:%s %s)\n", $2, $3)); + OUTYY(("P(provide_xfr:%s %s)\n", $2, $3)); if(cfg_parser->current_provide_xfr) cfg_parser->current_provide_xfr->next = acl; else - cfg_parser->current_zone->provide_xfr = acl; + cfg_parser->current_pattern->provide_xfr = acl; cfg_parser->current_provide_xfr = acl; } ; zone_outgoing_interface: VAR_OUTGOING_INTERFACE STRING { acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, "NOKEY"); - OUTYY(("P(zone_outgoing_interface:%s)\n", $2)); + OUTYY(("P(outgoing_interface:%s)\n", $2)); if(acl->rangetype!=acl_range_single) c_error("address range used for outgoing interface"); if(cfg_parser->current_outgoing_interface) cfg_parser->current_outgoing_interface->next = acl; else - cfg_parser->current_zone->outgoing_interface = acl; + cfg_parser->current_pattern->outgoing_interface = acl; cfg_parser->current_outgoing_interface = acl; } ; zone_allow_axfr_fallback: VAR_ALLOW_AXFR_FALLBACK STRING { - OUTYY(("P(zone_allow_axfr_fallback:%s)\n", $2)); + OUTYY(("P(allow_axfr_fallback:%s)\n", $2)); if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) yyerror("expected yes or no."); - else cfg_parser->current_zone->allow_axfr_fallback = (strcmp($2, "yes")==0); + else { + cfg_parser->current_pattern->allow_axfr_fallback = (strcmp($2, "yes")==0); + cfg_parser->current_pattern->allow_axfr_fallback_is_default = 0; + } } ; zone_rrl_whitelist: VAR_RRL_WHITELIST STRING { OUTYY(("P(zone_rrl_whitelist:%s)\n", $2)); #ifdef RATELIMIT - cfg_parser->current_zone->rrl_whitelist |= rrlstr2type($2); + cfg_parser->current_pattern->rrl_whitelist |= rrlstr2type($2); #endif } ; @@ -533,24 +733,25 @@ keystart: VAR_KEY if(!cfg_parser->current_key->name) c_error("previous key has no name"); if(!cfg_parser->current_key->algorithm) c_error("previous key has no algorithm"); if(!cfg_parser->current_key->secret) c_error("previous key has no secret blob"); - cfg_parser->current_key->next = key_options_create(cfg_parser->opt->region); - cfg_parser->current_key = cfg_parser->current_key->next; - } else { - cfg_parser->current_key = key_options_create(cfg_parser->opt->region); - cfg_parser->opt->keys = cfg_parser->current_key; + key_options_insert(cfg_parser->opt, cfg_parser->current_key); } - cfg_parser->opt->numkeys++; + cfg_parser->current_key = key_options_create(cfg_parser->opt->region); } ; contents_key: contents_key content_key | content_key; content_key: key_name | key_algorithm | key_secret; key_name: VAR_NAME STRING { + const dname_type* d; OUTYY(("P(key_name:%s)\n", $2)); #ifndef NDEBUG assert(cfg_parser->current_key); #endif cfg_parser->current_key->name = region_strdup(cfg_parser->opt->region, $2); + d = dname_parse(cfg_parser->opt->region, $2); + if(!d) c_error_msg("Failed to parse tsig key name %s", $2); + else region_recycle(cfg_parser->opt->region, (void*)d, + dname_total_size(d)); } ; key_algorithm: VAR_ALGORITHM STRING @@ -560,15 +761,27 @@ key_algorithm: VAR_ALGORITHM STRING assert(cfg_parser->current_key); #endif cfg_parser->current_key->algorithm = region_strdup(cfg_parser->opt->region, $2); + if(tsig_get_algorithm_by_name($2) == NULL) + c_error_msg("Bad tsig algorithm %s", $2); } ; key_secret: VAR_SECRET STRING { + uint8_t data[16384]; + int size; OUTYY(("key_secret:%s)\n", $2)); #ifndef NDEBUG assert(cfg_parser->current_key); #endif cfg_parser->current_key->secret = region_strdup(cfg_parser->opt->region, $2); + size = b64_pton($2, data, sizeof(data)); + if(size == -1) { + c_error_msg("Cannot base64 decode tsig secret %s", + cfg_parser->current_key->name? + cfg_parser->current_key->name:""); + } else if(size != 0) { + memset(data, 0xdd, size); /* wipe secret */ + } } ; diff --git a/usr.sbin/nsd/configure b/usr.sbin/nsd/configure index 1d6e64876f3..5d856e35a90 100644 --- a/usr.sbin/nsd/configure +++ b/usr.sbin/nsd/configure @@ -1,13 +1,11 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for NSD 3.2.16. +# Generated by GNU Autoconf 2.69 for NSD 4.0.0. # # Report bugs to <nsd-bugs@nlnetlabs.nl>. # # -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software -# Foundation, Inc. +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -136,6 +134,31 @@ export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh @@ -169,7 +192,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi -test x\$exitcode = x0 || exit 1" +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && @@ -214,21 +238,25 @@ IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : - # We cannot yet assume a decent shell, so we have to provide a - # neutralization value for shells without unset; and this also - # works around shells that cannot unset nonexistent variables. - # Preserve -v and -x to the replacement shell. - BASH_ENV=/dev/null - ENV=/dev/null - (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV - export CONFIG_SHELL - case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; - esac - exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 fi if test x$as_have_required = xno; then : @@ -331,6 +359,14 @@ $as_echo X"$as_dir" | } # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take @@ -452,6 +488,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). @@ -486,16 +526,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -507,28 +547,8 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -560,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='NSD' PACKAGE_TARNAME='nsd' -PACKAGE_VERSION='3.2.16' -PACKAGE_STRING='NSD 3.2.16' +PACKAGE_VERSION='4.0.0' +PACKAGE_STRING='NSD 4.0.0' PACKAGE_BUGREPORT='nsd-bugs@nlnetlabs.nl' PACKAGE_URL='' @@ -602,7 +622,7 @@ ac_includes_default="\ #endif" ac_subst_vars='LTLIBOBJS -zonestatsfile +SSL_LIBS HAVE_SSL ratelimit LIBOBJS @@ -617,16 +637,16 @@ INSTALL_PROGRAM LN_S AWK user +chrootdir +xfrdir +zonelistfile xfrdfile -difffile zonesdir piddir dbdir dbfile pidfile logfile -kill_priority -start_priority nsd_conf_file configdir EGREP @@ -682,16 +702,19 @@ ac_user_opts=' enable_option_checking with_configdir with_nsd_conf_file -with_start_priority -with_kill_priority +with_logfile with_pidfile with_dbfile with_zonesdir -with_difffile with_xfrdfile +with_zonelistfile +with_xfrdir with_chroot with_user +enable_flto +with_libevent enable_largefile +enable_recvmmsg with_facility with_max_ips with_tcp_timeout @@ -702,11 +725,9 @@ enable_checking enable_ratelimit with_ssl enable_nsec3 -enable_full_prehash enable_minimal_responses enable_draft_rrtypes enable_mmap -enable_zone_stats ' ac_precious_vars='build_alias host_alias @@ -1174,8 +1195,6 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1261,7 +1280,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures NSD 3.2.16 to adapt to many kinds of systems. +\`configure' configures NSD 4.0.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1322,7 +1341,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of NSD 3.2.16:";; + short | recursive ) echo "Configuration of NSD 4.0.0:";; esac cat <<\_ACEOF @@ -1330,20 +1349,20 @@ Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-flto Disable link-time optimization (gcc specific option) --disable-largefile omit support for large files + --disable-recvmmsg Disable recvmmsg and sendmmsg compilation for + compatibility with more Linux kernel versions --enable-root-server Configure NSD as a root server --disable-ipv6 Disables IPv6 support --enable-bind8-stats Enables BIND8 like NSTATS & XSTATS --enable-checking Enable internal runtime checks --enable-ratelimit Enable rate limiting --disable-nsec3 Disable NSEC3 support - --disable-full-prehash Disables NSEC3 full prehashing --disable-minimal-responses Disable response minimization. More truncation. --enable-draft-rrtypes Enable draft RRtypes. --enable-mmap Use mmap instead of malloc. Experimental. - --enable-zone-stats Maintain statistics per zone, instead of global - statistics. Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1351,17 +1370,21 @@ Optional Packages: --with-configdir=dir NSD configuration directory --with-nsd_conf_file=path Pathname to the NSD configuration file - --with-start_priority=number - NSD startup priority - --with-kill_priority=number - NSD shutdown priority + --with-logfile=path Pathname to the default log file --with-pidfile=path Pathname to the NSD pidfile --with-dbfile=path Pathname to the NSD database --with-zonesdir=dir NSD default location for zone files - --with-difffile=path Pathname to the NSD diff transfer journal file --with-xfrdfile=path Pathname to the NSD xfrd zone timer state file + --with-zonelistfile=path + Pathname to the NSD zone list file + --with-xfrdir=path Pathname to where the NSD transfer dir is created --with-chroot=dir NSD default chroot directory --with-user=username User name or ID to answer the queries with + --with-libevent=pathname + use libevent (will check /usr/local /opt/local + /usr/lib /usr/pkg /usr/sfw /usr or you can specify + an explicit path), useful when the zone count is + high. --with-facility=name Syslog default facility (LOG_DAEMON) --with-max-ips=number Limit on the number of ip-addresses that may be specified @@ -1452,10 +1475,10 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -NSD configure 3.2.16 -generated by GNU Autoconf 2.68 +NSD configure 4.0.0 +generated by GNU Autoconf 2.69 -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1732,7 +1755,7 @@ $as_echo "$ac_try_echo"; } >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || - $as_test_x conftest$ac_exeext + test -x conftest$ac_exeext }; then : ac_retval=0 else @@ -1805,62 +1828,51 @@ $as_echo "$ac_res" >&6; } } # ac_fn_c_check_type -# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES -# ---------------------------------------------------- -# Tries to find if the field MEMBER exists in type AGGR, after including -# INCLUDES, setting cache variable VAR accordingly. -ac_fn_c_check_member () +# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES +# --------------------------------------------- +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. +ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 -$as_echo_n "checking for $2.$3... " >&6; } -if eval \${$4+:} false; then : + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 +$4 int main () { -static $2 ac_aggr; -if (sizeof ac_aggr.$3) -return 0; +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" + eval "$3=yes" else - eval "$4=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -eval ac_res=\$$4 +eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno -} # ac_fn_c_check_member +} # ac_fn_c_check_decl # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- @@ -1928,12 +1940,252 @@ $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func + +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include <stdio.h> +#include <stdlib.h> +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 <conftest.val; ac_retval=0 +else + ac_retval=1 +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f conftest.val + + fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_compute_int cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by NSD $as_me 3.2.16, which was -generated by GNU Autoconf 2.68. Invocation command line was +It was created by NSD $as_me 4.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2305,7 +2557,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2345,7 +2597,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2398,7 +2650,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2439,7 +2691,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue @@ -2497,7 +2749,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2541,7 +2793,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2987,8 +3239,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <stdarg.h> #include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> +struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -3228,7 +3479,7 @@ do for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in @@ -3294,7 +3545,7 @@ do for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in @@ -3501,8 +3752,8 @@ else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -# define __EXTENSIONS__ 1 - $ac_includes_default +# define __EXTENSIONS__ 1 + $ac_includes_default int main () { @@ -3560,6 +3811,11 @@ if test "${with_configdir+set}" = set; then : fi +cat >>confdefs.h <<_ACEOF +#define CONFIGDIR "`eval echo $configdir`" +_ACEOF + + # # Determine configuration file @@ -3580,30 +3836,16 @@ _ACEOF # -# Determine start and kill priorities -start_priority=45 - -# Check whether --with-start_priority was given. -if test "${with_start_priority+set}" = set; then : - withval=$with_start_priority; start_priority=$withval -fi - - - -kill_priority=74 - -# Check whether --with-kill_priority was given. -if test "${with_kill_priority+set}" = set; then : - withval=$with_kill_priority; kill_priority=$withval -fi - - - -# # Default logfile # logfile=${localstatedir}/log/nsd.log +# Check whether --with-logfile was given. +if test "${with_logfile+set}" = set; then : + withval=$with_logfile; logfile=$withval +fi + + # # Database directory @@ -3672,36 +3914,66 @@ cat >>confdefs.h <<_ACEOF _ACEOF -# default diff file location. -difffile=${dbdir}/ixfr.db +# default xfrd file location. +xfrdfile=${dbdir}/xfrd.state + +# Check whether --with-xfrdfile was given. +if test "${with_xfrdfile+set}" = set; then : + withval=$with_xfrdfile; xfrdfile=$withval +fi + + +cat >>confdefs.h <<_ACEOF +#define XFRDFILE "`eval echo $xfrdfile`" +_ACEOF + + -# Check whether --with-difffile was given. -if test "${with_difffile+set}" = set; then : - withval=$with_difffile; difffile=$withval +# default zonelist file location. +zonelistfile=${dbdir}/zone.list + +# Check whether --with-zonelistfile was given. +if test "${with_zonelistfile+set}" = set; then : + withval=$with_zonelistfile; zonelistfile=$withval fi cat >>confdefs.h <<_ACEOF -#define DIFFFILE "`eval echo $difffile`" +#define ZONELISTFILE "`eval echo $zonelistfile`" _ACEOF -# default xfrd file location. -xfrdfile=${dbdir}/xfrd.state +# default xfr dir location. +xfrdir="/tmp" -# Check whether --with-xfrdfile was given. -if test "${with_xfrdfile+set}" = set; then : - withval=$with_xfrdfile; xfrdfile=$withval +# Check whether --with-xfrdir was given. +if test "${with_xfrdir+set}" = set; then : + withval=$with_xfrdir; xfrdir=$withval fi cat >>confdefs.h <<_ACEOF -#define XFRDFILE "`eval echo $xfrdfile`" +#define XFRDIR "`eval echo $xfrdir`" _ACEOF +# nsd sbin location. tmpinstantiate execprefix with defaults if not yet done. +if test "x${exec_prefix}" = "xNONE"; then + if test "x${prefix}" = "xNONE"; then exec_prefix="$ac_default_prefix" + else exec_prefix="${prefix}"; fi + nsd_start_path="`eval echo $sbindir`/nsd" + exec_prefix="NONE" +else + nsd_start_path="`eval echo $sbindir`/nsd" +fi + +cat >>confdefs.h <<_ACEOF +#define NSD_START_PATH "$nsd_start_path" +_ACEOF + + # # Determine default chroot directory # @@ -3719,6 +3991,7 @@ _ACEOF fi + # # Determine the user name to drop privileges to # @@ -3755,7 +4028,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3801,7 +4074,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3841,7 +4114,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3894,7 +4167,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3935,7 +4208,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue @@ -3993,7 +4266,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4037,7 +4310,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4233,8 +4506,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <stdarg.h> #include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> +struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -4396,7 +4668,7 @@ case $as_dir/ in #(( # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. @@ -4470,7 +4742,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LEX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4502,7 +4774,8 @@ a { ECHO; } b { REJECT; } c { yymore (); } d { yyless (1); } -e { yyless (input () != 0); } +e { /* IRIX 6.5 flex 2.5.4 underquotes its yyless argument. */ + yyless ((input () != 0)); } f { unput (yytext[0]); } . { BEGIN INITIAL; } %% @@ -4628,7 +4901,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_YACC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4660,6 +4933,40 @@ test -n "$YACC" || YACC="yacc" # Checks for typedefs, structures, and compiler characteristics. +# if cflags not set by user, check O3, then O2, else elide O2 flag +if test "x$ac_cv_env_CFLAGS_set" = "x"; then + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -O3" >&5 +$as_echo_n "checking whether $CC supports -O3... " >&6; } +cache=`echo O3 | sed 'y%.=/+-%___p_%'` +if eval \${cv_prog_cc_flag_$cache+:} false; then : + $as_echo_n "(cached) " >&6 +else + +echo 'void f(){}' >conftest.c +if test -z "`$CC -O3 -c conftest.c 2>&1`"; then +eval "cv_prog_cc_flag_$cache=yes" +else +eval "cv_prog_cc_flag_$cache=no" +fi +rm -f conftest* + +fi + +if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +: + + CFLAGS=`echo $CFLAGS | sed -e "s/-O2//g"`" -O3" + CFLAGS=`echo $CFLAGS` + +else +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +: + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -O2" >&5 @@ -4691,6 +4998,54 @@ $as_echo "no" >&6; } CFLAGS=`echo $CFLAGS | sed -e "s/-O2//g"` fi + +fi + +fi + + # Check whether --enable-flto was given. +if test "${enable_flto+set}" = set; then : + enableval=$enable_flto; +fi + + if test "x$enable_flto" != "xno"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC supports -flto" >&5 +$as_echo_n "checking if $CC supports -flto... " >&6; } + BAKCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -flto" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + if $CC $CFLAGS -o conftest conftest.c 2>&1 | grep "warning: no debug symbols in executable" >/dev/null; then + CFLAGS="$BAKCFLAGS" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi + rm -f conftest conftest.c conftest.o + +else + CFLAGS="$BAKCFLAGS" ; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 $as_echo_n "checking for an ANSI C-conforming const... " >&6; } if ${ac_cv_c_const+:} false; then : @@ -4702,11 +5057,11 @@ else int main () { -/* FIXME: Include the comments suggested by Paul. */ + #ifndef __cplusplus - /* Ultrix mips cc rejects this. */ + /* Ultrix mips cc rejects this sort of thing. */ typedef int charset[2]; - const charset cs; + const charset cs = { 0, 0 }; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; @@ -4723,8 +5078,9 @@ main () ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this. */ - char *t; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; @@ -4740,10 +5096,10 @@ main () iptr p = 0; ++p; } - { /* AIX XL C 1.02.0.0 rejects this saying + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; }; - struct s *b; b->j = 5; + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; @@ -5054,6 +5410,327 @@ fi # http://www.gnu.org/software/ac-archive/htmldoc/check_ssl.html and # modified for NSD. +# check for libevent + +# Check whether --with-libevent was given. +if test "${with_libevent+set}" = set; then : + withval=$with_libevent; +else + withval="yes" +fi + +if test x_$withval = x_yes -o x_$withval != x_no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libevent" >&5 +$as_echo_n "checking for libevent... " >&6; } + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" + fi + for dir in $withval; do + thedir="$dir" + if test -f "$dir/include/event.h"; then + found_libevent="yes" + if test "$thedir" != "/usr"; then + CPPFLAGS="$CPPFLAGS -I$thedir/include" + fi + break; + fi + done + if test x_$found_libevent != x_yes; then + if test -f "$dir/event.h" -a \( -f "$dir/libevent.la" -o -f "$dir/libev.la" \) ; then + # libevent source directory + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $thedir" >&5 +$as_echo "found in $thedir" >&6; } + CPPFLAGS="$CPPFLAGS -I$thedir -I$thedir/include" + # remove evdns from linking + ev_files_o=`ls $thedir/*.o | grep -v evdns\.o | grep -v bufferevent_openssl\.o` + cp $ev_files_o . + LDFLAGS="$ev_files_o $LDFLAGS -lm" + else + as_fn_error $? "Cannot find the libevent library. +You can restart ./configure --with-libevent=no to use a builtin alternative." "$LINENO" 5 + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $thedir" >&5 +$as_echo "found in $thedir" >&6; } + if test "$thedir" != "/usr" -a "$thedir" != ""; then + LDFLAGS="$LDFLAGS -L$thedir/lib" + + if test "x$enable_rpath" = xyes; then + if echo "$thedir/lib" | grep "^/" >/dev/null; then + RUNTIME_PATH="$RUNTIME_PATH -R$thedir/lib" + fi + fi + + fi + fi + # check for library used by libevent after 1.3c + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 +$as_echo_n "checking for library containing clock_gettime... " >&6; } +if ${ac_cv_search_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +for ac_lib in '' rt; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_clock_gettime=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_clock_gettime+:} false; then : + break +fi +done +if ${ac_cv_search_clock_gettime+:} false; then : + +else + ac_cv_search_clock_gettime=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 +$as_echo "$ac_cv_search_clock_gettime" >&6; } +ac_res=$ac_cv_search_clock_gettime +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + + # is the event.h header libev or libevent? + for ac_header in event.h +do : + ac_fn_c_check_header_compile "$LINENO" "event.h" "ac_cv_header_event_h" "$ac_includes_default +" +if test "x$ac_cv_header_event_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EVENT_H 1 +_ACEOF + +fi + +done + + ac_fn_c_check_decl "$LINENO" "EV_VERSION_MAJOR" "ac_cv_have_decl_EV_VERSION_MAJOR" "$ac_includes_default +#include <event.h> + +" +if test "x$ac_cv_have_decl_EV_VERSION_MAJOR" = xyes; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_set" >&5 +$as_echo_n "checking for library containing event_set... " >&6; } +if ${ac_cv_search_event_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char event_set (); +int +main () +{ +return event_set (); + ; + return 0; +} +_ACEOF +for ac_lib in '' ev; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_event_set=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_event_set+:} false; then : + break +fi +done +if ${ac_cv_search_event_set+:} false; then : + +else + ac_cv_search_event_set=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_set" >&5 +$as_echo "$ac_cv_search_event_set" >&6; } +ac_res=$ac_cv_search_event_set +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_set" >&5 +$as_echo_n "checking for library containing event_set... " >&6; } +if ${ac_cv_search_event_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char event_set (); +int +main () +{ +return event_set (); + ; + return 0; +} +_ACEOF +for ac_lib in '' event; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_event_set=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_event_set+:} false; then : + break +fi +done +if ${ac_cv_search_event_set+:} false; then : + +else + ac_cv_search_event_set=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_set" >&5 +$as_echo "$ac_cv_search_event_set" >&6; } +ac_res=$ac_cv_search_event_set +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + +fi + + for ac_func in event_base_free +do : + ac_fn_c_check_func "$LINENO" "event_base_free" "ac_cv_func_event_base_free" +if test "x$ac_cv_func_event_base_free" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EVENT_BASE_FREE 1 +_ACEOF + +fi +done + # only in libevent 1.2 and later + for ac_func in event_base_once +do : + ac_fn_c_check_func "$LINENO" "event_base_once" "ac_cv_func_event_base_once" +if test "x$ac_cv_func_event_base_once" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EVENT_BASE_ONCE 1 +_ACEOF + +fi +done + # only in libevent 1.4.1 and later + for ac_func in event_base_new +do : + ac_fn_c_check_func "$LINENO" "event_base_new" "ac_cv_func_event_base_new" +if test "x$ac_cv_func_event_base_new" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EVENT_BASE_NEW 1 +_ACEOF + +fi +done + # only in libevent 1.4.1 and later + for ac_func in event_base_get_method +do : + ac_fn_c_check_func "$LINENO" "event_base_get_method" "ac_cv_func_event_base_get_method" +if test "x$ac_cv_func_event_base_get_method" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EVENT_BASE_GET_METHOD 1 +_ACEOF + +fi +done + # only in libevent 1.4.3 and later + for ac_func in ev_loop +do : + ac_fn_c_check_func "$LINENO" "ev_loop" "ac_cv_func_ev_loop" +if test "x$ac_cv_func_ev_loop" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EV_LOOP 1 +_ACEOF + +fi +done + # only in libev. (tested on 3.51) + for ac_func in ev_default_loop +do : + ac_fn_c_check_func "$LINENO" "ev_default_loop" "ac_cv_func_ev_default_loop" +if test "x$ac_cv_func_ev_default_loop" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EV_DEFAULT_LOOP 1 +_ACEOF + +fi +done + # only in libev. (tested on 4.00) +else + +$as_echo "#define USE_MINI_EVENT 1" >>confdefs.h + +fi + # Checks for header files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } @@ -5461,6 +6138,217 @@ _ACEOF fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if nonblocking sockets work" >&5 +$as_echo_n "checking if nonblocking sockets work... " >&6; } +if echo $target | grep mingw32 >/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no (windows)" >&5 +$as_echo "no (windows)" >&6; } + +$as_echo "#define NONBLOCKING_IS_BROKEN 1" >>confdefs.h + +else +if test "$cross_compiling" = yes; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: crosscompile(yes)" >&5 +$as_echo "crosscompile(yes)" >&6; } + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +int main(void) +{ + int port; + int sfd, cfd; + int num = 10; + int i, p; + struct sockaddr_in a; + /* test if select and nonblocking reads work well together */ + /* open port. + fork child to send 10 messages. + select to read. + then try to nonblocking read the 10 messages + then, nonblocking read must give EAGAIN + */ + + port = 12345 + (time(0)%32); + sfd = socket(PF_INET, SOCK_DGRAM, 0); + if(sfd == -1) { + perror("socket"); + return 1; + } + memset(&a, 0, sizeof(a)); + a.sin_family = AF_INET; + a.sin_port = htons(port); + a.sin_addr.s_addr = inet_addr("127.0.0.1"); + if(bind(sfd, (struct sockaddr*)&a, sizeof(a)) < 0) { + perror("bind"); + return 1; + } + if(fcntl(sfd, F_SETFL, O_NONBLOCK) == -1) { + perror("fcntl"); + return 1; + } + + cfd = socket(PF_INET, SOCK_DGRAM, 0); + if(cfd == -1) { + perror("client socket"); + return 1; + } + a.sin_port = 0; + if(bind(cfd, (struct sockaddr*)&a, sizeof(a)) < 0) { + perror("client bind"); + return 1; + } + a.sin_port = htons(port); + + /* no handler, causes exit in 10 seconds */ + alarm(10); + + /* send and receive on the socket */ + if((p=fork()) == 0) { + for(i=0; i<num; i++) { + if(sendto(cfd, &i, sizeof(i), 0, + (struct sockaddr*)&a, sizeof(a)) < 0) { + perror("sendto"); + return 1; + } + } + } else { + /* parent */ + fd_set rset; + int x; + if(p == -1) { + perror("fork"); + return 1; + } + FD_ZERO(&rset); + FD_SET(sfd, &rset); + if(select(sfd+1, &rset, NULL, NULL, NULL) < 1) { + perror("select"); + return 1; + } + i = 0; + while(i < num) { + if(recv(sfd, &x, sizeof(x), 0) != sizeof(x)) { + if(errno == EAGAIN) + continue; + perror("recv"); + return 1; + } + i++; + } + /* now we want to get EAGAIN: nonblocking goodness */ + errno = 0; + recv(sfd, &x, sizeof(x), 0); + if(errno != EAGAIN) { + perror("trying to recv again"); + return 1; + } + /* EAGAIN encountered */ + } + + close(sfd); + close(cfd); + return 0; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +$as_echo "#define NONBLOCKING_IS_BROKEN 1" >>confdefs.h + + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether mkdir has one arg" >&5 +$as_echo_n "checking whether mkdir has one arg... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include <stdio.h> +#include <unistd.h> +#ifdef HAVE_WINSOCK2_H +#include <winsock2.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +int +main () +{ + + (void)mkdir("directory"); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define MKDIR_HAS_ONE_ARG 1" >>confdefs.h + + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + +# set -I. and -Isrcdir +if test -n "$CPPFLAGS"; then + CPPFLAGS="$CPPFLAGS -I." +else + CPPFLAGS="-I." +fi +if test "$srcdir" != "."; then + CPPFLAGS="$CPPFLAGS -I$srcdir" +fi + @@ -6806,8 +7694,76 @@ _ACEOF esac rm -rf conftest* fi + + +fi + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void*" >&5 +$as_echo_n "checking size of void*... " >&6; } +if ${ac_cv_sizeof_voidp+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void*))" "ac_cv_sizeof_voidp" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_voidp" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (void*) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_voidp=0 + fi fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_voidp" >&5 +$as_echo "$ac_cv_sizeof_voidp" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_VOIDP $ac_cv_sizeof_voidp +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of off_t" >&5 +$as_echo_n "checking size of off_t... " >&6; } +if ${ac_cv_sizeof_off_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (off_t))" "ac_cv_sizeof_off_t" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_off_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (off_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_off_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_off_t" >&5 +$as_echo "$ac_cv_sizeof_off_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_OFF_T $ac_cv_sizeof_off_t +_ACEOF + + for ac_func in arc4random arc4random_uniform do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` @@ -6820,7 +7776,7 @@ _ACEOF fi done -for ac_func in tzset alarm chroot dup2 endpwent gethostname memset memcpy socket strcasecmp strchr strdup strerror strncasecmp strtol writev getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask strptime setusercontext initgroups setresuid setreuid setresgid setregid getpwnam mmap +for ac_func in tzset alarm chroot dup2 endpwent gethostname memset memcpy pwrite socket strcasecmp strchr strdup strerror strncasecmp strtol writev getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask strptime setusercontext initgroups setresuid setreuid setresgid setregid getpwnam mmap do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -6833,6 +7789,91 @@ fi done +# Check whether --enable-recvmmsg was given. +if test "${enable_recvmmsg+set}" = set; then : + enableval=$enable_recvmmsg; +fi + +case "$enable_recvmmsg" in + no) + ;; + yes|*) + ac_fn_c_check_func "$LINENO" "recvmmsg" "ac_cv_func_recvmmsg" +if test "x$ac_cv_func_recvmmsg" = xyes; then : + +if test "$cross_compiling" = yes; then : + + +$as_echo "#define HAVE_RECVMMSG 1" >>confdefs.h + + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include <sys/socket.h> +#include <errno.h> +int main(void) +{ + int s = socket(AF_INET, SOCK_DGRAM, 0); + int r = recvmmsg(s, 0, 0, 0, 0) == -1 && errno == ENOSYS; + close(s); + return r; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + +$as_echo "#define HAVE_RECVMMSG 1" >>confdefs.h + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi + + ac_fn_c_check_func "$LINENO" "sendmmsg" "ac_cv_func_sendmmsg" +if test "x$ac_cv_func_sendmmsg" = xyes; then : + +if test "$cross_compiling" = yes; then : + + +$as_echo "#define HAVE_SENDMMSG 1" >>confdefs.h + + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include <sys/socket.h> +#include <errno.h> +int main(void) +{ + int s = socket(AF_INET, SOCK_DGRAM, 0); + int r = sendmmsg(s, 0, 0, 0) == -1 && errno == ENOSYS; + close(s); + return r; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + + +$as_echo "#define HAVE_SENDMMSG 1" >>confdefs.h + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi + + + ;; +esac + # check if setreuid en setregid fail, on MacOSX10.4(darwin8). if echo $build_os | grep darwin8 > /dev/null; then @@ -7081,7 +8122,7 @@ _ACEOF cat >>confdefs.h <<_ACEOF -#define TCP_BACKLOG 5 +#define TCP_BACKLOG 256 _ACEOF @@ -7115,6 +8156,16 @@ cat >>confdefs.h <<_ACEOF _ACEOF +cat >>confdefs.h <<_ACEOF +#define NSD_CONTROL_PORT 8952 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define NSD_CONTROL_VERSION 1 +_ACEOF + + facility=LOG_DAEMON # Check whether --with-facility was given. @@ -7476,6 +8527,147 @@ done fi +if test x$HAVE_SSL = x"yes"; then + +# check if libssl needs libdl +BAKLIBS="$LIBS" +LIBS="-lssl $LIBS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libssl needs libdl" >&5 +$as_echo_n "checking if libssl needs libdl... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char SSL_CTX_new (); +int +main () +{ +return SSL_CTX_new (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + LIBS="$BAKLIBS" + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + LIBS="$BAKLIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5 +$as_echo_n "checking for library containing dlopen... " >&6; } +if ${ac_cv_search_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +for ac_lib in '' dl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_dlopen=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_dlopen+:} false; then : + break +fi +done +if ${ac_cv_search_dlopen+:} false; then : + +else + ac_cv_search_dlopen=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5 +$as_echo "$ac_cv_search_dlopen" >&6; } +ac_res=$ac_cv_search_dlopen +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + SSL_LIBS="-lssl" + + for ac_header in openssl/ssl.h +do : + ac_fn_c_check_header_compile "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default +" +if test "x$ac_cv_header_openssl_ssl_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_OPENSSL_SSL_H 1 +_ACEOF + +fi + +done + + for ac_header in openssl/err.h +do : + ac_fn_c_check_header_compile "$LINENO" "openssl/err.h" "ac_cv_header_openssl_err_h" "$ac_includes_default +" +if test "x$ac_cv_header_openssl_err_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_OPENSSL_ERR_H 1 +_ACEOF + +fi + +done + + for ac_header in openssl/rand.h +do : + ac_fn_c_check_header_compile "$LINENO" "openssl/rand.h" "ac_cv_header_openssl_rand_h" "$ac_includes_default +" +if test "x$ac_cv_header_openssl_rand_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_OPENSSL_RAND_H 1 +_ACEOF + +fi + +done + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No SSL, therefore remote-control is disabled" >&5 +$as_echo "$as_me: WARNING: No SSL, therefore remote-control is disabled" >&2;} +fi # Check whether --enable-nsec3 was given. if test "${enable_nsec3+set}" = set; then : @@ -7485,29 +8677,24 @@ fi case "$enable_nsec3" in no) ;; - yes|*) + yes) cat >>confdefs.h <<_ACEOF #define NSEC3 /**/ _ACEOF - ;; -esac - -# Check whether --enable-full-prehash was given. -if test "${enable_full_prehash+set}" = set; then : - enableval=$enable_full_prehash; -fi - -case "$enable_full_prehash" in - no) ;; - yes|*) + *) + if test x$HAVE_SSL = x"yes"; then cat >>confdefs.h <<_ACEOF -#define FULL_PREHASH /**/ +#define NSEC3 /**/ _ACEOF + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No SSL, therefore NSEC3 is disabled" >&5 +$as_echo "$as_me: WARNING: No SSL, therefore NSEC3 is disabled" >&2;} + fi ;; esac @@ -7639,33 +8826,6 @@ _ACEOF ;; esac -# -# Default zonestatsfile -# -zonestatsfile=${localstatedir}/log/nsd.stats - - -cat >>confdefs.h <<_ACEOF -#define ZONESTATSFILE "`eval echo $zonestatsfile`" -_ACEOF - -# Check whether --enable-zone_stats was given. -if test "${enable_zone_stats+set}" = set; then : - enableval=$enable_zone_stats; -fi - -case "$enable_zone_stats" in - yes) - -cat >>confdefs.h <<_ACEOF -#define USE_ZONE_STATS /**/ -_ACEOF - - ;; - no|*) - ;; -esac - @@ -7693,26 +8853,14 @@ fi # big fat warning if test "$enable_checking" = "yes"; then - echo "*************************************************" - echo "* You have activated \"--enable-checking\" *" - echo "* *" - echo "* This will instruct NSD to be stricter *" - echo "* when validating its input. This could lead *" - echo "* to a reduced service level. *" - echo "* *" - echo "*************************************************" -fi - -if test "$enable_zone_stats" = "yes"; then - echo "*************************************************" - echo "* You have activated \"--enable-zone-stats\" *" - echo "* *" - echo "* This will make NSD maintain statistics *" - echo "* on a per zone basis. This could lead to *" - echo "* a reduced service level and an *" - echo "* a larger memory footprint. *" - echo "* *" - echo "*************************************************" + echo "************************************************" + echo "* You have activated \"--enable-checking\" *" + echo "* *" + echo "* This will instruct NSD to be stricter *" + echo "* when validating its input. This could lead *" + echo "* to a reduced service level. *" + echo "* *" + echo "************************************************" fi ac_config_files="$ac_config_files Makefile" @@ -8124,16 +9272,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -8193,28 +9341,16 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -8235,8 +9371,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by NSD $as_me 3.2.16, which was -generated by GNU Autoconf 2.68. Invocation command line was +This file was extended by NSD $as_me 4.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -8297,11 +9433,11 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -NSD config.status 3.2.16 -configured by $0, generated by GNU Autoconf 2.68, +NSD config.status 4.0.0 +configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -8391,7 +9527,7 @@ fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' diff --git a/usr.sbin/nsd/configure.ac b/usr.sbin/nsd/configure.ac index 514b8d92853..565c9948fc9 100644 --- a/usr.sbin/nsd/configure.ac +++ b/usr.sbin/nsd/configure.ac @@ -4,7 +4,7 @@ dnl sinclude(acx_nlnetlabs.m4) -AC_INIT(NSD,3.2.16,nsd-bugs@nlnetlabs.nl) +AC_INIT(NSD,4.0.0,nsd-bugs@nlnetlabs.nl) AC_CONFIG_HEADER([config.h]) AC_AIX @@ -34,6 +34,7 @@ configdir=$sysconfdir/nsd AC_ARG_WITH([configdir], AC_HELP_STRING([--with-configdir=dir], [NSD configuration directory]), [configdir=$withval]) +AC_DEFINE_UNQUOTED(CONFIGDIR, ["`eval echo $configdir`"], [NSD config dir]) AC_SUBST(configdir) # @@ -48,23 +49,12 @@ AC_SUBST(nsd_conf_file) AC_DEFINE_UNQUOTED(CONFIGFILE, ["`eval echo $nsd_conf_file`"], [Pathname to the NSD configuration file]) # -# Determine start and kill priorities -start_priority=45 -AC_ARG_WITH([start_priority], - AC_HELP_STRING([--with-start_priority=number], [NSD startup priority]), - [start_priority=$withval]) -AC_SUBST(start_priority) - -kill_priority=74 -AC_ARG_WITH([kill_priority], - AC_HELP_STRING([--with-kill_priority=number], [NSD shutdown priority]), - [kill_priority=$withval]) -AC_SUBST(kill_priority) - -# # Default logfile # logfile=${localstatedir}/log/nsd.log +AC_ARG_WITH([logfile], + AC_HELP_STRING([--with-logfile=path], [Pathname to the default log file]), + [logfile=$withval]) AC_SUBST(logfile) # @@ -113,13 +103,6 @@ AC_ARG_WITH([zonesdir], AC_SUBST(zonesdir) AC_DEFINE_UNQUOTED(ZONESDIR, ["`eval echo $zonesdir`"], [NSD default location for zone files. Empty string or NULL to disable.]) -# default diff file location. -difffile=${dbdir}/ixfr.db -AC_ARG_WITH([difffile], AC_HELP_STRING([--with-difffile=path], - [Pathname to the NSD diff transfer journal file]), [difffile=$withval]) -AC_DEFINE_UNQUOTED(DIFFFILE, ["`eval echo $difffile`"], [Pathname to the NSD diff transfer journal file.]) -AC_SUBST(difffile) - # default xfrd file location. xfrdfile=${dbdir}/xfrd.state AC_ARG_WITH([xfrdfile], AC_HELP_STRING([--with-xfrdfile=path], @@ -127,6 +110,31 @@ AC_ARG_WITH([xfrdfile], AC_HELP_STRING([--with-xfrdfile=path], AC_DEFINE_UNQUOTED(XFRDFILE, ["`eval echo $xfrdfile`"], [Pathname to the NSD xfrd zone timer state file.]) AC_SUBST(xfrdfile) +# default zonelist file location. +zonelistfile=${dbdir}/zone.list +AC_ARG_WITH([zonelistfile], AC_HELP_STRING([--with-zonelistfile=path], + [Pathname to the NSD zone list file]), [zonelistfile=$withval]) +AC_DEFINE_UNQUOTED(ZONELISTFILE, ["`eval echo $zonelistfile`"], [Pathname to the NSD zone list file.]) +AC_SUBST(zonelistfile) + +# default xfr dir location. +xfrdir="/tmp" +AC_ARG_WITH([xfrdir], AC_HELP_STRING([--with-xfrdir=path], + [Pathname to where the NSD transfer dir is created]), [xfrdir=$withval]) +AC_DEFINE_UNQUOTED(XFRDIR, ["`eval echo $xfrdir`"], [Pathname to where the NSD transfer dir is created.]) +AC_SUBST(xfrdir) + +# nsd sbin location. tmpinstantiate execprefix with defaults if not yet done. +if test "x${exec_prefix}" = "xNONE"; then + if test "x${prefix}" = "xNONE"; then exec_prefix="$ac_default_prefix" + else exec_prefix="${prefix}"; fi + nsd_start_path="`eval echo $sbindir`/nsd" + exec_prefix="NONE" +else + nsd_start_path="`eval echo $sbindir`/nsd" +fi +AC_DEFINE_UNQUOTED(NSD_START_PATH, ["$nsd_start_path"], [Pathname to start nsd from nsd-control]) + # # Determine default chroot directory # @@ -136,6 +144,7 @@ AC_ARG_WITH([chroot], chrootdir=$withval AC_DEFINE_UNQUOTED(CHROOTDIR, ["`eval echo $chrootdir`"], [NSD default chroot directory]) ]) +AC_SUBST(chrootdir) # # Determine the user name to drop privileges to @@ -246,7 +255,17 @@ fi ])dnl # Checks for typedefs, structures, and compiler characteristics. -CHECK_COMPILER_FLAG(O2, [], [ CFLAGS=`echo $CFLAGS | sed -e "s/-O2//g"` ]) +# if cflags not set by user, check O3, then O2, else elide O2 flag +if test "x$ac_cv_env_CFLAGS_set" = "x"; then + CHECK_COMPILER_FLAG(O3, [ + CFLAGS=`echo $CFLAGS | sed -e "s/-O2//g"`" -O3" + dnl remove double spaces + CFLAGS=`echo $CFLAGS` + ], [ + CHECK_COMPILER_FLAG(O2, [], [ CFLAGS=`echo $CFLAGS | sed -e "s/-O2//g"` ]) + ]) +fi +ACX_CHECK_FLTO AC_C_CONST AC_C_INLINE AC_TYPE_UID_T @@ -307,6 +326,69 @@ AC_DEFUN([CHECK_SSL], [ fi ])dnl +# check for libevent +AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname], + [use libevent (will check /usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr or you can specify an explicit path), useful when the zone count is high.]), + [ ],[ withval="yes" ]) +if test x_$withval = x_yes -o x_$withval != x_no; then + AC_MSG_CHECKING(for libevent) + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" + fi + for dir in $withval; do + thedir="$dir" + if test -f "$dir/include/event.h"; then + found_libevent="yes" + dnl assume /usr is in default path. + if test "$thedir" != "/usr"; then + CPPFLAGS="$CPPFLAGS -I$thedir/include" + fi + break; + fi + done + if test x_$found_libevent != x_yes; then + if test -f "$dir/event.h" -a \( -f "$dir/libevent.la" -o -f "$dir/libev.la" \) ; then + # libevent source directory + AC_MSG_RESULT(found in $thedir) + CPPFLAGS="$CPPFLAGS -I$thedir -I$thedir/include" + # remove evdns from linking + ev_files_o=`ls $thedir/*.o | grep -v evdns\.o | grep -v bufferevent_openssl\.o` + cp $ev_files_o . + LDFLAGS="$ev_files_o $LDFLAGS -lm" + else + AC_MSG_ERROR([Cannot find the libevent library. +You can restart ./configure --with-libevent=no to use a builtin alternative.]) + fi + else + AC_MSG_RESULT(found in $thedir) + dnl assume /usr is in default path, do not add "". + if test "$thedir" != "/usr" -a "$thedir" != ""; then + LDFLAGS="$LDFLAGS -L$thedir/lib" + ACX_RUNTIME_PATH_ADD([$thedir/lib]) + fi + fi + # check for library used by libevent after 1.3c + AC_SEARCH_LIBS([clock_gettime], [rt]) + + # is the event.h header libev or libevent? + AC_CHECK_HEADERS([event.h],,, [AC_INCLUDES_DEFAULT]) + AC_CHECK_DECL(EV_VERSION_MAJOR, [ + AC_SEARCH_LIBS(event_set, [ev]) + ],[ + AC_SEARCH_LIBS(event_set, [event]) + ],[AC_INCLUDES_DEFAULT +#include <event.h> + ]) + AC_CHECK_FUNCS([event_base_free]) # only in libevent 1.2 and later + AC_CHECK_FUNCS([event_base_once]) # only in libevent 1.4.1 and later + AC_CHECK_FUNCS([event_base_new]) # only in libevent 1.4.1 and later + AC_CHECK_FUNCS([event_base_get_method]) # only in libevent 1.4.3 and later + AC_CHECK_FUNCS([ev_loop]) # only in libev. (tested on 3.51) + AC_CHECK_FUNCS([ev_default_loop]) # only in libev. (tested on 4.00) +else + AC_DEFINE(USE_MINI_EVENT, 1, [Define if you want to use internal select based events]) +fi + # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT @@ -373,13 +455,13 @@ AC_DEFUN([AC_CHECK_STRPTIME_WORKS], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING(whether strptime works) if test c${cross_compiling} = cno; then -AC_TRY_RUN([ +AC_RUN_IFELSE([AC_LANG_SOURCE([[ #define _XOPEN_SOURCE #include <time.h> int main(void) { struct tm tm; char *res; res = strptime("20070207111842", "%Y%m%d%H%M%S", &tm); if (!res) return 1; return 0; } -] , [eval "ac_cv_c_strptime_works=yes"], [eval "ac_cv_c_strptime_works=no"]) +]])] , [eval "ac_cv_c_strptime_works=yes"], [eval "ac_cv_c_strptime_works=no"]) else eval "ac_cv_c_strptime_works=maybe" fi @@ -395,6 +477,18 @@ AC_SEARCH_LIBS(inet_pton, [nsl]) AC_SEARCH_LIBS(socket, [socket]) AC_CHECK_STRPTIME_WORKS +ACX_CHECK_NONBLOCKING_BROKEN +ACX_MKDIR_ONE_ARG + +# set -I. and -Isrcdir +if test -n "$CPPFLAGS"; then + CPPFLAGS="$CPPFLAGS -I." +else + CPPFLAGS="-I." +fi +if test "$srcdir" != "."; then + CPPFLAGS="$CPPFLAGS -I$srcdir" +fi dnl LIBGTOP_CHECK_TYPE dnl Stolen from Gnome's anjuta @@ -473,8 +567,51 @@ AC_FUNC_MALLOC AC_TYPE_SIGNAL AC_FUNC_FSEEKO AC_SYS_LARGEFILE +AC_CHECK_SIZEOF(void*) +AC_CHECK_SIZEOF(off_t) AC_CHECK_FUNCS([arc4random arc4random_uniform]) -AC_CHECK_FUNCS([tzset alarm chroot dup2 endpwent gethostname memset memcpy socket strcasecmp strchr strdup strerror strncasecmp strtol writev getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask strptime setusercontext initgroups setresuid setreuid setresgid setregid getpwnam mmap]) +AC_CHECK_FUNCS([tzset alarm chroot dup2 endpwent gethostname memset memcpy pwrite socket strcasecmp strchr strdup strerror strncasecmp strtol writev getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask strptime setusercontext initgroups setresuid setreuid setresgid setregid getpwnam mmap]) + +AC_ARG_ENABLE(recvmmsg, AC_HELP_STRING([--disable-recvmmsg], [Disable recvmmsg and sendmmsg compilation for compatibility with more Linux kernel versions])) +case "$enable_recvmmsg" in + no) + ;; + yes|*) + AC_CHECK_FUNC([recvmmsg], [ +AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include <sys/socket.h> +#include <errno.h> +int main(void) +{ + int s = socket(AF_INET, SOCK_DGRAM, 0); + int r = recvmmsg(s, 0, 0, 0, 0) == -1 && errno == ENOSYS; + close(s); + return r; +} +]])], [ +AC_DEFINE([HAVE_RECVMMSG], [1], [Define if recvmmsg is implemented])], [ +], [ +AC_DEFINE([HAVE_RECVMMSG], [1], [Define if recvmmsg exists])] +)]) + AC_CHECK_FUNC([sendmmsg], [ +AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include <sys/socket.h> +#include <errno.h> +int main(void) +{ + int s = socket(AF_INET, SOCK_DGRAM, 0); + int r = sendmmsg(s, 0, 0, 0) == -1 && errno == ENOSYS; + close(s); + return r; +} +]])], [ +AC_DEFINE([HAVE_SENDMMSG], [1], [Define if sendmmsg is implemented])], [ +], [ +AC_DEFINE([HAVE_SENDMMSG], [1], [Define if sendmmsg exists])] +)]) + + ;; +esac # check if setreuid en setregid fail, on MacOSX10.4(darwin8). if echo $build_os | grep darwin8 > /dev/null; then @@ -520,13 +657,15 @@ dnl Some random defines's dnl AC_DEFINE_UNQUOTED([IDENTITY], ["unidentified server"], [Define to the default nsd identity.]) AC_DEFINE_UNQUOTED([VERSION], [PACKAGE_STRING], [Define to the NSD version to answer version.server query.]) -AC_DEFINE_UNQUOTED([TCP_BACKLOG], [5], [Define to the backlog to be used with listen.]) +AC_DEFINE_UNQUOTED([TCP_BACKLOG], [256], [Define to the backlog to be used with listen.]) AC_DEFINE_UNQUOTED([TCP_PORT], ["53"], [Define to the default tcp port.]) AC_DEFINE_UNQUOTED([TCP_MAX_MESSAGE_LEN], [65535], [Define to the default maximum message length.]) AC_DEFINE_UNQUOTED([UDP_PORT], ["53"], [Define to the default udp port.]) AC_DEFINE_UNQUOTED([UDP_MAX_MESSAGE_LEN], [512], [Define to the default maximum udp message length.]) AC_DEFINE_UNQUOTED([EDNS_MAX_MESSAGE_LEN], [4096], [Define to the default maximum message length with EDNS.]) AC_DEFINE_UNQUOTED([MAXSYSLOGMSGLEN], [512], [Define to the maximum message length to pass to syslog.]) +AC_DEFINE_UNQUOTED([NSD_CONTROL_PORT], [8952], [Define to the default nsd-control port.]) +AC_DEFINE_UNQUOTED([NSD_CONTROL_VERSION], [1], [Define to nsd-control proto version.]) dnl dnl Determine the syslog facility to use @@ -614,22 +753,30 @@ AC_SUBST(ratelimit) # we need SSL for TSIG (and maybe also for NSEC3). CHECK_SSL +if test x$HAVE_SSL = x"yes"; then + ACX_LIB_SSL + SSL_LIBS="-lssl" + AC_SUBST(SSL_LIBS) + AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT]) + AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT]) + AC_CHECK_HEADERS([openssl/rand.h],,, [AC_INCLUDES_DEFAULT]) +else + AC_MSG_WARN([No SSL, therefore remote-control is disabled]) +fi AC_ARG_ENABLE(nsec3, AC_HELP_STRING([--disable-nsec3], [Disable NSEC3 support])) case "$enable_nsec3" in no) ;; - yes|*) + yes) AC_DEFINE_UNQUOTED([NSEC3], [], [Define this to enable NSEC3 support.]) - ;; -esac - -AC_ARG_ENABLE(full-prehash, AC_HELP_STRING([--disable-full-prehash], [Disables NSEC3 full prehashing])) -case "$enable_full_prehash" in - no) ;; - yes|*) - AC_DEFINE_UNQUOTED([FULL_PREHASH], [], [Define this to enable NSEC3 full prehashing.]) + *) + if test x$HAVE_SSL = x"yes"; then + AC_DEFINE_UNQUOTED([NSEC3], [], [Define this to enable NSEC3 support.]) + else + AC_MSG_WARN([No SSL, therefore NSEC3 is disabled]) + fi ;; esac @@ -663,21 +810,6 @@ case "$enable_mmap" in ;; esac -# -# Default zonestatsfile -# -zonestatsfile=${localstatedir}/log/nsd.stats -AC_SUBST(zonestatsfile) -AC_DEFINE_UNQUOTED(ZONESTATSFILE, ["`eval echo $zonestatsfile`"], [Pathname to the NSD statistics file]) -AC_ARG_ENABLE(zone_stats, AC_HELP_STRING([--enable-zone-stats], [Maintain statistics per zone, instead of global statistics.])) -case "$enable_zone_stats" in - yes) - AC_DEFINE_UNQUOTED([USE_ZONE_STATS], [], [Define this to enable zone statistics.]) - ;; - no|*) - ;; -esac - AH_BOTTOM([ /* define before includes as it specifies what standard to use. */ #if (defined(HAVE_PSELECT) && !defined (HAVE_PSELECT_PROTO)) \ @@ -851,26 +983,14 @@ AH_BOTTOM([ # big fat warning if test "$enable_checking" = "yes"; then - echo "*************************************************" - echo "* You have activated \"--enable-checking\" *" - echo "* *" - echo "* This will instruct NSD to be stricter *" - echo "* when validating its input. This could lead *" - echo "* to a reduced service level. *" - echo "* *" - echo "*************************************************" -fi - -if test "$enable_zone_stats" = "yes"; then - echo "*************************************************" - echo "* You have activated \"--enable-zone-stats\" *" - echo "* *" - echo "* This will make NSD maintain statistics *" - echo "* on a per zone basis. This could lead to *" - echo "* a reduced service level and an *" - echo "* a larger memory footprint. *" - echo "* *" - echo "*************************************************" + echo "************************************************" + echo "* You have activated \"--enable-checking\" *" + echo "* *" + echo "* This will instruct NSD to be stricter *" + echo "* when validating its input. This could lead *" + echo "* to a reduced service level. *" + echo "* *" + echo "************************************************" fi AC_CONFIG_FILES([Makefile]) diff --git a/usr.sbin/nsd/dname.c b/usr.sbin/nsd/dname.c index d4af6329bd7..a2971982506 100644 --- a/usr.sbin/nsd/dname.c +++ b/usr.sbin/nsd/dname.c @@ -1,7 +1,7 @@ /* * dname.c -- Domain name handling. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -17,19 +17,11 @@ #include <limits.h> #include <stdio.h> #include <string.h> -#include <errno.h> #include "dns.h" #include "dname.h" #include "query.h" -/** - * The maximum number of labels is the maximum domain name, less 1 for - * the root (len == 0) label, divided by two (minimum non-root label of - * one character + length byte), then add back in one for the root label. - */ -#define MAXLABELS (((MAXDOMAINLEN - 1) / 2) + 1) - const dname_type * dname_make(region_type *region, const uint8_t *name, int normalize) { @@ -309,10 +301,8 @@ dname_is_subdomain(const dname_type *left, const dname_type *right) int -dname_compare(const void *vleft, const void *vright) +dname_compare(const dname_type *left, const dname_type *right) { - const dname_type* left = (const dname_type*) vleft; - const dname_type* right = (const dname_type*) vright; int result; uint8_t label_count; uint8_t i; @@ -392,29 +382,16 @@ const char * dname_to_string(const dname_type *dname, const dname_type *origin) { static char buf[MAXDOMAINLEN * 5]; - return dname_to_string_r(dname, origin, buf); -} - -const char * -dname_to_string_r(const dname_type *dname, const dname_type *origin, - char* buf) -{ size_t i; - size_t labels_to_convert = 0; + size_t labels_to_convert = dname->label_count - 1; int absolute = 1; char *dst; const uint8_t *src; - if (!dname) { - *buf = '\0'; - return buf; - } - dst = buf; + if (dname->label_count == 1) { - *dst++ = '.'; - *dst = '\0'; + strlcpy(buf, ".", sizeof(buf)); return buf; } - labels_to_convert = dname->label_count - 1; if (origin && dname_is_subdomain(dname, origin)) { int common_labels = dname_label_match_count(dname, origin); @@ -422,6 +399,7 @@ dname_to_string_r(const dname_type *dname, const dname_type *origin, absolute = 0; } + dst = buf; src = dname_name(dname); for (i = 0; i < labels_to_convert; ++i) { size_t len = label_length(src); @@ -517,60 +495,77 @@ dname_replace(region_type* region, return res; } -#ifndef FULL_PREHASH -/** - * Make wildcard synthesis. - * - */ -int -dname_make_wildcard(struct region *region, - struct dname const *dname, - struct dname const **wildcard) +char* wirelabel2str(const uint8_t* label) { - uint8_t name_size; - uint8_t label_count; - uint8_t *names; - uint8_t *labels; - struct dname *new_dname; - unsigned int i; - /* - * Checks: - * dname label_count + 1 < MAXLABELS - * dname size + 2 < MAXDOMAINLEN - */ - if (dname->label_count > (MAXLABELS - 1)) { - return EINVAL; - } - if (dname->name_size > (MAXDOMAINLEN - 2)) { - return EINVAL; + static char buf[MAXDOMAINLEN*5+3]; + char* p = buf; + uint8_t lablen; + lablen = *label++; + while(lablen--) { + uint8_t ch = *label++; + if (isalnum(ch) || ch == '-' || ch == '_') { + *p++ = ch; + } else if (ch == '.' || ch == '\\') { + *p++ = '\\'; + *p++ = ch; + } else { + snprintf(p, 5, "\\%03u", (unsigned int)ch); + p += 4; + } } + *p++ = 0; + return buf; +} - label_count = dname->label_count + 1; - name_size = dname->name_size + 2; - new_dname = (struct dname *) region_alloc(region, - sizeof(dname_type) + - (label_count * sizeof(uint8_t)) + - (name_size * sizeof(uint8_t))); - if (new_dname == NULL) { - return ENOMEM; +char* wiredname2str(const uint8_t* dname) +{ + static char buf[MAXDOMAINLEN*5+3]; + char* p = buf; + uint8_t lablen; + if(*dname == 0) { + strlcpy(buf, ".", sizeof(buf)); + return buf; } - new_dname->label_count = label_count; - new_dname->name_size = name_size; - labels = (uint8_t *) dname_label_offsets(new_dname); - memcpy(labels, dname_label_offsets(dname), - dname->label_count * sizeof(uint8_t)); - for (i = 0; i < dname->label_count; i++) { - labels[i] += 2; + lablen = *dname++; + while(lablen) { + while(lablen--) { + uint8_t ch = *dname++; + if (isalnum(ch) || ch == '-' || ch == '_') { + *p++ = ch; + } else if (ch == '.' || ch == '\\') { + *p++ = '\\'; + *p++ = ch; + } else { + snprintf(p, 5, "\\%03u", (unsigned int)ch); + p += 4; + } + } + lablen = *dname++; + *p++ = '.'; } - labels[i] = 0; - - names = (uint8_t *) dname_name(new_dname); - *names++ = '\001'; - *names++ = '*'; - memcpy(names, dname_name(dname), - dname->name_size * sizeof(uint8_t)); - *wildcard = new_dname; - return 0; + *p++ = 0; + return buf; } -#endif +int dname_equal_nocase(uint8_t* a, uint8_t* b, uint16_t len) +{ + uint8_t i, lablen; + while(len > 0) { + /* check labellen */ + if(*a != *b) + return 0; + lablen = *a++; + b++; + len--; + /* malformed or compression ptr; we stop scanning */ + if((lablen & 0xc0) || len < lablen) + return (memcmp(a, b, len) == 0); + /* check the label, lowercased */ + for(i=0; i<lablen; i++) { + if(DNAME_NORMALIZE(*a++) != DNAME_NORMALIZE(*b++)) + return 0; + } + len -= lablen; + } + return 1; +} diff --git a/usr.sbin/nsd/dns.c b/usr.sbin/nsd/dns.c index 7f33a294fda..fbec4f5a2d6 100644 --- a/usr.sbin/nsd/dns.c +++ b/usr.sbin/nsd/dns.c @@ -1,7 +1,7 @@ /* * dns.c -- DNS definitions. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * diff --git a/usr.sbin/nsd/dns.h b/usr.sbin/nsd/dns.h index c4aa5cdf935..79f8b678df2 100644 --- a/usr.sbin/nsd/dns.h +++ b/usr.sbin/nsd/dns.h @@ -1,7 +1,7 @@ /* * dns.h -- DNS definitions. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -176,6 +176,8 @@ typedef enum nsd_rc nsd_rc_type; #define EUI64ADDRLEN (64/8) #endif +#define NSEC3_HASH_LEN 20 + /* * The different types of RDATA wireformat data. */ diff --git a/usr.sbin/nsd/lookup3.c b/usr.sbin/nsd/lookup3.c index e76239ce6a4..64ebbf9d4e5 100644 --- a/usr.sbin/nsd/lookup3.c +++ b/usr.sbin/nsd/lookup3.c @@ -1,5 +1,5 @@ /* - February 2013(Matthijs) patch defines for BSD endianness, from Brad Smith. + February 2013(Wouter) patch defines for BSD endianness, from Brad Smith. January 2012(Wouter) added randomised initial value, fallout from 28c3. March 2007(Wouter) adapted from lookup3.c original, add config.h include. added #ifdef VALGRIND to remove 298,384,660 'unused variable k8' warnings. @@ -50,6 +50,9 @@ on 1 byte), but shoehorning those bytes into integers efficiently is messy. #include <time.h> /* defines time_t for timings in the test */ /*#include <stdint.h> defines uint32_t etc (from config.h) */ #include <sys/param.h> /* attempt to define endianness */ +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> /* attempt to define endianness (solaris) */ +#endif #ifdef linux # include <endian.h> /* attempt to define endianness */ #endif @@ -75,19 +78,25 @@ hash_set_raninit(uint32_t v) */ #if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ __BYTE_ORDER == __LITTLE_ENDIAN) || \ - (defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) && \ - _BYTE_ORDER == _LITTLE_ENDIAN) || \ (defined(i386) || defined(__i386__) || defined(__i486__) || \ - defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL) || defined(__x86)) # define HASH_LITTLE_ENDIAN 1 # define HASH_BIG_ENDIAN 0 #elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ __BYTE_ORDER == __BIG_ENDIAN) || \ - (defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && \ - _BYTE_ORDER == _BIG_ENDIAN) || \ - (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) + (defined(sparc) || defined(__sparc) || defined(__sparc__) || defined(POWERPC) || defined(mc68000) || defined(sel)) # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 1 +#elif defined(_MACHINE_ENDIAN_H_) +/* test for machine_endian_h protects failure if some are empty strings */ +# if defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && _BYTE_ORDER == _BIG_ENDIAN +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +# endif +# if defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) && _BYTE_ORDER == _LITTLE_ENDIAN +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +# endif /* _MACHINE_ENDIAN_H_ */ #else # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 0 diff --git a/usr.sbin/nsd/mkinstalldirs b/usr.sbin/nsd/mkinstalldirs index a246f542699..b61f354cf8e 100644 --- a/usr.sbin/nsd/mkinstalldirs +++ b/usr.sbin/nsd/mkinstalldirs @@ -4,7 +4,7 @@ # Created: 1993-05-16 # Public domain -# $Id: mkinstalldirs,v 1.2 2011/01/27 12:29:14 jakob Exp $ +# $Id: mkinstalldirs,v 1.3 2013/11/26 12:53:58 sthen Exp $ errstatus=0 diff --git a/usr.sbin/nsd/nsd-checkconf.8.in b/usr.sbin/nsd/nsd-checkconf.8.in index 66cb02cc4b5..a812f3710b3 100644 --- a/usr.sbin/nsd/nsd-checkconf.8.in +++ b/usr.sbin/nsd/nsd-checkconf.8.in @@ -1,5 +1,5 @@ -.TH "nsd\-checkconf" "8" "Jul 22, 2013" "NLnet Labs" "nsd 3.2.16" -.\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. +.TH "nsd\-checkconf" "8" "Oct 29, 2013" "NLnet Labs" "nsd 4.0.0" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .LP @@ -14,6 +14,8 @@ .IR option ] .RB [ \-z .IR zonename ] +.RB [ \-p +.IR pattern ] .RB [ \-s .IR keyname ] .I configfile @@ -24,10 +26,9 @@ reads a configuration file. It prints parse errors to standard error, and performs additional checks on the contents. The configfile format is described in nsd.conf(5). .P -The utility of this program is to check a config file for errors -before using it in nsd(8) or nsd\-zonec(8). This program can also be used -for shell scripts to access the nsd config file, using the \-o and -\-z options. +The utility of this program is to check a config file for errors before +using it in nsd(8). This program can also be used for shell scripts to +access the nsd config file, using the \-o and \-z options. .P .SH "OPTIONS" .TP @@ -41,12 +42,17 @@ Print usage help information and exit. .TP .B \-o\fI option Return only this option from the config file. This option can -to be used in conjunction with the +be used in conjunction with the .B \-z -option. +and the +.B \-p +option, or without them to query the server: section. The special value .I zones prints out a list of configured zones. +The special value +.I patterns +prints out a list of configured patterns. .P .RS This option is primarily used by @@ -62,6 +68,11 @@ option is not given, nothing is printed. Prints the key secret (base64 blob) configured for this key in the config file. Used to help shell scripts parse the config file. .TP +.B \-p\fI pattern +Return the option specified with +.B \-o +for the given pattern name. +.TP .B \-z\fI zonename Return the option specified with .B \-o @@ -69,9 +80,7 @@ for zone 'zonename'. .P .RS If this option is not given, the server section of the config file -is used. This option is primarily used by -.B nsdc -to parse the config file from the shell. +is used. .RE .P .RS @@ -85,8 +94,7 @@ default configuration file .SH "SEE ALSO" .LP -nsd(8), nsdc(8), nsd.conf(5), nsd\-notify(8), nsd\-patch(8), -nsd-xfer(8), nsd\-zonec(8) +\fInsd\fR(8), \fInsd.conf\fR(5), \fInsd\-control\fR(8) .SH "AUTHORS" .LP .B NSD diff --git a/usr.sbin/nsd/nsd-checkconf.c b/usr.sbin/nsd/nsd-checkconf.c index c40560c87fb..d940aadc133 100644 --- a/usr.sbin/nsd/nsd-checkconf.c +++ b/usr.sbin/nsd/nsd-checkconf.c @@ -1,7 +1,7 @@ /* * checkconf - Read and repeat configuration file to output. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -21,29 +21,30 @@ extern char *optarg; extern int optind; -#define ZONE_GET_ACL(NAME, VAR) \ +#define ZONE_GET_ACL(NAME, VAR, PATTERN) \ if (strcasecmp(#NAME, (VAR)) == 0) { \ - quote_acl((zone->NAME)); \ + quote_acl(PATTERN->NAME); \ return; \ } -#define ZONE_GET_OUTGOING(NAME, VAR) \ +#define ZONE_GET_OUTGOING(NAME, VAR, PATTERN) \ if (strcasecmp(#NAME, (VAR)) == 0) { \ acl_options_t* acl; \ - for(acl=zone->NAME; acl; acl=acl->next) \ + for(acl=PATTERN->NAME; acl; acl=acl->next) \ quote(acl->ip_address_spec); \ return; \ } -#define ZONE_GET_STR(NAME, VAR) \ +#define ZONE_GET_STR(NAME, VAR, PATTERN) \ if (strcasecmp(#NAME, (VAR)) == 0) { \ - quote(zone->NAME); \ + quote(PATTERN->NAME); \ return; \ } -#define ZONE_GET_BIN(NAME, VAR) \ +#define ZONE_GET_BIN(NAME, VAR, PATTERN) \ if (strcasecmp(#NAME, (VAR)) == 0) { \ - printf("%s\n", zone->NAME?"yes":"no"); \ + printf("%s\n", (PATTERN->NAME)?"yes":"no"); \ + return; \ } #define ZONE_GET_RRL(NAME, VAR, PATTERN) \ @@ -70,9 +71,9 @@ extern int optind; return; \ } -#define SERV_GET_IP(NAME, VAR) \ +#define SERV_GET_IP(NAME, MEMBER, VAR) \ if (strcasecmp(#NAME, (VAR)) == 0) { \ - for(ip = opt->ip_addresses; ip; ip=ip->next) \ + for(ip = opt->MEMBER; ip; ip=ip->next) \ { \ quote(ip->address); \ } \ @@ -129,6 +130,7 @@ usage(void) fprintf(stderr, "-v Verbose, echo settings that take effect to std output.\n"); fprintf(stderr, "-h Print this help information.\n"); fprintf(stderr, "-o option Print value of the option specified to stdout.\n"); + fprintf(stderr, "-p pattern Print option value for the pattern given.\n"); fprintf(stderr, "-z zonename Print option value for the zone given.\n"); fprintf(stderr, "-a keyname Print algorithm name for the TSIG key.\n"); fprintf(stderr, "-s keyname Print base64 secret blob for the TSIG key.\n"); @@ -179,7 +181,7 @@ print_acl(const char* varname, acl_options_t* acl) printf("%s %s\n", acl->ip_address_spec, acl->nokey?"NOKEY":(acl->blocked?"BLOCKED": (acl->key_name?acl->key_name:"(null)"))); - if(1) { + if(verbosity>1) { printf("\t# %s", acl->is_ipv6?"ip6":"ip4"); if(acl->port == 0) printf(" noport"); else printf(" port=%d", acl->port); @@ -225,23 +227,21 @@ print_acl_ips(const char* varname, acl_options_t* acl) } void -config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, const char *z) +config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, + const char *z, const char* pat) { - zone_options_t* zone; ip_address_option_t* ip; if (k) { /* find key */ - key_options_t* key = opt->keys; - for( ; key ; key=key->next) { - if(strcmp(key->name, k) == 0) { - if (s) { - quote(key->secret); - } else { - quote(key->algorithm); - } - return; + key_options_t* key = key_options_find(opt, k); + if(key) { + if (s) { + quote(key->secret); + } else { + quote(key->algorithm); } + return; } printf("Could not find key %s\n", k); return; @@ -252,43 +252,69 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, const } if (z) { + zone_options_t* zone; const dname_type *dname = dname_parse(opt->region, z); if(!dname) { printf("Could not parse zone name %s\n", z); exit(1); } - /* look per zone */ - RBTREE_FOR(zone, zone_options_t*, opt->zone_options) - { - if (dname_compare(dname, zone->node.key) == 0) { - /* -z matches, return are in the defines */ - ZONE_GET_STR(name, o); - ZONE_GET_STR(zonefile, o); - ZONE_GET_ACL(request_xfr, o); - ZONE_GET_ACL(provide_xfr, o); - ZONE_GET_ACL(allow_notify, o); - ZONE_GET_ACL(notify, o); - ZONE_GET_BIN(notify_retry, o); - ZONE_GET_OUTGOING(outgoing_interface, o); - ZONE_GET_BIN(allow_axfr_fallback, o); + zone = zone_options_find(opt, dname); + if(!zone) { + printf("Zone does not exist: %s\n", z); + exit(1); + } + ZONE_GET_STR(name, o, zone); + if(strcasecmp("pattern", o)==0) { + quote(zone->pattern->pname); + return; + } + ZONE_GET_BIN(part_of_config, o, zone); + ZONE_GET_STR(zonefile, o, zone->pattern); + ZONE_GET_ACL(request_xfr, o, zone->pattern); + ZONE_GET_ACL(provide_xfr, o, zone->pattern); + ZONE_GET_ACL(allow_notify, o, zone->pattern); + ZONE_GET_ACL(notify, o, zone->pattern); + ZONE_GET_BIN(notify_retry, o, zone->pattern); + ZONE_GET_OUTGOING(outgoing_interface, o, zone->pattern); + ZONE_GET_BIN(allow_axfr_fallback, o, zone->pattern); #ifdef RATELIMIT - ZONE_GET_RRL(rrl_whitelist, o, zone); + ZONE_GET_RRL(rrl_whitelist, o, zone->pattern); #endif - printf("Zone option not handled: %s %s\n", z, o); - exit(1); - } + printf("Zone option not handled: %s %s\n", z, o); + exit(1); + } else if(pat) { + pattern_options_t* p = pattern_options_find(opt, pat); + if(!p) { + printf("Pattern does not exist: %s\n", pat); + exit(1); + } + if(strcasecmp("name", o)==0) { + quote(p->pname); + return; } - printf("Zone does not exist: %s\n", z); + ZONE_GET_STR(zonefile, o, p); + ZONE_GET_ACL(request_xfr, o, p); + ZONE_GET_ACL(provide_xfr, o, p); + ZONE_GET_ACL(allow_notify, o, p); + ZONE_GET_ACL(notify, o, p); + ZONE_GET_BIN(notify_retry, o, p); + ZONE_GET_OUTGOING(outgoing_interface, o, p); + ZONE_GET_BIN(allow_axfr_fallback, o, p); +#ifdef RATELIMIT + ZONE_GET_RRL(rrl_whitelist, o, p); +#endif + printf("Pattern option not handled: %s %s\n", pat, o); exit(1); } else { /* look in the server section */ - SERV_GET_IP(ip_address, o); + SERV_GET_IP(ip_address, ip_addresses, o); /* bin */ SERV_GET_BIN(ip_transparent, o); SERV_GET_BIN(debug_mode, o); - SERV_GET_BIN(ip4_only, o); - SERV_GET_BIN(ip6_only, o); + SERV_GET_BIN(do_ip4, o); + SERV_GET_BIN(do_ip6, o); SERV_GET_BIN(hide_version, o); + SERV_GET_BIN(zonefiles_check, o); /* str */ SERV_GET_STR(database, o); SERV_GET_STR(identity, o); @@ -298,12 +324,10 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, const SERV_GET_STR(chroot, o); SERV_GET_STR(username, o); SERV_GET_STR(zonesdir, o); - SERV_GET_STR(difffile, o); SERV_GET_STR(xfrdfile, o); + SERV_GET_STR(xfrdir, o); + SERV_GET_STR(zonelistfile, o); SERV_GET_STR(port, o); -#if defined(BIND8_STATS) && defined(USE_ZONE_STATS) - SERV_GET_STR(zonestatsfile, o); -#endif /* int */ SERV_GET_INT(server_count, o); SERV_GET_INT(tcp_count, o); @@ -322,30 +346,66 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, const SERV_GET_INT(rrl_ipv6_prefix_length, o); SERV_GET_INT(rrl_whitelist_ratelimit, o); #endif + /* remote control */ + SERV_GET_BIN(control_enable, o); + SERV_GET_IP(control_interface, control_interface, o); + SERV_GET_INT(control_port, o); + SERV_GET_STR(server_key_file, o); + SERV_GET_STR(server_cert_file, o); + SERV_GET_STR(control_key_file, o); + SERV_GET_STR(control_cert_file, o); if(strcasecmp(o, "zones") == 0) { + zone_options_t* zone; RBTREE_FOR(zone, zone_options_t*, opt->zone_options) quote(zone->name); return; } + if(strcasecmp(o, "patterns") == 0) { + pattern_options_t* p; + RBTREE_FOR(p, pattern_options_t*, opt->patterns) + quote(p->pname); + return; + } printf("Server option not handled: %s\n", o); exit(1); } } +/* print zone content items */ +static void print_zone_content_elems(pattern_options_t* pat) +{ + if(pat->zonefile) + print_string_var("zonefile:", pat->zonefile); +#ifdef RATELIMIT + zone_print_rrl_whitelist("\trrl-whitelist: ", pat->rrl_whitelist); +#endif + print_acl("allow-notify:", pat->allow_notify); + print_acl("request-xfr:", pat->request_xfr); + if(!pat->notify_retry_is_default) + printf("\tnotify-retry: %d\n", pat->notify_retry); + print_acl("notify:", pat->notify); + print_acl("provide-xfr:", pat->provide_xfr); + print_acl_ips("outgoing-interface:", pat->outgoing_interface); + if(!pat->allow_axfr_fallback_is_default) + printf("\tallow-axfr-fallback: %s\n", + pat->allow_axfr_fallback?"yes":"no"); +} + void config_test_print_server(nsd_options_t* opt) { ip_address_option_t* ip; key_options_t* key; zone_options_t* zone; + pattern_options_t* pat; printf("# Config settings.\n"); printf("server:\n"); printf("\tdebug-mode: %s\n", opt->debug_mode?"yes":"no"); printf("\tip-transparent: %s\n", opt->ip_transparent?"yes":"no"); - printf("\tip4-only: %s\n", opt->ip4_only?"yes":"no"); - printf("\tip6-only: %s\n", opt->ip6_only?"yes":"no"); + printf("\tdo-ip4: %s\n", opt->do_ip4?"yes":"no"); + printf("\tdo-ip6: %s\n", opt->do_ip6?"yes":"no"); printf("\thide-version: %s\n", opt->hide_version?"yes":"no"); print_string_var("database:", opt->database); print_string_var("identity:", opt->identity); @@ -360,16 +420,18 @@ config_test_print_server(nsd_options_t* opt) print_string_var("pidfile:", opt->pidfile); print_string_var("port:", opt->port); printf("\tstatistics: %d\n", opt->statistics); -#if defined(BIND8_STATS) && defined(USE_ZONE_STATS) - printf("\tzone-stats-file: %s\n", opt->zonestatsfile); -#endif print_string_var("chroot:", opt->chroot); print_string_var("username:", opt->username); print_string_var("zonesdir:", opt->zonesdir); - print_string_var("difffile:", opt->difffile); print_string_var("xfrdfile:", opt->xfrdfile); + print_string_var("zonelistfile:", opt->zonelistfile); + print_string_var("xfrdir:", opt->xfrdir); printf("\txfrd_reload_timeout: %d\n", opt->xfrd_reload_timeout); printf("\tverbosity: %d\n", opt->verbosity); + for(ip = opt->ip_addresses; ip; ip=ip->next) + { + print_string_var("ip-address:", ip->address); + } #ifdef RATELIMIT printf("\trrl-size: %d\n", (int)opt->rrl_size); printf("\trrl-ratelimit: %d\n", (int)opt->rrl_ratelimit); @@ -378,43 +440,68 @@ config_test_print_server(nsd_options_t* opt) printf("\trrl-ipv6-prefix-length: %d\n", (int)opt->rrl_ipv6_prefix_length); printf("\trrl-whitelist-ratelimit: %d\n", (int)opt->rrl_whitelist_ratelimit); #endif - - for(ip = opt->ip_addresses; ip; ip=ip->next) - { - print_string_var("ip-address:", ip->address); - } - for(key = opt->keys; key; key=key->next) + printf("\tzonefiles-check: %s\n", opt->zonefiles_check?"yes":"no"); + + printf("\nremote-control:\n"); + printf("\tcontrol-enable: %s\n", opt->control_enable?"yes":"no"); + for(ip = opt->control_interface; ip; ip=ip->next) + print_string_var("control-interface:", ip->address); + printf("\tcontrol-port: %d\n", opt->control_port); + print_string_var("server-key-file:", opt->server_key_file); + print_string_var("server-cert-file:", opt->server_cert_file); + print_string_var("control-key-file:", opt->control_key_file); + print_string_var("control-cert-file:", opt->control_cert_file); + + RBTREE_FOR(key, key_options_t*, opt->keys) { printf("\nkey:\n"); print_string_var("name:", key->name); print_string_var("algorithm:", key->algorithm); print_string_var("secret:", key->secret); } + RBTREE_FOR(pat, pattern_options_t*, opt->patterns) + { + if(pat->implicit) continue; + printf("\npattern:\n"); + print_string_var("name:", pat->pname); + print_zone_content_elems(pat); + } RBTREE_FOR(zone, zone_options_t*, opt->zone_options) { + if(!zone->part_of_config) + continue; printf("\nzone:\n"); print_string_var("name:", zone->name); - print_string_var("zonefile:", zone->zonefile); -#ifdef RATELIMIT - zone_print_rrl_whitelist("\trrl-whitelist: ", zone->rrl_whitelist); -#endif - print_acl("allow-notify:", zone->allow_notify); - print_acl("request-xfr:", zone->request_xfr); - printf("\tnotify-retry: %d\n", zone->notify_retry); - print_acl("notify:", zone->notify); - print_acl("provide-xfr:", zone->provide_xfr); - print_acl_ips("outgoing-interface:", zone->outgoing_interface); - printf("\tallow-axfr-fallback: %s\n", zone->allow_axfr_fallback?"yes":"no"); + print_zone_content_elems(zone->pattern); } } +static void +append_trailing_slash(const char** dirname, region_type* region) +{ + int l = strlen(*dirname); + if (l>0 && (*dirname)[l-1] != '/') { + char *dirname_slash = region_alloc(region, l+2); + memcpy(dirname_slash, *dirname, l+1); + strlcat(dirname_slash, "/", l+2); + *dirname = dirname_slash; + } +} + +static int +file_inside_chroot(const char* fname, const char* chr) +{ + /* true if filename starts with chroot or is not absolute */ + return ((fname && fname[0] && strncmp(fname, chr, strlen(chr)) == 0) || + (fname && fname[0] != '/')); +} + static int additional_checks(nsd_options_t* opt, const char* filename) { ip_address_option_t* ip = opt->ip_addresses; zone_options_t* zone; - key_options_t* key; int num = 0; int errors = 0; while(ip) { @@ -433,7 +520,7 @@ additional_checks(nsd_options_t* opt, const char* filename) fprintf(stderr, "%s: cannot parse zone name syntax for zone %s.\n", filename, zone->name); errors ++; } - if(zone->allow_notify && !zone->request_xfr) { + if(zone->pattern->allow_notify && !zone->pattern->request_xfr) { fprintf(stderr, "%s: zone %s has allow-notify but no request-xfr" " items. Where can it get a zone transfer when a notify " "is received?\n", filename, zone->name); @@ -441,29 +528,6 @@ additional_checks(nsd_options_t* opt, const char* filename) } } - for(key = opt->keys; key; key=key->next) - { - const dname_type* dname = dname_parse(opt->region, key->name); /* memory leak. */ - uint8_t data[4000]; - int size; - - if(!dname) { - fprintf(stderr, "%s: cannot parse tsig name syntax for key %s.\n", filename, key->name); - errors ++; - } - size = b64_pton(key->secret, data, sizeof(data)); - if(size == -1) { - fprintf(stderr, "%s: cannot base64 decode tsig secret: for key %s.\n", filename, key->name); - errors ++; - } - if(tsig_get_algorithm_by_name(key->algorithm) == NULL) - { - fprintf(stderr, "%s: bad tsig algorithm %s: for key \ -%s.\n", filename, key->algorithm, key->name); - errors ++; - } - } - #ifndef BIND8_STATS if(opt->statistics > 0) { @@ -471,16 +535,7 @@ additional_checks(nsd_options_t* opt, const char* filename) filename, opt->statistics); errors ++; } -# ifndef USE_ZONE_STATS - if(opt->zonestatsfile) - { - fprintf(stderr, "%s: 'zone-stats-file: %s' but per zone BIND 8 statistics feature not enabled.\n", - filename, opt->zonestatsfile); - errors ++; - } -# endif #endif - #ifndef HAVE_CHROOT if(opt->chroot != 0) { @@ -497,30 +552,46 @@ additional_checks(nsd_options_t* opt, const char* filename) /* not done here: parsing of ip-address. parsing of username. */ - if (opt->chroot) { - int l = strlen(opt->chroot); - - if (strncmp(opt->chroot, opt->pidfile, l) != 0) { + if (opt->chroot && opt->chroot[0]) { + /* append trailing slash for strncmp checking */ + append_trailing_slash(&opt->chroot, opt->region); + append_trailing_slash(&opt->xfrdir, opt->region); + append_trailing_slash(&opt->zonesdir, opt->region); + + /* zonesdir must be absolute and within chroot, + * all other pathnames may be relative to zonesdir */ + if (strncmp(opt->zonesdir, opt->chroot, strlen(opt->chroot)) != 0) { + fprintf(stderr, "%s: zonesdir %s is not relative to chroot %s.\n", + filename, opt->zonesdir, opt->chroot); + errors ++; + } + if (!file_inside_chroot(opt->pidfile, opt->chroot)) { fprintf(stderr, "%s: pidfile %s is not relative to chroot %s.\n", filename, opt->pidfile, opt->chroot); errors ++; } - if (strncmp(opt->chroot, opt->database, l) != 0) { - fprintf(stderr, "%s: databasefile %s is not relative to chroot %s.\n", + if (!file_inside_chroot(opt->database, opt->chroot)) { + fprintf(stderr, "%s: database %s is not relative to chroot %s.\n", filename, opt->database, opt->chroot); errors ++; } - if (strncmp(opt->chroot, opt->difffile, l) != 0) { - fprintf(stderr, "%s: difffile %s is not relative to chroot %s.\n", - filename, opt->difffile, opt->chroot); - errors ++; - } - if (strncmp(opt->chroot, opt->xfrdfile, l) != 0) { + if (!file_inside_chroot(opt->xfrdfile, opt->chroot)) { fprintf(stderr, "%s: xfrdfile %s is not relative to chroot %s.\n", filename, opt->xfrdfile, opt->chroot); errors ++; } - } + if (!file_inside_chroot(opt->zonelistfile, opt->chroot)) { + fprintf(stderr, "%s: zonelistfile %s is not relative to chroot %s.\n", + filename, opt->zonelistfile, opt->chroot); + errors ++; + } + if (!file_inside_chroot(opt->xfrdir, opt->chroot)) { + fprintf(stderr, "%s: xfrdir %s is not relative to chroot %s.\n", + filename, opt->xfrdir, opt->chroot); + errors ++; + } + } + if (atoi(opt->port) <= 0) { fprintf(stderr, "%s: port number '%s' is not a positive number.\n", filename, opt->port); @@ -529,7 +600,7 @@ additional_checks(nsd_options_t* opt, const char* filename) if(errors != 0) { fprintf(stderr, "%s: %d semantic errors in %d zones, %d keys.\n", filename, errors, (int)nsd_options_num_zones(opt), - (int)opt->numkeys); + (int)opt->keys->count); } return (errors == 0); @@ -544,20 +615,25 @@ main(int argc, char* argv[]) const char * conf_opt = NULL; /* what option do you want? Can be NULL -> print all */ const char * conf_zone = NULL; /* what zone are we talking about */ const char * conf_key = NULL; /* what key is needed */ + const char * conf_pat = NULL; /* what pattern is talked about */ const char* configfile; nsd_options_t *options; log_init("nsd-checkconf"); /* Parse the command line... */ - while ((c = getopt(argc, argv, "vo:a:s:z:")) != -1) { + while ((c = getopt(argc, argv, "vo:a:p:s:z:")) != -1) { switch (c) { case 'v': verbose = 1; + verbosity++; break; case 'o': conf_opt = optarg; break; + case 'p': + conf_pat = optarg; + break; case 'a': if (conf_key) { fprintf(stderr, "Error: cannot combine -a with -s or other -a.\n"); @@ -590,18 +666,21 @@ main(int argc, char* argv[]) /* read config file */ options = nsd_options_create(region_create(xalloc, free)); tsig_init(options->region); - if (!parse_options_file(options, configfile) || + if (!parse_options_file(options, configfile, NULL, NULL) || !additional_checks(options, configfile)) { exit(2); } if (conf_opt || conf_key) { - config_print_zone(options, conf_key, key_sec, underscore(conf_opt), conf_zone); + config_print_zone(options, conf_key, key_sec, + underscore(conf_opt), conf_zone, conf_pat); } else { if (verbose) { - printf("# Read file %s: %d zones, %d keys.\n", + printf("# Read file %s: %d patterns, %d fixed-zones, " + "%d keys.\n", configfile, + (int)options->patterns->count, (int)nsd_options_num_zones(options), - (int)options->numkeys); + (int)options->keys->count); config_test_print_server(options); } } diff --git a/usr.sbin/nsd/nsd.8.in b/usr.sbin/nsd/nsd.8.in index 00b36f070a8..ec6ad6365fa 100644 --- a/usr.sbin/nsd/nsd.8.in +++ b/usr.sbin/nsd/nsd.8.in @@ -1,10 +1,10 @@ -.TH "NSD" "8" "Jul 22, 2013" "NLnet Labs" "NSD 3.2.16" -.\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. +.TH "NSD" "8" "Oct 29, 2013" "NLnet Labs" "NSD 4.0.0" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .LP .B nsd -\- Name Server Daemon (NSD) version 3.2.16. +\- Name Server Daemon (NSD) version 4.0.0. .SH "SYNOPSIS" .LP .B nsd @@ -56,7 +56,7 @@ argument and put itself into background and answers queries on port .I port option. The .I database -must be generated beforehand with nsd\-zonec(8). By default, +is created if it does not exist. By default, .B NSD will bind to all local interfaces available. Use the .B \-a @@ -83,7 +83,7 @@ configfile. .P Normally .B NSD -should be started with the `nsdc(8) start` command invoked from a +should be started with the `nsd\-control(8) start` command invoked from a .I /etc/rc.d/nsd.sh script or similar at the operating system startup. .TP @@ -112,7 +112,7 @@ Read specified For format description see nsd.conf(5). .TP .B \-d -Turn on debugging mode, do not fork, stay in the foreground. +Do not fork, stay in the foreground. .TP .B \-f\fI database Use the specified @@ -160,7 +160,7 @@ only useful on machines with multiple CPUs and/or network adapters. The maximum .I number of concurrent TCP connection that can be handled by each server. The -default is 10. +default is 100. .TP .B \-P\fI pidfile Use the specified @@ -219,7 +219,8 @@ SIGTERM Stop answering queries, shutdown, and exit normally. .TP SIGHUP -Reload the database. +Reload. Scans zone files and if changed (mtime) reads +them in. Also reopens the logfile (assists logrotation). .TP SIGUSR1 Dump BIND8\-style statistics into the log. Ignored otherwise. @@ -246,19 +247,9 @@ facility, unless the option is specified. .SH "SEE ALSO" .LP -nsdc(8), nsd.conf(5), nsd\-checkconf(8), nsd\-notify(8), -nsd\-patch(8), nsd\-xfer(8), nsd\-zonec(8) +\fInsd.conf\fR(5), \fInsd\-checkconf\fR(8), \fInsd\-control\fR(8) .SH "AUTHORS" .LP .B NSD was written by NLnet Labs and RIPE NCC joint team. Please see CREDITS file in the distribution for further details. -.SH "BUGS" -.LP -.B NSD -will answer the queries erroneously if the -.I database -was not properly compiled with nsd\-zonec(8). Therefore problems with -misconfigured master zone files or nsd\-zonec(8) bugs may not be visible -until the queries are actually answered with -.BR NSD . diff --git a/usr.sbin/nsd/nsd.c b/usr.sbin/nsd/nsd.c index be730b7a0f6..740c069b210 100644 --- a/usr.sbin/nsd/nsd.c +++ b/usr.sbin/nsd/nsd.c @@ -1,7 +1,7 @@ /* * nsd.c -- nsd(8) * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -41,13 +41,14 @@ #include <unistd.h> #include "nsd.h" -#include "namedb.h" #include "options.h" #include "tsig.h" +#include "remote.h" /* The server handler... */ static struct nsd nsd; static char hostname[MAXHOSTNAMELEN]; +extern config_parser_state_t* cfg_parser; static void error(const char *format, ...) ATTR_FORMAT(printf, 1, 2); @@ -67,7 +68,7 @@ usage (void) " -a ip-address[@port] Listen to the specified incoming IP address (and port)\n" " May be specified multiple times).\n" " -c configfile Read specified configfile instead of %s.\n" - " -d Enable debug mode (do not fork as a daemon process).\n" + " -d do not fork as a daemon process.\n" #ifndef NDEBUG " -F facilities Specify the debug facilities.\n" #endif /* NDEBUG */ @@ -107,7 +108,7 @@ version(void) fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf(stderr, "Written by NLnet Labs.\n\n"); fprintf(stderr, - "Copyright (C) 2001-2011 NLnet Labs. This is free software.\n" + "Copyright (C) 2001-2006 NLnet Labs. This is free software.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS\n" "FOR A PARTICULAR PURPOSE.\n"); exit(0); @@ -127,18 +128,25 @@ error(const char *format, ...) exit(1); } +static void +append_trailing_slash(const char** dirname, region_type* region) +{ + int l = strlen(*dirname); + if (l>0 && (*dirname)[l-1] != '/') { + char *dirname_slash = region_alloc(region, l+2); + memcpy(dirname_slash, *dirname, l+1); + strlcat(dirname_slash, "/", l+2); + /* old dirname is leaked, this is only used for chroot, once */ + *dirname = dirname_slash; + } +} + static int file_inside_chroot(const char* fname, const char* chr) { -#ifdef NDEBUG - assert(chr); -#endif /* NDEBUG */ - /* filename and chroot the same? */ - if (fname && fname[0] && chr[0] && !strncmp(fname, chr, strlen(chr))) - return 2; /* strip chroot, file rotation ok */ - else if (fname && fname[0] != '/') - return 1; /* don't strip, file rotation ok */ - return 0; /* don't strip, don't try file rotation */ + /* true if filename starts with chroot or is not absolute */ + return ((fname && fname[0] && strncmp(fname, chr, strlen(chr)) == 0) || + (fname && fname[0] != '/')); } void @@ -291,7 +299,7 @@ sig_handler(int sig) nsd.signal_hint_child = 1; return; case SIGHUP: - nsd.signal_hint_reload = 1; + nsd.signal_hint_reload_hup = 1; return; case SIGALRM: nsd.signal_hint_stats = 1; @@ -308,9 +316,6 @@ sig_handler(int sig) nsd.signal_hint_statsusr = 1; break; case SIGINT: - /* Silent shutdown... */ - nsd.signal_hint_quit = 1; - break; case SIGTERM: default: nsd.signal_hint_shutdown = 1; @@ -323,67 +328,9 @@ sig_handler(int sig) * */ #ifdef BIND8_STATS - -#ifdef USE_ZONE_STATS -static void -fprintf_zone_stats(FILE* fd, zone_type* zone, time_t now) -{ - int i; - - /* NSTATS */ - fprintf(fd, "NSTATS %s %lu", - dname_to_string(domain_dname(zone->apex),0), - (unsigned long) now); - - for (i = 0; i <= 255; i++) { - if (zone->st.qtype[i] != 0) { - fprintf(fd, " %s=%lu", rrtype_to_string(i), - zone->st.qtype[i]); - } - } - fprintf(fd, "\n"); - - /* XSTATS */ - fprintf(fd, "XSTATS %s %lu" - " RR=%lu RNXD=%lu RFwdR=%lu RDupR=%lu RFail=%lu RFErr=%lu RErr=%lu RAXFR=%lu" - " RLame=%lu ROpts=%lu SSysQ=%lu SAns=%lu SFwdQ=%lu SDupQ=%lu SErr=%lu RQ=%lu" - " RIQ=%lu RFwdQ=%lu RDupQ=%lu RTCP=%lu SFwdR=%lu SFail=%lu SFErr=%lu SNaAns=%lu" - " SNXD=%lu RUQ=%lu RURQ=%lu RUXFR=%lu RUUpd=%lu\n", - dname_to_string(domain_dname(zone->apex),0), - (unsigned long) now, - zone->st.dropped, - (unsigned long)0, (unsigned long)0, - (unsigned long)0, (unsigned long)0, - (unsigned long)0, (unsigned long)0, - zone->st.raxfr, - (unsigned long)0, (unsigned long)0, - (unsigned long)0, - zone->st.qudp + zone->st.qudp6 - zone->st.dropped, - (unsigned long)0, (unsigned long)0, - zone->st.txerr, - zone->st.opcode[OPCODE_QUERY], - zone->st.opcode[OPCODE_IQUERY], - zone->st.wrongzone, - (unsigned long)0, - zone->st.ctcp + zone->st.ctcp6, - (unsigned long)0, - zone->st.rcode[RCODE_SERVFAIL], - zone->st.rcode[RCODE_FORMAT], - zone->st.nona, - zone->st.rcode[RCODE_NXDOMAIN], - (unsigned long)0, (unsigned long)0, - (unsigned long)0, - zone->st.opcode[OPCODE_UPDATE]); -} -#endif - void bind8_stats (struct nsd *nsd) { -#ifdef USE_ZONE_STATS - FILE* fd; - zone_type* zone; -#endif char buf[MAXSYSLOGMSGLEN]; char *msg, *t; int i, len; @@ -438,23 +385,6 @@ bind8_stats (struct nsd *nsd) (unsigned long)0, (unsigned long)0, (unsigned long)0, nsd->st.opcode[OPCODE_UPDATE]); } -#ifdef USE_ZONE_STATS - /* ZSTATS */ - log_msg(LOG_INFO, "ZSTATS %s", nsd->zonestatsfile); - if ((fd = fopen(nsd->zonestatsfile, "a")) == NULL ) { - log_msg(LOG_ERR, "cannot open zone statsfile %s: %s", - nsd->zonestatsfile, strerror(errno)); - return; - } - /* Write stats per zone */ - zone = nsd->db->zones; - while (zone) { - fprintf_zone_stats(fd, zone, now); - zone = zone->next; - } - fclose(fd); -#endif - } #endif /* BIND8_STATS */ @@ -469,7 +399,6 @@ main(int argc, char *argv[]) pid_t oldpid; size_t i; struct sigaction action; - FILE* dbfd; #ifdef HAVE_GETPWNAM struct passwd *pwd = NULL; #endif /* HAVE_GETPWNAM */ @@ -493,9 +422,6 @@ main(int argc, char *argv[]) nsd.dbfile = 0; nsd.pidfile = 0; nsd.server_kind = NSD_SERVER_MAIN; -#ifdef USE_ZONE_STATS - nsd.zonestatsfile = 0; -#endif for (i = 0; i < MAX_INTERFACES; i++) { memset(&hints[i], 0, sizeof(hints[i])); @@ -665,19 +591,27 @@ main(int argc, char *argv[]) error("server identity too long (%u characters)", (unsigned) strlen(nsd.identity)); } + if(!tsig_init(nsd.region)) + error("init tsig failed"); /* Read options */ - nsd.options = nsd_options_create(region_create(xalloc, free)); - if(!parse_options_file(nsd.options, configfile)) { + nsd.options = nsd_options_create(region_create_custom(xalloc, free, + DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE, + DEFAULT_INITIAL_CLEANUP_SIZE, 1)); + if(!parse_options_file(nsd.options, configfile, NULL, NULL)) { error("could not read config: %s\n", configfile); } - if(nsd.options->ip4_only) { + if(!parse_zone_list_file(nsd.options)) { + error("could not read zonelist file %s\n", + nsd.options->zonelistfile); + } + if(nsd.options->do_ip4 && !nsd.options->do_ip6) { for (i = 0; i < MAX_INTERFACES; ++i) { hints[i].ai_family = AF_INET; } } #ifdef INET6 - if(nsd.options->ip6_only) { + if(nsd.options->do_ip6 && !nsd.options->do_ip4) { for (i = 0; i < MAX_INTERFACES; ++i) { hints[i].ai_family = AF_INET6; } @@ -752,11 +686,6 @@ main(int argc, char *argv[]) if(nsd.st.period == 0) { nsd.st.period = nsd.options->statistics; } -#ifdef USE_ZONE_STATS - if (nsd.zonestatsfile == 0) { - nsd.zonestatsfile = nsd.options->zonestatsfile; - } -#endif /* USE_ZONE_STATS */ #endif /* BIND8_STATS */ #ifdef HAVE_CHROOT if(nsd.chrootdir == 0) nsd.chrootdir = nsd.options->chroot; @@ -819,8 +748,6 @@ main(int argc, char *argv[]) nsd.children[i].need_to_send_QUIT = 0; nsd.children[i].need_to_exit = 0; nsd.children[i].has_exited = 0; - nsd.children[i].dirty_zones = stack_create(nsd.region, - nsd_options_num_zones(nsd.options)); } nsd.this_child = NULL; @@ -925,40 +852,37 @@ main(int argc, char *argv[]) /* endpwent(); */ #endif /* HAVE_GETPWNAM */ - if(!tsig_init(nsd.region)) - error("init tsig failed"); #if defined(HAVE_SSL) key_options_tsig_add(nsd.options); #endif - /* Relativize the pathnames for chroot... */ - if (nsd.chrootdir) { - int l = strlen(nsd.chrootdir); - + append_trailing_slash(&nsd.options->xfrdir, nsd.options->region); + /* Check relativity of pathnames to chroot */ + if (nsd.chrootdir && nsd.chrootdir[0]) { /* existing chrootdir: append trailing slash for strncmp checking */ - if (l>0 && strncmp(nsd.chrootdir + (l-1), "/", 1) != 0) { - char *chroot_slash = region_alloc(nsd.region, sizeof(char)*(l+2)); - memcpy(chroot_slash, nsd.chrootdir, sizeof(char)*(l+1)); - strlcat(chroot_slash, "/", sizeof(char)*(l+2)); - nsd.chrootdir = chroot_slash; - ++l; - } + append_trailing_slash(&nsd.chrootdir, nsd.region); + append_trailing_slash(&nsd.options->zonesdir, nsd.options->region); - if (strncmp(nsd.chrootdir, nsd.pidfile, l) != 0) { - error("%s is not relative to %s: chroot not possible", + /* zonesdir must be absolute and within chroot, + * all other pathnames may be relative to zonesdir */ + if (strncmp(nsd.options->zonesdir, nsd.chrootdir, strlen(nsd.chrootdir)) != 0) { + error("zonesdir %s is not relative to %s: chroot not possible", + nsd.options->zonesdir, nsd.chrootdir); + } else if (!file_inside_chroot(nsd.pidfile, nsd.chrootdir)) { + error("pidfile %s is not relative to %s: chroot not possible", nsd.pidfile, nsd.chrootdir); - } else if (strncmp(nsd.chrootdir, nsd.dbfile, l) != 0) { - error("%s is not relative to %s: chroot not possible", + } else if (!file_inside_chroot(nsd.dbfile, nsd.chrootdir)) { + error("database %s is not relative to %s: chroot not possible", nsd.dbfile, nsd.chrootdir); - } else if (strncmp(nsd.chrootdir, nsd.options->xfrdfile, l) != 0) { - error("%s is not relative to %s: chroot not possible", + } else if (!file_inside_chroot(nsd.options->xfrdfile, nsd.chrootdir)) { + error("xfrdfile %s is not relative to %s: chroot not possible", nsd.options->xfrdfile, nsd.chrootdir); - } else if (strncmp(nsd.chrootdir, nsd.options->difffile, l) != 0) { - error("%s is not relative to %s: chroot not possible", - nsd.options->difffile, nsd.chrootdir); - } else if (strncmp(nsd.chrootdir, nsd.options->zonesdir, l) != 0) { - error("%s is not relative to %s: chroot not possible", - nsd.options->zonesdir, nsd.chrootdir); + } else if (!file_inside_chroot(nsd.options->zonelistfile, nsd.chrootdir)) { + error("zonelistfile %s is not relative to %s: chroot not possible", + nsd.options->zonelistfile, nsd.chrootdir); + } else if (!file_inside_chroot(nsd.options->xfrdir, nsd.chrootdir)) { + error("xfrdir %s is not relative to %s: chroot not possible", + nsd.options->xfrdir, nsd.chrootdir); } } @@ -1008,6 +932,7 @@ main(int argc, char *argv[]) nsd.mode = NSD_RUN; nsd.signal_hint_child = 0; nsd.signal_hint_reload = 0; + nsd.signal_hint_reload_hup = 0; nsd.signal_hint_quit = 0; nsd.signal_hint_shutdown = 0; nsd.signal_hint_stats = 0; @@ -1016,10 +941,16 @@ main(int argc, char *argv[]) /* Initialize the server... */ if (server_init(&nsd) != 0) { - log_msg(LOG_ERR, "server initialization failed, %s could " + error("server initialization failed, %s could " "not be started", argv0); - exit(1); } +#if defined(HAVE_SSL) + if(nsd.options->control_enable) { + /* read ssl keys while superuser and outside chroot */ + if(!(nsd.rc = daemon_remote_create(nsd.options))) + error("could not perform remote control setup"); + } +#endif /* HAVE_SSL */ /* Unless we're debugging, fork... */ if (!nsd.debug) { @@ -1031,10 +962,7 @@ main(int argc, char *argv[]) /* Child */ break; case -1: - log_msg(LOG_ERR, "fork() failed: %s", strerror(errno)); - server_close_all_sockets(nsd.udp, nsd.ifs); - server_close_all_sockets(nsd.tcp, nsd.ifs); - exit(1); + error("fork() failed: %s", strerror(errno)); default: /* Parent is done */ server_close_all_sockets(nsd.udp, nsd.ifs); @@ -1044,8 +972,7 @@ main(int argc, char *argv[]) /* Detach ourselves... */ if (setsid() == -1) { - log_msg(LOG_ERR, "setsid() failed: %s", strerror(errno)); - exit(1); + error("setsid() failed: %s", strerror(errno)); } if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { @@ -1077,37 +1004,42 @@ main(int argc, char *argv[]) /* Chroot */ #ifdef HAVE_CHROOT - if (nsd.chrootdir && strlen(nsd.chrootdir)) { - int l = strlen(nsd.chrootdir); - int ret = 0; - - while (l>0 && nsd.chrootdir[l-1] == '/') - --l; + if (nsd.chrootdir && nsd.chrootdir[0]) { + int l = strlen(nsd.chrootdir)-1; /* ends in trailing slash */ - /* filename after chroot */ - ret = file_inside_chroot(nsd.log_filename, nsd.chrootdir); - if (ret) { + if (file_inside_chroot(nsd.log_filename, nsd.chrootdir)) nsd.file_rotation_ok = 1; - if (ret == 2) /* also strip chroot */ + + /* strip chroot from pathnames if they're absolute */ + nsd.options->zonesdir += l; + if (nsd.log_filename){ + if (nsd.log_filename[0] == '/') nsd.log_filename += l; } - nsd.dbfile += l; - nsd.pidfile += l; - nsd.options->xfrdfile += l; - nsd.options->difffile += l; - nsd.options->zonesdir += l; + if (nsd.pidfile[0] == '/') + nsd.pidfile += l; + if (nsd.dbfile[0] == '/') + nsd.dbfile += l; + if (nsd.options->xfrdfile[0] == '/') + nsd.options->xfrdfile += l; + if (nsd.options->zonelistfile[0] == '/') + nsd.options->zonelistfile += l; + if (nsd.options->xfrdir[0] == '/') + nsd.options->xfrdir += l; + + /* strip chroot from pathnames of "include:" statements + * on subsequent repattern commands */ + cfg_parser->chroot = nsd.chrootdir; #ifdef HAVE_TZSET /* set timezone whilst not yet in chroot */ tzset(); #endif if (chroot(nsd.chrootdir)) { - log_msg(LOG_ERR, "unable to chroot: %s", strerror(errno)); - exit(1); + error("unable to chroot: %s", strerror(errno)); } if (chdir("/")) { - log_msg(LOG_ERR, "unable to chdir to chroot: %s", strerror(errno)); - exit(1); + error("unable to chdir to chroot: %s", strerror(errno)); } DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed root directory to %s", nsd.chrootdir)); @@ -1128,13 +1060,6 @@ main(int argc, char *argv[]) DEBUG(DEBUG_IPC,1, (LOG_INFO, "file rotation on %s %sabled", nsd.log_filename, nsd.file_rotation_ok?"en":"dis")); - /* Check if nsd.db exists */ - if ((dbfd = fopen(nsd.dbfile, "r")) == NULL) { - log_msg(LOG_ERR, "unable to open %s for reading: %s", nsd.dbfile, strerror(errno)); - exit(1); - } - fclose(dbfd); - /* Write pidfile */ if (writepid(&nsd) == -1) { log_msg(LOG_ERR, "cannot overwrite the pidfile %s: %s", @@ -1176,11 +1101,19 @@ main(int argc, char *argv[]) } #endif /* HAVE_GETPWNAM */ + if(nsd.server_kind == NSD_SERVER_MAIN) { + server_prepare_xfrd(&nsd); + /* xfrd forks this before reading database, so it does not get + * the memory size of the database */ + server_start_xfrd(&nsd, 0, 0); + } if (server_prepare(&nsd) != 0) { - log_msg(LOG_ERR, "server preparation failed, %s could " - "not be started", argv0); unlinkpid(nsd.pidfile); - exit(1); + error("server preparation failed, %s could " + "not be started", argv0); + } + if(nsd.server_kind == NSD_SERVER_MAIN) { + server_send_soa_xfrd(&nsd, 0); } /* Really take off */ diff --git a/usr.sbin/nsd/nsd.conf.5.in b/usr.sbin/nsd/nsd.conf.5.in index 573d4aa9d8d..ab80e005296 100644 --- a/usr.sbin/nsd/nsd.conf.5.in +++ b/usr.sbin/nsd/nsd.conf.5.in @@ -1,5 +1,5 @@ -.TH "nsd.conf" "5" "Jul 22, 2013" "NLnet Labs" "nsd 3.2.16" -.\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. +.TH "nsd.conf" "5" "Oct 29, 2013" "NLnet Labs" "nsd 4.0.0" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .LP @@ -34,6 +34,9 @@ server: database: "@dbfile@" .RE .RS 5 +zonelistfile: "@zonelistfile@" +.RE +.RS 5 username: @user@ .RE .RS 5 @@ -43,9 +46,6 @@ logfile: "@logfile@" pidfile: "@pidfile@" .RE .RS 5 -difffile: "@difffile@" -.RE -.RS 5 xfrdfile: "@xfrdfile@" .RE .TP @@ -66,17 +66,21 @@ attributes, or a value. .P At the top level only .B server: -or -.B zone: -or +and .B key: +and +.B pattern: +and +.B zone: are allowed. These are followed by their attributes or the start of a new .B server: -or -.B zone: or .B key: +or +.B pattern: +or +.B zone: clause. The .B zone: attribute is followed by zone options. The @@ -85,13 +89,18 @@ attribute is followed by global options for the .B NSD server. A .B key: -attribute is used to define keys for authentication. +attribute is used to define keys for authentication. The +.B pattern: +attribute is followed by the zone options for zones that use the pattern. .P Files can be included using the .B include: -directive. It can appear anywhere, and takes a single filename as -an argument. Processing continues as if the text from the included -file was copied into the config file at that point. +directive. It can appear anywhere, and takes a single filename as an +argument. Processing continues as if the text from the included file +was copied into the config file at that point. If a chroot is used +an absolute filename is needed (with the chroot prepended), so that +the include can be parsed before and after application of the chroot (and +the knowledge of what that chroot is). .SS "Server Options" .LP The global options (if not overridden from the NSD commandline) are @@ -115,15 +124,11 @@ Turns on debugging mode for nsd, does not fork a daemon process. Default is no. Same as commandline option .BR \-d. .TP -.B ip4\-only:\fR <yes or no> -If yes, NSD only listens to IPv4 connections. Same as commandline -option -.BR \-4. +.B do\-ip4:\fR <yes or no> +If yes, NSD listens to IPv4 connections. Default yes. .TP -.B ip6\-only:\fR <yes or no> -If yes, NSD only listens to IPv6 connections. Same as commandline -option -.BR \-6. +.B do\-ip6:\fR <yes or no> +If yes, NSD listens to IPv6 connections. Default yes. .TP .B database:\fR <filename> By default @@ -132,6 +137,14 @@ is used. The specified file is used to store the compiled zone information. Same as commandline option .BR \-f. .TP +.B zonelistfile:\fR <filename> +By default +.I @zonelistfile@ +is used. The specified file is used to store the dynamically added +list of zones. The list is written to by NSD to add and delete zones. +It is a text file with a zone\-name and pattern\-name on each line. +This file is used for the nsd\-control addzone and delzone commands. +.TP .B identity:\fR <string> Returns the specified identity when asked for CH TXT ID.SERVER. Default is the name as returned by gethostname(3). Same as @@ -155,8 +168,7 @@ option .TP .B tcp\-count:\fR <number> The maximum number of concurrent, active TCP connections by each server. -Default is 10. This option should have a value below 1000. -Same as commandline option +Default is 100. Same as commandline option .BR \-n . .TP .B tcp\-query\-count:\fR <number> @@ -188,11 +200,13 @@ If not present no statistics are dumped. Statistics are produced every number seconds. Same as commandline option .BR \-s . .TP -.B zone\-stats\-file:\fR <filename> -If per zone statistics is enabled, file to dump the statistics. -.TP .B chroot:\fR <directory> -NSD will chroot on startup to the specified directory. Same as +NSD will chroot on startup to the specified directory. Note that if +elsewhere in the configuration you specify an absolute pathname to a file +inside the chroot, you have to prepend the \fBchroot\fR path. That way, +you can switch the chroot option on and off without having to modify +anything else in the configuration. Set the value to "" (the empty string) +to disable the chroot. By default "\fI@chrootdir@\fR" is used. Same as commandline option .BR \-t . .TP @@ -202,18 +216,17 @@ username. Can be username, id or id.gid. Same as commandline option .BR \-u . .TP .B zonesdir:\fR <directory> -Change the working directory to the specified directory before -accessing zone files. Same as commandline option -.B \-d -for nsd\-zonec(8). Also nsd(8) will access files (pid file, database -file, log file) relative to this directory. Set the value to "" -(the empty string) to disable the change of working directory. +Change the working directory to the specified directory before accessing +zone files. Also, NSD will access \fBdatabase\fR, \fBzonelistfile\fR, +\fBlogfile\fR, \fBpidfile\fR, \fBxfrdfile\fR, \fBxfrdir\fR, +\fBserver-key-file\fR, \fBserver-cert-file\fR, \fBcontrol-key-file\fR and +\fBcontrol-cert-file\fR +relative to this directory. Set the value to "" (the empty string) +to disable the change of working directory. By default "\fI@zonesdir@\fR" +is used. .TP .B difffile:\fR <filename> -When NSD receives IXFR updates it will store them in this file. -This file contains the differences between the database file and the -latest zone version. Default is -.IR @difffile@ . +Ignored, for compatibility with NSD3 config files. .TP .B xfrdfile:\fR <filename> The soa timeout and zone transfer daemon in NSD will save its state @@ -223,12 +236,17 @@ gone. For more details see the section on zone expiry behavior of NSD. Default is .IR @xfrdfile@ . .TP +.B xfrdir:\fR <directory> +The zone transfers are stored here before they are processed. A directory +is created here that is removed when NSD exits. Default is +.IR @xfrdir@ . +.TP .B xfrd\-reload\-timeout:\fR <number> If this value is \-1, xfrd will not trigger a reload after a zone transfer. If positive xfrd will trigger a reload after a zone transfer, then it will wait for the number of seconds before it will trigger a new reload. Setting this value throttles the reloads to -once per the number of seconds. The default is 10 seconds. +once per the number of seconds. The default is 1 second. .TP .B verbosity:\fR <level> This value specifies the verbosity level for (non\-debug) logging. @@ -238,6 +256,12 @@ zone transfers. 2 lists soft warnings that are encountered. .B hide\-version:\fR <yes or no> Prevent NSD from replying with the version string on CHAOS class queries. +.TP +.B zonefiles\-check\fR <yes or no> +Make NSD check the mtime of zone files on start and sighup. If you +disable it it starts faster (less disk activity in case of a lot of zones). +The default is enabled. The nsd\-control reload command reloads zone files +regardless of this option. .\" rrlstart .TP .B rrl\-size:\fR <numbuckets> @@ -256,10 +280,10 @@ This option controls the number of packets discarded before we send back a SLIP (a response with "truncated" bit set to one). 0 disables the sending of SLIP packets, 1 means every query will get a SLIP response. .TP -.B rrl\-ipv4\-prefix:\fR <subnet> +.B rrl\-ipv4\-prefix\-length:\fR <subnet> IPv4 prefix length. Addresses are grouped by netblock. .TP -.B rrl\-ipv6\-prefix:\fR <subnet> +.B rrl\-ipv6\-prefix\-length:\fR <subnet> IPv6 prefix length. Addresses are grouped by netblock. .TP .B rrl\-whitelist\-ratelimit:\fR <qps> @@ -268,6 +292,83 @@ whitelisted. Default 2000 qps. With the rrl\-whitelist option you can set specific queries to receive this qps limit instead of the normal limit. With the value 0 the rate is unlimited. .\" rrlend +.SS "Remote Control" +The +.B remote\-control: +clause is used to set options for using the \fInsd\-control\fR(8) +tool to give commands to the running NSD server. It is disabled by +default, and listens for localhost by default. It uses TLS over TCP +where the server and client authenticate to each other with self\-signed +certificates. The self\-signed certificates can be generated with the +\fInsd\-control\-setup\fR tool. The key files are read by NSD before +the chroot and before dropping user permissions, so they can be outside +the chroot and readable by the superuser only. +.TP +.B control\-enable:\fR <yes or no> +Enable remote control, default is no. +.TP +.B control\-interface:\fR <ip4 or ip6> +NSD will bind to the listed addresses to service control requests +(on TCP). Can be given multiple times to bind multiple ip\-addresses. +Use 0.0.0.0 and ::0 to service the wildcard interface. If none are given +NSD listens to the localhost 127.0.0.1 and ::1 interfaces for control, +if control is enabled with control\-enable. +.TP +.B control\-port:\fR <number> +The port number for remote control service. 8952 by default. +.TP +.B server\-key\-file:\fR <filename> +Path to the server private key, by default +.IR @configdir@/nsd_server.key . +This file is generated by the \fInsd\-control\-setup\fR utility. +This file is used by the nsd server, but not by \fInsd\-control\fR. +.TP +.B server\-cert\-file:\fR <filename> +Path to the server self signed certificate, by default +.IR @configdir@/nsd_server.pem . +This file is generated by the \fInsd\-control\-setup\fR utility. +This file is used by the nsd server, and also by \fInsd\-control\fR. +.TP +.B control\-key\-file:\fR <filename> +Path to the control client private key, by default +.IR @configdir@/nsd_control.key . +This file is generated by the \fInsd\-control\-setup\fR utility. +This file is used by \fInsd\-control\fR. +.TP +.B control\-cert\-file:\fR <filename> +Path to the control client certificate, by default +.IR @configdir@/nsd_control.pem . +This certificate has to be signed with the server certificate. +This file is generated by the \fInsd\-control\-setup\fR utility. +This file is used by \fInsd\-control\fR. +.SS "Pattern Options" +The +.B pattern: +clause is used to denote a set of options to apply to some zones. +The same zone options as for a zone are allowed. +.TP +.B name:\fR <string> +The name of the pattern. This is a (case sensitive) string. The pattern +names that start with "_implicit_" are used internally for zones that +have no pattern (they are defined in nsd.conf directly). +.TP +.B include\-pattern:\fR <pattern\-name> +The options from the given pattern are included at this point in +this pattern. The referenced pattern must be defined above this one. +.TP +.B <zone option>:\fR <value> +The zone options such as +.BR zonefile , +.BR allow\-notify , +.BR request\-xfr , +.BR allow\-axfr\-fallback , +.BR notify , +.BR notify\-retry , +.BR provide\-xfr , +and +.B outgoing\-interface +can be given. They are applied to the patterns and zones that include +this pattern. .SS "Zone Options" .LP For every zone the options need to be specified in one @@ -275,6 +376,11 @@ For every zone the options need to be specified in one clause. The access control list elements can be given multiple times to add multiple servers. These elements need to be added explicitly. +.LP +For zones that are configured in the \fInsd.conf\fR config file their +settings are hardcoded (in an implicit pattern for themselves only) +and they cannot be deleted via delzone, but remove them from the config +file and repattern. .TP .B name:\fR <string> The name of the zone. This is the domain name of the apex of the @@ -283,14 +389,17 @@ zone. May end with a '.' (in FQDN notation). For example each zone. .TP .B zonefile:\fR <filename> -The file containing the zone information. This file is used by -nsd\-zonec(8). This attribute must be present in each zone. +The file containing the zone information. If this attribute is present +it is used to read and write the zone contents. If the attribute is +absent it prevents writing out of the zone. .TP .B allow\-notify:\fR <ip\-spec> <key\-name | NOKEY | BLOCKED> Access control list. The listed (primary) address is allowed to send notifies to this (secondary) server. Notifies from unlisted or specifically BLOCKED addresses are discarded. If NOKEY is given no TSIG signature is required. +BLOCKED supersedes other entries, other entries are scanned for a match +in the order of the statements. .P .RS The ip\-spec is either a plain IP address (IPv4 or IPv6), or can be @@ -342,6 +451,8 @@ Access control list. The listed address (a secondary) is allowed to request AXFR from this server. Zone data will be provided to the address. The specified key is used during AXFR. For unlisted or BLOCKED addresses no data is provided, requests are discarded. +BLOCKED supersedes other entries, other entries are scanned for a match +in the order of the statements. .P .RS The ip\-spec is either a plain IP address (IPv4 or IPv6), or can be @@ -361,7 +472,11 @@ The ip\-address is a plain IP address (IPv4 or IPv6). A port number can be added using a suffix of @number, for example 1.2.3.4@5300. .RE -\" rrlstart +.TP +.B include\-pattern:\fR <pattern\-name> +The options from the given pattern are included at this point. +The referenced pattern must be defined above this zone. +.\" rrlstart .TP .B rrl\-whitelist:\fR <rrltype> This option causes queries of this rrltype to be whitelisted, for this @@ -554,8 +669,7 @@ default configuration file .SH "SEE ALSO" .LP -nsd(8), nsdc(8), nsd\-checkconf(8), nsd\-notify(8), -nsd\-patch(8), nsd\-xfer(8), nsd\-zonec(8) +\fInsd\fR(8), \fInsd\-checkconf\fR(8), \fInsd\-control\fR(8) .SH "AUTHORS" .LP .B NSD diff --git a/usr.sbin/nsd/nsec3.c b/usr.sbin/nsd/nsec3.c index 0220845113f..f84a4cedb6e 100644 --- a/usr.sbin/nsd/nsec3.c +++ b/usr.sbin/nsd/nsec3.c @@ -1,31 +1,93 @@ /* * nsec3.c -- nsec3 handling. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * */ -#include <config.h> +#include "config.h" #ifdef NSEC3 #include <stdio.h> #include <stdlib.h> -#include <errno.h> #include "nsec3.h" #include "iterated_hash.h" #include "namedb.h" #include "nsd.h" #include "answer.h" +#include "udbzone.h" +#include "options.h" -#define NSEC3_SHA1_HASH 1 /* same type code as DS hash */ +#define NSEC3_RDATA_BITMAP 5 +/* compare nsec3 hashes in nsec3 tree */ +static int +cmp_hash_tree(const void* x, const void* y) +{ + const domain_type* a = (const domain_type*)x; + const domain_type* b = (const domain_type*)y; + return memcmp(a->nsec3->nsec3_hash, b->nsec3->nsec3_hash, + NSEC3_HASH_LEN); +} + +/* compare nsec3 hashes in nsec3 wc tree */ +static int +cmp_wchash_tree(const void* x, const void* y) +{ + const domain_type* a = (const domain_type*)x; + const domain_type* b = (const domain_type*)y; + return memcmp(a->nsec3->nsec3_wc_hash, b->nsec3->nsec3_wc_hash, + NSEC3_HASH_LEN); +} + +/* compare nsec3 hashes in nsec3 ds tree */ +static int +cmp_dshash_tree(const void* x, const void* y) +{ + const domain_type* a = (const domain_type*)x; + const domain_type* b = (const domain_type*)y; + return memcmp(a->nsec3->nsec3_ds_parent_hash, + b->nsec3->nsec3_ds_parent_hash, NSEC3_HASH_LEN); +} + +/* compare base32-encoded nsec3 hashes in nsec3 rr tree, they are + * stored in the domain name of the node */ +static int +cmp_nsec3_tree(const void* x, const void* y) +{ + const domain_type* a = (const domain_type*)x; + const domain_type* b = (const domain_type*)y; + /* labelcount + 32long label */ + assert(dname_name(a->dname)[0] == 32); + assert(dname_name(b->dname)[0] == 32); + return memcmp(dname_name(a->dname), dname_name(b->dname), 33); +} + +void nsec3_zone_trees_create(struct region* region, zone_type* zone) +{ + if(!zone->nsec3tree) + zone->nsec3tree = rbtree_create(region, cmp_nsec3_tree); + if(!zone->hashtree) + zone->hashtree = rbtree_create(region, cmp_hash_tree); + if(!zone->wchashtree) + zone->wchashtree = rbtree_create(region, cmp_wchash_tree); + if(!zone->dshashtree) + zone->dshashtree = rbtree_create(region, cmp_dshash_tree); +} + +void nsec3_hash_tree_clear(struct zone* zone) +{ + hash_tree_clear(zone->nsec3tree); + hash_tree_clear(zone->hashtree); + hash_tree_clear(zone->wchashtree); + hash_tree_clear(zone->dshashtree); +} static void detect_nsec3_params(rr_type* nsec3_apex, const unsigned char** salt, int* salt_len, int* iter) { - /* always uses first NSEC3 record with SOA bit set */ assert(salt && salt_len && iter); assert(nsec3_apex); *salt_len = rdata_atom_data(nsec3_apex->rdatas[3])[0]; @@ -33,676 +95,678 @@ detect_nsec3_params(rr_type* nsec3_apex, *iter = read_uint16(rdata_atom_data(nsec3_apex->rdatas[2])); } -static const dname_type * -nsec3_hash_dname_param(region_type *region, zone_type *zone, - const dname_type *dname, rr_type* param_rr) +const dname_type * +nsec3_b32_create(region_type* region, zone_type* zone, unsigned char* hash) { - unsigned char hash[SHA_DIGEST_LENGTH]; + const dname_type* dname; char b32[SHA_DIGEST_LENGTH*2+1]; + b32_ntop(hash, SHA_DIGEST_LENGTH, b32, sizeof(b32)); + dname=dname_parse(region, b32); + dname=dname_concatenate(region, dname, domain_dname(zone->apex)); + return dname; +} + +void +nsec3_hash_and_store(zone_type* zone, const dname_type* dname, uint8_t* store) +{ const unsigned char* nsec3_salt = NULL; int nsec3_saltlength = 0; int nsec3_iterations = 0; - detect_nsec3_params(param_rr, &nsec3_salt, + detect_nsec3_params(zone->nsec3_param, &nsec3_salt, &nsec3_saltlength, &nsec3_iterations); - iterated_hash(hash, nsec3_salt, nsec3_saltlength, dname_name(dname), - dname->name_size, nsec3_iterations); - b32_ntop(hash, sizeof(hash), b32, sizeof(b32)); - dname=dname_parse(region, b32); - dname=dname_concatenate(region, dname, domain_dname(zone->apex)); - return dname; + iterated_hash((unsigned char*)store, nsec3_salt, nsec3_saltlength, + dname_name(dname), dname->name_size, nsec3_iterations); } -const dname_type * -nsec3_hash_dname(region_type *region, zone_type *zone, - const dname_type *dname) +#define STORE_HASH(x,y) memmove(domain->nsec3->x,y,NSEC3_HASH_LEN); domain->nsec3->have_##x =1; + +/** find hash or create it and store it */ +static void +nsec3_lookup_hash_and_wc(zone_type* zone, const dname_type* dname, + domain_type* domain, region_type* tmpregion) { - return nsec3_hash_dname_param(region, zone, - dname, zone->nsec3_soa_rr); + const dname_type* wcard; + if(domain->nsec3->have_nsec3_hash && domain->nsec3->have_nsec3_wc_hash) { + return; + } + /* lookup failed; disk failure or so */ + nsec3_hash_and_store(zone, dname, domain->nsec3->nsec3_hash); + domain->nsec3->have_nsec3_hash = 1; + wcard = dname_parse(tmpregion, "*"); + wcard = dname_concatenate(tmpregion, wcard, dname); + nsec3_hash_and_store(zone, wcard, domain->nsec3->nsec3_wc_hash); + domain->nsec3->have_nsec3_wc_hash = 1; +} + +static void +nsec3_lookup_hash_ds(zone_type* zone, const dname_type* dname, + domain_type* domain) +{ + if(domain->nsec3->have_nsec3_ds_parent_hash) { + return; + } + /* lookup failed; disk failure or so */ + nsec3_hash_and_store(zone, dname, domain->nsec3->nsec3_ds_parent_hash); + domain->nsec3->have_nsec3_ds_parent_hash = 1; } static int nsec3_has_soa(rr_type* rr) { - if(rdata_atom_size(rr->rdatas[5]) >= 3 && /* has types in bitmap */ - rdata_atom_data(rr->rdatas[5])[0] == 0 && /* first window = 0, */ - /* [1]: windowlen must be >= 1 */ - rdata_atom_data(rr->rdatas[5])[2]&0x02) /* SOA bit set */ + if(rdata_atom_size(rr->rdatas[NSEC3_RDATA_BITMAP]) >= 3 && /* has types in bitmap */ + rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[0] == 0 && /* first window = 0, */ + /* [1]: bitmap length must be >= 1 */ + /* [2]: bit[6] = SOA, thus mask first bitmap octet with 0x02 */ + rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[2]&0x02) { /* SOA bit set */ return 1; + } return 0; } static rr_type* -find_zone_nsec3(namedb_type* namedb, zone_type *zone) +check_apex_soa(namedb_type* namedb, zone_type *zone) { - size_t i; + uint8_t h[NSEC3_HASH_LEN]; domain_type* domain; + const dname_type* hashed_apex, *dname = domain_dname(zone->apex); + unsigned j; + rrset_type* nsec3_rrset; region_type* tmpregion; - /* Check settings in NSEC3PARAM. - Hash algorithm must be OK. And a NSEC3 with soa bit - must map to the zone apex. */ - rrset_type* paramset = domain_find_rrset(zone->apex, zone, TYPE_NSEC3PARAM); - if(!paramset || !paramset->rrs || !paramset->rr_count) - return NULL; + + nsec3_hash_and_store(zone, dname, h); tmpregion = region_create(xalloc, free); - for(i=0; i < paramset->rr_count; i++) { - rr_type* rr = ¶mset->rrs[i]; - const dname_type* hashed_apex; - rrset_type* nsec3_rrset; - size_t j; - const unsigned char *salt1; - int saltlen1, iter1; - - if(rdata_atom_data(rr->rdatas[0])[0] != NSEC3_SHA1_HASH) { - log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has unknown hash algo %d", - dname_to_string(domain_dname(zone->apex), NULL), (int)i, - rdata_atom_data(rr->rdatas[0])[0]); - continue; - } - if(rdata_atom_data(rr->rdatas[1])[0] != 0) { - /* draft-nsec3-09: NSEC3PARAM records with flags - field value other than zero MUST be ignored. */ - continue; - } - /* check hash of apex -> NSEC3 with soa bit on */ - hashed_apex = nsec3_hash_dname_param(tmpregion, - zone, domain_dname(zone->apex), ¶mset->rrs[i]); - domain = domain_table_find(namedb->domains, hashed_apex); - if(!domain) { - log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has no hash(apex).", - dname_to_string(domain_dname(zone->apex), NULL), (int)i); - log_msg(LOG_ERR, "hash(apex)= %s", - dname_to_string(hashed_apex, NULL)); - continue; - } - nsec3_rrset = domain_find_rrset(domain, zone, TYPE_NSEC3); - if(!nsec3_rrset) { - log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) has no NSEC3 RRset", - dname_to_string(domain_dname(zone->apex), NULL), (int)i); - continue; - } - detect_nsec3_params(rr, &salt1, &saltlen1, &iter1); - /* find SOA bit enabled nsec3, with the same settings */ - for(j=0; j < nsec3_rrset->rr_count; j++) { - const unsigned char *salt2; - int saltlen2, iter2; - if(!nsec3_has_soa(&nsec3_rrset->rrs[j])) - continue; - /* check params OK. Ignores the optout bit. */ - detect_nsec3_params(&nsec3_rrset->rrs[j], - &salt2, &saltlen2, &iter2); - if(saltlen1 == saltlen2 && iter1 == iter2 && - rdata_atom_data(rr->rdatas[0])[0] == /* algo */ - rdata_atom_data(nsec3_rrset->rrs[j].rdatas[0])[0] - && memcmp(salt1, salt2, saltlen1) == 0) { - /* found it */ - DEBUG(DEBUG_QUERY, 1, (LOG_INFO, - "detected NSEC3 for zone %s saltlen=%d iter=%d", - dname_to_string(domain_dname( - zone->apex),0), saltlen2, iter2)); - region_destroy(tmpregion); - return &nsec3_rrset->rrs[j]; - } + hashed_apex = nsec3_b32_create(tmpregion, zone, h); + domain = domain_table_find(namedb->domains, hashed_apex); + if(!domain) { + log_msg(LOG_ERR, "%s NSEC3PARAM entry has no hash(apex).", + domain_to_string(zone->apex)); + log_msg(LOG_ERR, "hash(apex)= %s", + dname_to_string(hashed_apex, NULL)); + region_destroy(tmpregion); + return NULL; + } + nsec3_rrset = domain_find_rrset(domain, zone, TYPE_NSEC3); + if(!nsec3_rrset) { + log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) has no NSEC3 RRset.", + domain_to_string(zone->apex)); + log_msg(LOG_ERR, "hash(apex)= %s", + dname_to_string(hashed_apex, NULL)); + region_destroy(tmpregion); + return NULL; + } + for(j=0; j<nsec3_rrset->rr_count; j++) { + if(nsec3_has_soa(&nsec3_rrset->rrs[j])) { + region_destroy(tmpregion); + return &nsec3_rrset->rrs[j]; } - log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) no NSEC3 with SOAbit", - dname_to_string(domain_dname(zone->apex), NULL), (int)i); } + log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) NSEC3 has no SOA flag.", + domain_to_string(zone->apex)); + log_msg(LOG_ERR, "hash(apex)= %s", + dname_to_string(hashed_apex, NULL)); region_destroy(tmpregion); return NULL; } -/* check that the rrset has an NSEC3 that uses the same parameters as the - zone is using. Pass NSEC3 rrset, and zone must have nsec3_rrset set. - if you pass NULL then 0 is returned. */ -static int -nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset) +static struct rr* +udb_zone_find_nsec3param(udb_base* udb, udb_ptr* uz, struct zone* z) { - rdata_atom_type* prd; - size_t i; - if(!rrset) - return 0; /* without rrset, no matching params either */ - assert(rrset && rrset->zone && (base || rrset->zone->nsec3_soa_rr)); - if(!base) - base = rrset->zone->nsec3_soa_rr; - prd = base->rdatas; - for(i=0; i < rrset->rr_count; ++i) { + udb_ptr urr; + unsigned i; + rrset_type* rrset = domain_find_rrset(z->apex, z, TYPE_NSEC3PARAM); + if(!rrset) /* no NSEC3PARAM in mem */ + return NULL; + udb_ptr_new(&urr, udb, &ZONE(uz)->nsec3param); + if(!urr.data || RR(&urr)->len < 5) { + /* no NSEC3PARAM in udb */ + udb_ptr_unlink(&urr, udb); + return NULL; + } + /* find matching NSEC3PARAM RR in memory */ + for(i=0; i<rrset->rr_count; i++) { + /* if this RR matches the udb RR then we are done */ rdata_atom_type* rd = rrset->rrs[i].rdatas; - assert(rrset->rrs[i].type == TYPE_NSEC3); - if(rdata_atom_data(rd[0])[0] == - rdata_atom_data(prd[0])[0] && /* hash algo */ - rdata_atom_data(rd[2])[0] == - rdata_atom_data(prd[2])[0] && /* iterations 0 */ - rdata_atom_data(rd[2])[1] == - rdata_atom_data(prd[2])[1] && /* iterations 1 */ - rdata_atom_data(rd[3])[0] == - rdata_atom_data(prd[3])[0] && /* salt length */ - memcmp(rdata_atom_data(rd[3])+1, - rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0]) - == 0 ) - { - /* this NSEC3 matches nsec3 parameters from zone */ - return 1; + if(RR(&urr)->wire[0] == rdata_atom_data(rd[0])[0] && /*alg*/ + RR(&urr)->wire[1] == rdata_atom_data(rd[1])[0] && /*flg*/ + RR(&urr)->wire[2] == rdata_atom_data(rd[2])[0] && /*iter*/ + RR(&urr)->wire[3] == rdata_atom_data(rd[2])[1] && + RR(&urr)->wire[4] == rdata_atom_data(rd[3])[0] && /*slen*/ + RR(&urr)->len >= 5 + RR(&urr)->wire[4] && + memcmp(RR(&urr)->wire+5, rdata_atom_data(rd[3])+1, + rdata_atom_data(rd[3])[0]) == 0) { + udb_ptr_unlink(&urr, udb); + return &rrset->rrs[i]; } } - return 0; + udb_ptr_unlink(&urr, udb); + return NULL; +} + +void +nsec3_find_zone_param(struct namedb* db, struct zone* zone, udb_ptr* z) +{ + /* get nsec3param RR from udb */ + zone->nsec3_param = udb_zone_find_nsec3param(db->udb, z, zone); } -#ifdef FULL_PREHASH +/* check params ok for one RR */ +static int +nsec3_rdata_params_ok(rdata_atom_type* prd, rdata_atom_type* rd) +{ + return (rdata_atom_data(rd[0])[0] == + rdata_atom_data(prd[0])[0] && /* hash algo */ + rdata_atom_data(rd[2])[0] == + rdata_atom_data(prd[2])[0] && /* iterations 0 */ + rdata_atom_data(rd[2])[1] == + rdata_atom_data(prd[2])[1] && /* iterations 1 */ + rdata_atom_data(rd[3])[0] == + rdata_atom_data(prd[3])[0] && /* salt length */ + memcmp(rdata_atom_data(rd[3])+1, + rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0]) + == 0 ); +} int -nsec3_find_cover(namedb_type* db, zone_type* zone, - const dname_type* hashname, domain_type** result) +nsec3_rr_uses_params(rr_type* rr, zone_type* zone) { - rrset_type *rrset; - domain_type *walk; - domain_type *closest_match; - domain_type *closest_encloser; - int exact; + if(!rr || rr->rdata_count < 4) + return 0; + return nsec3_rdata_params_ok(zone->nsec3_param->rdatas, rr->rdatas); +} - assert(result); - assert(zone->nsec3_soa_rr); - - exact = domain_table_search( - db->domains, hashname, &closest_match, &closest_encloser); - /* exact match of hashed domain name + it has an NSEC3? */ - if(exact && - nsec3_rrset_params_ok(NULL, - domain_find_rrset(closest_encloser, zone, TYPE_NSEC3))) { - *result = closest_encloser; - assert(*result != 0); - return 1; +int +nsec3_in_chain_count(domain_type* domain, zone_type* zone) +{ + rrset_type* rrset = domain_find_rrset(domain, zone, TYPE_NSEC3); + unsigned i; + int count = 0; + if(!rrset || !zone->nsec3_param) + return 0; /* no NSEC3s, none in the chain */ + for(i=0; i<rrset->rr_count; i++) { + if(nsec3_rr_uses_params(&rrset->rrs[i], zone)) + count++; } + return count; +} - /* find covering NSEC3 record, lexicographically before the closest match */ - /* use nsec3_lookup to jumpstart the search */ - walk = closest_match->nsec3_lookup; - rrset = 0; - while(walk && dname_is_subdomain(domain_dname(walk), domain_dname(zone->apex))) - { - if(nsec3_rrset_params_ok(NULL, - domain_find_rrset(walk, zone, TYPE_NSEC3))) { - /* this rrset is OK NSEC3, exit while */ - rrset = domain_find_rrset(walk, zone, TYPE_NSEC3); - break; +struct domain* +nsec3_chain_find_prev(struct zone* zone, struct domain* domain) +{ + if(domain->nsec3 && domain->nsec3->nsec3_node.key) { + /* see if there is a prev */ + rbnode_t* r = rbtree_previous(&domain->nsec3->nsec3_node); + if(r != RBTREE_NULL) { + /* found a previous, which is not the root-node in + * the prehash tree (and thus points to the tree) */ + return (domain_type*)r->key; } - walk = domain_previous(walk); } - if(rrset) - *result = walk; - else { - /* - * There are no NSEC3s before the closest match. - * so the hash name is before the first NSEC3 record in the zone. - * use last NSEC3, which covers the wraparound in hash space - * - * Since the zone has an NSEC3 with the SOA bit set for NSEC3 to turn on, - * there is also a last nsec3, so find_cover always assigns *result!=0. - */ - *result = zone->nsec3_last; + if(zone->nsec3_last) + return zone->nsec3_last; + return NULL; +} + +void +nsec3_clear_precompile(struct namedb* db, zone_type* zone) +{ + domain_type* walk; + /* clear prehash items (there must not be items for other zones) */ + prehash_clear(db->domains); + /* clear trees */ + hash_tree_clear(zone->nsec3tree); + hash_tree_clear(zone->hashtree); + hash_tree_clear(zone->wchashtree); + hash_tree_clear(zone->dshashtree); + /* wipe hashes */ + + /* wipe precompile */ + walk = zone->apex; + while(walk && domain_is_subdomain(walk, zone->apex)) { + if(walk->nsec3) { + if(nsec3_domain_part_of_zone(walk, zone)) { + walk->nsec3->nsec3_node.key = NULL; + walk->nsec3->nsec3_cover = NULL; + walk->nsec3->nsec3_wcard_child_cover = NULL; + walk->nsec3->nsec3_is_exact = 0; + walk->nsec3->have_nsec3_hash = 0; + walk->nsec3->have_nsec3_wc_hash = 0; + walk->nsec3->hash_node.key = NULL; + walk->nsec3->wchash_node.key = NULL; + } + if(!walk->parent || + nsec3_domain_part_of_zone(walk->parent, zone)) { + walk->nsec3->nsec3_ds_parent_cover = NULL; + walk->nsec3->nsec3_ds_parent_is_exact = 0; + walk->nsec3->have_nsec3_ds_parent_hash = 0; + walk->nsec3->dshash_node.key = NULL; + } + } + walk = domain_next(walk); + } + zone->nsec3_last = NULL; +} + +/* see if domain name is part of (existing names in) the nsec3 zone */ +int +nsec3_domain_part_of_zone(domain_type* d, zone_type* z) +{ + while(d) { + if(d->is_apex) + return (z->apex == d); /* zonecut, if right zone*/ + d = d->parent; } - assert(*result != 0); return 0; } -#else +/* condition when a domain is precompiled */ +int +nsec3_condition_hash(domain_type* d, zone_type* z) +{ + return d->is_existing && !domain_has_only_NSEC3(d, z) && + nsec3_domain_part_of_zone(d, z) && !domain_is_glue(d, z); +} + +/* condition when a domain is ds precompiled */ +int +nsec3_condition_dshash(domain_type* d, zone_type* z) +{ + return d->is_existing && !domain_has_only_NSEC3(d, z) && + (domain_find_rrset(d, z, TYPE_DS) || + domain_find_rrset(d, z, TYPE_NS)) && d != z->apex; +} + +zone_type* +nsec3_tree_zone(namedb_type* db, domain_type* d) +{ + /* see nsec3_domain_part_of_zone; domains part of zone that has + * apex above them */ + /* this does not use the rrset->zone pointer because there may be + * no rrsets left at apex (no SOA), e.g. during IXFR */ + while(d) { + if(d->is_apex) { + /* we can try a SOA if its present (faster than tree)*/ + /* DNSKEY and NSEC3PARAM are also good indicators */ + rrset_type *rrset; + for (rrset = d->rrsets; rrset; rrset = rrset->next) + if (rrset_rrtype(rrset) == TYPE_SOA || + rrset_rrtype(rrset) == TYPE_DNSKEY || + rrset_rrtype(rrset) == TYPE_NSEC3PARAM) + return rrset->zone; + return namedb_find_zone(db, d->dname); + } + d = d->parent; + } + return NULL; +} + +zone_type* +nsec3_tree_dszone(namedb_type* db, domain_type* d) +{ + /* the DStree does not contain nodes with d==z->apex */ + if(d->is_apex) + d = d->parent; + return nsec3_tree_zone(db, d); +} int -nsec3_find_cover(namedb_type* ATTR_UNUSED(db), zone_type* zone, - const dname_type* hashname, struct nsec3_domain **result) +nsec3_find_cover(zone_type* zone, uint8_t* hash, size_t hashlen, + domain_type** result) { - rbnode_t *node; + rbnode_t* r = NULL; int exact; + domain_type d; + uint8_t n[48]; + + /* nsec3tree is sorted by b32 encoded domain name of the NSEC3 */ + b32_ntop(hash, hashlen, (char*)(n+5), sizeof(n)-5); + d.dname = (dname_type*)n; + n[0] = 34; /* name_size */ + n[1] = 2; /* label_count */ + n[2] = 0; /* label_offset[0] */ + n[3] = 0; /* label_offset[1] */ + n[4] = 32; /* label-size[0] */ assert(result); - if (!zone->nsec3_domains) - return 0; - - exact = rbtree_find_less_equal(zone->nsec3_domains, hashname, &node); - if (!node) { - exact = 0; - node = rbtree_last(zone->nsec3_domains); - } + assert(zone->nsec3_param && zone->nsec3tree); - while (node != RBTREE_NULL) { - struct rrset *nsec3_rrset; - struct nsec3_domain *nsec3_domain = - (struct nsec3_domain *) node; - nsec3_rrset = domain_find_rrset(nsec3_domain->nsec3_domain, - zone, TYPE_NSEC3); - if (!nsec3_rrset) { - /* - * RRset in zone->nsec3_domains whose type != NSEC3 - * If we get here, something is seriously wrong! - */ - return 0; - } - if (nsec3_rrset_params_ok(NULL, nsec3_rrset) != 0) { - *result = nsec3_domain; - return exact; - } - exact = 0; /* No match, so we're looking for closest match */ - node = rbtree_previous(node); + exact = rbtree_find_less_equal(zone->nsec3tree, &d, &r); + if(r) { + *result = (domain_type*)r->key; + } else { + *result = zone->nsec3_last; } - /* - * If we reach this point, *result == NULL. This should - * never happen since the zone should have one NSEC3 record with - * the SOA bit set, which matches a NSEC3PARAM RR in the zone. - */ return exact; } -#endif -#ifdef FULL_PREHASH -static void -prehash_domain_r(namedb_type* db, zone_type* zone, - domain_type* domain, region_type* region) +void +nsec3_precompile_domain(struct namedb* db, struct domain* domain, + struct zone* zone, region_type* tmpregion) { - int exact; - const dname_type *wcard, *wcard_child, *hashname; domain_type* result = 0; - if(!zone->nsec3_soa_rr) - { - /* set to 0 (in case NSEC3 removed after an update) */ - domain->nsec3_is_exact = 0; - domain->nsec3_cover = NULL; - domain->nsec3_wcard_child_cover = NULL; - return; - } + int exact; + allocate_domain_nsec3(db->domains, domain); + + /* hash it */ + nsec3_lookup_hash_and_wc(zone, domain_dname(domain), domain, tmpregion); - hashname = nsec3_hash_dname(region, zone, domain_dname(domain)); - exact = nsec3_find_cover(db, zone, hashname, &result); - domain->nsec3_cover = result; + /* add into tree */ + zone_add_domain_in_hash_tree(db->region, &zone->hashtree, + cmp_hash_tree, domain, &domain->nsec3->hash_node); + zone_add_domain_in_hash_tree(db->region, &zone->wchashtree, + cmp_wchash_tree, domain, &domain->nsec3->wchash_node); + + /* lookup in tree cover ptr (or exact) */ + exact = nsec3_find_cover(zone, domain->nsec3->nsec3_hash, + sizeof(domain->nsec3->nsec3_hash), &result); + domain->nsec3->nsec3_cover = result; if(exact) - domain->nsec3_is_exact = 1; - else domain->nsec3_is_exact = 0; + domain->nsec3->nsec3_is_exact = 1; + else domain->nsec3->nsec3_is_exact = 0; /* find cover for *.domain for wildcard denial */ - wcard = dname_parse(region, "*"); - wcard_child = dname_concatenate(region, wcard, domain_dname(domain)); - hashname = nsec3_hash_dname(region, zone, wcard_child); - exact = nsec3_find_cover(db, zone, hashname, &result); - domain->nsec3_wcard_child_cover = result; - - if(exact && !domain_wildcard_child(domain)) - { - /* We found an exact match for the *.domain NSEC3 hash, - * but the domain wildcard child (*.domain) does not exist. - * Thus there is a hash collision. It will cause servfail - * for NXdomain queries below this domain. - */ - log_msg(LOG_WARNING, "prehash: collision of wildcard " - "denial for %s. Sign zone with different salt " - "to remove collision.", - dname_to_string(domain_dname(domain),0)); - } + exact = nsec3_find_cover(zone, domain->nsec3->nsec3_wc_hash, + sizeof(domain->nsec3->nsec3_wc_hash), &result); + domain->nsec3->nsec3_wcard_child_cover = result; } -#else - -static void -prehash_domain_r(namedb_type* db, zone_type* zone, - domain_type* domain, region_type* region) +void +nsec3_precompile_domain_ds(struct namedb* db, struct domain* domain, + struct zone* zone) { + domain_type* result = 0; int exact; - const dname_type *hashname; - struct nsec3_domain* result = NULL; - - domain->nsec3_cover = NULL; - hashname = nsec3_hash_dname(region, zone, domain_dname(domain)); - exact = nsec3_find_cover(db, zone, hashname, &result); - if (result && exact) - { - result->covers = domain; - domain->nsec3_cover = result->nsec3_domain; - } - return; -} -#endif - -static void -prehash_domain(namedb_type* db, zone_type* zone, - domain_type* domain, region_type* region) -{ - prehash_domain_r(db, zone, domain, region); + allocate_domain_nsec3(db->domains, domain); + + /* hash it : it could have different hash parameters then the + other hash for this domain name */ + nsec3_lookup_hash_ds(zone, domain_dname(domain), domain); + /* lookup in tree cover ptr (or exact) */ + exact = nsec3_find_cover(zone, domain->nsec3->nsec3_ds_parent_hash, + sizeof(domain->nsec3->nsec3_ds_parent_hash), &result); + if(exact) + domain->nsec3->nsec3_ds_parent_is_exact = 1; + else domain->nsec3->nsec3_ds_parent_is_exact = 0; + domain->nsec3->nsec3_ds_parent_cover = result; + /* add into tree */ + zone_add_domain_in_hash_tree(db->region, &zone->dshashtree, + cmp_dshash_tree, domain, &domain->nsec3->dshash_node); } -#ifdef FULL_PREHASH static void -prehash_ds(namedb_type* db, zone_type* zone, - domain_type* domain, region_type* region) +parse_nsec3_name(const dname_type* dname, uint8_t* hash, size_t buflen) { - domain_type* result = 0; - const dname_type* hashname; - int exact; - - if(!zone->nsec3_soa_rr) { - domain->nsec3_ds_parent_is_exact = 0; - domain->nsec3_ds_parent_cover = NULL; + /* first label must be the match, */ + size_t lablen = (buflen-1) * 8 / 5; + const uint8_t* wire = dname_name(dname); + assert(lablen == 32 && buflen == NSEC3_HASH_LEN+1); + /* labels of length 32 for SHA1, and must have space+1 for convert */ + if(wire[0] != lablen) { + /* not NSEC3 */ + memset(hash, 0, buflen); return; } - - /* hash again, other zone could have different hash parameters */ - hashname = nsec3_hash_dname(region, zone, domain_dname(domain)); - exact = nsec3_find_cover(db, zone, hashname, &result); - if(exact) - domain->nsec3_ds_parent_is_exact = 1; - else domain->nsec3_ds_parent_is_exact = 0; - domain->nsec3_ds_parent_cover = result; + (void)b32_pton((char*)wire+1, hash, buflen); } -#endif -#ifndef FULL_PREHASH -struct domain * -find_last_nsec3_domain(struct zone *zone) +void +nsec3_precompile_nsec3rr(namedb_type* db, struct domain* domain, + struct zone* zone) { - rbnode_t *node; - if (zone->nsec3_domains == NULL) { - return NULL; + allocate_domain_nsec3(db->domains, domain); + /* add into nsec3tree */ + zone_add_domain_in_hash_tree(db->region, &zone->nsec3tree, + cmp_nsec3_tree, domain, &domain->nsec3->nsec3_node); + /* fixup the last in the zone */ + if(rbtree_last(zone->nsec3tree)->key == domain) { + zone->nsec3_last = domain; } - node = rbtree_last(zone->nsec3_domains); - if (node == RBTREE_NULL) { - return NULL; - } - return ((struct nsec3_domain *) node)->nsec3_domain; } void -prehash_zone_incremental(struct namedb *db, struct zone *zone) +nsec3_precompile_newparam(namedb_type* db, zone_type* zone) { - region_type *temp_region; - rbnode_t *node; - /* find zone NSEC3PARAM settings */ - zone->nsec3_soa_rr = find_zone_nsec3(db, zone); - if (zone->nsec3_soa_rr == NULL) { - zone->nsec3_last = NULL; - return; - } - if (db->nsec3_mod_domains == NULL) { - return; - } - zone->nsec3_last = find_last_nsec3_domain(zone); - temp_region = region_create(xalloc, free); - node = rbtree_first(db->nsec3_mod_domains); - while (node != RBTREE_NULL) { - struct nsec3_mod_domain *nsec3_mod_domain = - (struct nsec3_mod_domain *) node; - struct domain *walk = nsec3_mod_domain->domain; - struct domain *domain_zone_apex; - - if (!walk || - (dname_is_subdomain(domain_dname(walk), - domain_dname(zone->apex)) == 0)) { - node = rbtree_next(node); - continue; + region_type* tmpregion = region_create(xalloc, free); + domain_type* walk; + time_t s = time(NULL); + unsigned n = 0, c = 0; + + /* add nsec3s of chain to nsec3tree */ + for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); + walk = domain_next(walk)) { + n++; + if(nsec3_in_chain_count(walk, zone) != 0) { + nsec3_precompile_nsec3rr(db, walk, zone); } - if (walk->nsec3_cover != NULL) { - node = rbtree_next(node); - continue; - } - /* Empty Terminal */ - if (!walk->is_existing) { - walk->nsec3_cover = NULL; - node = rbtree_next(node); - continue; - } - - /* - * Don't hash NSEC3 only nodes, unless possibly - * part of a weird case where node is empty nonterminal - * requiring NSEC3 but node name also is the hashed - * node name of another node requiring NSEC3. - * NSEC3 Empty Nonterminal with NSEC3 RRset present. - */ - if (domain_has_only_NSEC3(walk, zone) != 0) { - struct domain *next_domain = domain_next(walk); - if ((next_domain == NULL) || - (next_domain->parent != walk)) { - walk->nsec3_cover = NULL; - node = rbtree_next(node); - continue; - } + } + /* hash and precompile zone */ + for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); + walk = domain_next(walk)) { + if(nsec3_condition_hash(walk, zone)) { + nsec3_precompile_domain(db, walk, zone, tmpregion); + region_free_all(tmpregion); } - - /* - * Identify domain nodes that belong to the zone - * which are not glue records. What if you hit a - * record that's in two zones but which has no - * cut point between the zones. Not valid but - * someone is gonna try it sometime. - * This implementation doesn't link an NSEC3 - * record to the domain. - */ - domain_zone_apex = domain_find_zone_apex(walk); - if ((domain_zone_apex != NULL) && - (domain_zone_apex == zone->apex) && - (domain_is_glue(walk, zone) == 0)) { - - prehash_domain(db, zone, walk, temp_region); - region_free_all(temp_region); + if(nsec3_condition_dshash(walk, zone)) + nsec3_precompile_domain_ds(db, walk, zone); + if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > s + ZONEC_PCT_TIME) { + s = time(NULL); + VERBOSITY(1, (LOG_INFO, "nsec3 %s %d %%", + zone->opts->name, c*100/n)); } - - node = rbtree_next(node); } - namedb_nsec3_mod_domains_destroy(db); - region_destroy(temp_region); + region_destroy(tmpregion); } void -prehash_zone(struct namedb* db, struct zone* zone) +prehash_zone_complete(struct namedb* db, struct zone* zone) { - domain_type *walk; - domain_type *last_nsec3_node; - region_type *temp_region; - assert(db && zone); + udb_ptr udbz; + /* robust clear it */ + nsec3_clear_precompile(db, zone); /* find zone settings */ - zone->nsec3_soa_rr = find_zone_nsec3(db, zone); - if(!zone->nsec3_soa_rr) { + + assert(db && zone); + if(!udb_zone_search(db->udb, &udbz, dname_name(domain_dname( + zone->apex)), domain_dname(zone->apex)->name_size)) { + udb_ptr_init(&udbz, db->udb); /* zero the ptr */ + } + nsec3_find_zone_param(db, zone, &udbz); + if(!zone->nsec3_param || !check_apex_soa(db, zone)) { + zone->nsec3_param = NULL; zone->nsec3_last = NULL; + udb_ptr_unlink(&udbz, db->udb); return; } - temp_region = region_create(xalloc, free); + udb_ptr_unlink(&udbz, db->udb); + nsec3_precompile_newparam(db, zone); +} - /* go through entire zone and setup nsec3_lookup speedup */ - walk = zone->apex; - last_nsec3_node = NULL; - /* since we walk in sorted order, we pass all NSEC3s in sorted - order and we can set the lookup ptrs */ - while(walk && dname_is_subdomain( - domain_dname(walk), domain_dname(zone->apex))) - { - struct domain *domain_zone_apex; +static void +init_lookup_key_hash_tree(domain_type* d, uint8_t* hash) +{ memcpy(d->nsec3->nsec3_hash, hash, NSEC3_HASH_LEN); } - if (walk->nsec3_cover != NULL) { - walk = domain_next(walk); - continue; - } - /* Empty Terminal */ - if (walk->is_existing == 0) { - walk->nsec3_cover = NULL; - walk = domain_next(walk); - continue; - } +static void +init_lookup_key_wc_tree(domain_type* d, uint8_t* hash) +{ memcpy(d->nsec3->nsec3_wc_hash, hash, NSEC3_HASH_LEN); } - /* - * Don't hash NSEC3 only nodes, unless possibly - * part of a weird case where node is empty nonterminal - * requiring NSEC3 but node name also is the hashed - * node name of another node requiring NSEC3. - * NSEC3 Empty Nonterminal with NSEC3 RRset present. - */ - if (domain_has_only_NSEC3(walk, zone)) { - struct domain *next_domain = domain_next(walk); - if ((next_domain == NULL) || - (next_domain->parent != walk)) { - walk->nsec3_cover = NULL; - walk = domain_next(walk); - continue; - } - } +static void +init_lookup_key_ds_tree(domain_type* d, uint8_t* hash) +{ memcpy(d->nsec3->nsec3_ds_parent_hash, hash, NSEC3_HASH_LEN); } - /* - * Identify domain nodes that belong to the zone - * which are not glue records. What if you hit a - * record that's in two zones but which has no - * cut point between the zones. Not valid but - * someone is gonna try it sometime. - * This implementation doesn't link an NSEC3 - * record to the domain. - */ - domain_zone_apex = domain_find_zone_apex(walk); - if ((domain_zone_apex != NULL) && - (domain_zone_apex == zone->apex) && - (domain_is_glue(walk, zone) == 0)) - { - prehash_domain(db, zone, walk, temp_region); - region_free_all(temp_region); - } - walk = domain_next(walk); +/* find first in the tree and true if the first to process it */ +static int +process_first(rbtree_t* tree, uint8_t* hash, rbnode_t** p, + void (*init)(domain_type*, uint8_t*)) +{ + domain_type d; + struct nsec3_domain_data n; + if(!tree) { + *p = RBTREE_NULL; + return 0; + } + d.nsec3 = &n; + init(&d, hash); + if(rbtree_find_less_equal(tree, &d, p)) { + /* found an exact match */ + return 1; } - region_destroy(temp_region); + if(!*p) /* before first, go from first */ + *p = rbtree_first(tree); + /* the inexact, smaller, match we found, does not itself need to + * be edited */ + else + *p = rbtree_next(*p); /* if this becomes NULL, nothing to do */ + return 0; } -#else - +/* set end pointer if possible */ static void -prehash_zone(struct namedb* db, struct zone* zone) +process_end(rbtree_t* tree, uint8_t* hash, rbnode_t** p, + void (*init)(domain_type*, uint8_t*)) { - domain_type *walk; - domain_type *last_nsec3_node; - region_type *temp_region; - assert(db && zone); - - /* find zone settings */ - zone->nsec3_soa_rr = find_zone_nsec3(db, zone); - if(!zone->nsec3_soa_rr) { - zone->nsec3_last = NULL; + domain_type d; + struct nsec3_domain_data n; + if(!tree) { + *p = RBTREE_NULL; return; } - temp_region = region_create(xalloc, free); - - /* go through entire zone and setup nsec3_lookup speedup */ - walk = zone->apex; - last_nsec3_node = NULL; - /* since we walk in sorted order, we pass all NSEC3s in sorted - order and we can set the lookup ptrs */ - while(walk && dname_is_subdomain( - domain_dname(walk), domain_dname(zone->apex))) - { - zone_type* z = domain_find_zone(walk); - if(z && z==zone) - { - if(domain_find_rrset(walk, zone, TYPE_NSEC3)) - last_nsec3_node = walk; - walk->nsec3_lookup = last_nsec3_node; - } - walk = domain_next(walk); + d.nsec3 = &n; + init(&d, hash); + if(rbtree_find_less_equal(tree, &d, p)) { + /* an exact match, fine, because this one does not get + * processed */ + return; } - zone->nsec3_last = last_nsec3_node; - - /* go through entire zone */ - walk = zone->apex; - while(walk && dname_is_subdomain( - domain_dname(walk), domain_dname(zone->apex))) - { - zone_type* z; - if(!walk->is_existing && domain_has_only_NSEC3(walk, zone)) { - walk->nsec3_cover = NULL; - walk->nsec3_wcard_child_cover = NULL; - walk = domain_next(walk); - continue; - } - z = domain_find_zone(walk); - if(z && z==zone && !domain_is_glue(walk, zone)) - { - prehash_domain(db, zone, walk, temp_region); - region_free_all(temp_region); - } - /* prehash the DS (parent zone) */ - if(domain_find_rrset(walk, zone, TYPE_DS) || - (domain_find_rrset(walk, zone, TYPE_NS) && - walk != zone->apex)) - { - assert(walk != zone->apex /* DS must be above zone cut */); - prehash_ds(db, zone, walk, temp_region); - region_free_all(temp_region); - } - walk = domain_next(walk); + /* inexact element, but if NULL, until first element in tree */ + if(!*p) { + *p = rbtree_first(tree); + return; } - region_destroy(temp_region); + /* inexact match, use next element, if possible, the smaller + * element is part of the range */ + *p = rbtree_next(*p); + /* if next returns null, we go until the end of the tree */ } -#endif -void -prehash(struct namedb* db, int updated_only) +/* prehash domains in hash range start to end */ +static void +process_range(zone_type* zone, domain_type* start, + domain_type* end, domain_type* nsec3) { - zone_type *z; - time_t end, start = time(NULL); - int count = 0; - for(z = db->zones; z; z = z->next) - { - if(!updated_only || z->updated) { - prehash_zone(db, z); - if(z->nsec3_soa_rr) - count++; + /* start NULL means from first in tree */ + /* end NULL means to last in tree */ + rbnode_t *p = RBTREE_NULL, *pwc = RBTREE_NULL, *pds = RBTREE_NULL; + rbnode_t *p_end = RBTREE_NULL, *pwc_end = RBTREE_NULL, *pds_end = RBTREE_NULL; + /* because the nodes are on the prehashlist, the domain->nsec3 is + * already allocated, and we need not allocate it here */ + /* set start */ + if(start) { + uint8_t hash[NSEC3_HASH_LEN+1]; + parse_nsec3_name(domain_dname(start), hash, sizeof(hash)); + /* if exact match on first, set is_exact */ + if(process_first(zone->hashtree, hash, &p, init_lookup_key_hash_tree)) { + ((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3; + ((domain_type*)(p->key))->nsec3->nsec3_is_exact = 1; + p = rbtree_next(p); } + (void)process_first(zone->wchashtree, hash, &pwc, init_lookup_key_wc_tree); + if(process_first(zone->dshashtree, hash, &pds, init_lookup_key_ds_tree)){ + ((domain_type*)(pds->key))->nsec3-> + nsec3_ds_parent_cover = nsec3; + ((domain_type*)(pds->key))->nsec3-> + nsec3_ds_parent_is_exact = 1; + pds = rbtree_next(pds); + } + } else { + if(zone->hashtree) + p = rbtree_first(zone->hashtree); + if(zone->wchashtree) + pwc = rbtree_first(zone->wchashtree); + if(zone->dshashtree) + pds = rbtree_first(zone->dshashtree); + } + /* set end */ + if(end) { + uint8_t hash[NSEC3_HASH_LEN+1]; + parse_nsec3_name(domain_dname(end), hash, sizeof(hash)); + process_end(zone->hashtree, hash, &p_end, init_lookup_key_hash_tree); + process_end(zone->wchashtree, hash, &pwc_end, init_lookup_key_wc_tree); + process_end(zone->dshashtree, hash, &pds_end, init_lookup_key_ds_tree); } - end = time(NULL); - if(count > 0) - VERBOSITY(1, (LOG_INFO, "nsec3-prepare took %lld " - "seconds for %d zones.", (long long)(end-start), count)); -} + /* precompile */ + while(p != RBTREE_NULL && p != p_end) { + ((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3; + ((domain_type*)(p->key))->nsec3->nsec3_is_exact = 0; + p = rbtree_next(p); + } + while(pwc != RBTREE_NULL && pwc != pwc_end) { + ((domain_type*)(pwc->key))->nsec3-> + nsec3_wcard_child_cover = nsec3; + pwc = rbtree_next(pwc); + } + while(pds != RBTREE_NULL && pds != pds_end) { + ((domain_type*)(pds->key))->nsec3-> + nsec3_ds_parent_cover = nsec3; + ((domain_type*)(pds->key))->nsec3-> + nsec3_ds_parent_is_exact = 0; + pds = rbtree_next(pds); + } +} -#ifndef FULL_PREHASH +/* prehash a domain from the prehash list */ static void -nsec3_hash_and_find_cover(struct region *region, - struct namedb *db, const struct dname *domain_dname, - struct zone *zone, int *exact, struct domain **result) +process_prehash_domain(domain_type* domain, zone_type* zone) { - dname_type const *hash_dname; - struct nsec3_domain *nsec3_domain; - - *result = NULL; - *exact = 0; - hash_dname = nsec3_hash_dname(region, zone, domain_dname); - *exact = nsec3_find_cover(db, zone, hash_dname, &nsec3_domain); - if (nsec3_domain != NULL) { - *result = nsec3_domain->nsec3_domain; - } - return; + /* in the hashtree, wchashtree, dshashtree walk through to next NSEC3 + * and set precompile pointers to point to this domain (or is_exact), + * the first domain can be is_exact. If it is the last NSEC3, also + * process the initial part (before the first) */ + rbnode_t* nx; + + /* this domain is part of the prehash list and therefore the + * domain->nsec3 is allocated and need not be allocated here */ + assert(domain->nsec3 && domain->nsec3->nsec3_node.key); + nx = rbtree_next(&domain->nsec3->nsec3_node); + if(nx != RBTREE_NULL) { + /* process until next nsec3 */ + domain_type* end = (domain_type*)nx->key; + process_range(zone, domain, end, domain); + } else { + /* first is root, but then comes the first nsec3 */ + domain_type* first = (domain_type*)(rbtree_first( + zone->nsec3tree)->key); + /* last in zone */ + process_range(zone, domain, NULL, domain); + /* also process before first in zone */ + process_range(zone, NULL, first, domain); + } } -static void -nsec3_hash_and_find_wild_cover(struct region *region, - struct namedb *db, struct domain *domain, - struct zone *zone, int *exact, struct domain **result) +void prehash_zone(struct namedb* db, struct zone* zone) { - struct dname const *wcard_child; - /* find cover for *.domain for wildcard denial */ - (void) dname_make_wildcard(region, domain_dname(domain), - &wcard_child); - nsec3_hash_and_find_cover(region, db, wcard_child, zone, exact, - result); - if ((*exact != 0) && - (domain_wildcard_child(domain) == NULL)) { - /* We found an exact match for the *.domain NSEC3 hash, - * but the domain wildcard child (*.domain) does not exist. - * Thus there is a hash collision. It will cause servfail - * for NXdomain queries below this domain. - */ - log_msg(LOG_WARNING, - "collision of wildcard denial for %s. " - "Sign zone with different salt to remove collision.", - dname_to_string(domain_dname(domain), NULL)); + domain_type* d; + if(!zone->nsec3_param) { + prehash_clear(db->domains); + return; } -} -#endif + /* process prehash list */ + for(d = db->domains->prehash_list; d; d = d->nsec3->prehash_next) { + process_prehash_domain(d, zone); + } + /* clear prehash list */ + prehash_clear(db->domains); + if(!check_apex_soa(db, zone)) { + zone->nsec3_param = NULL; + zone->nsec3_last = NULL; + } +} /* add the NSEC3 rrset to the query answer at the given domain */ static void -nsec3_add_rrset(struct query *query, struct answer *answer, +nsec3_add_rrset(struct query* query, struct answer* answer, rr_section_type section, struct domain* domain) { if(domain) { @@ -714,30 +778,20 @@ nsec3_add_rrset(struct query *query, struct answer *answer, /* this routine does hashing at query-time. slow. */ static void -nsec3_add_nonexist_proof(struct query *query, struct answer *answer, - struct domain *encloser, struct namedb* db, const dname_type* qname) +nsec3_add_nonexist_proof(struct query* query, struct answer* answer, + struct domain* encloser, const dname_type* qname) { - const dname_type *to_prove; -#ifdef FULL_PREHASH - const dname_type *hashed; -#else - int exact = 0; -#endif - domain_type *cover = NULL; + uint8_t hash[NSEC3_HASH_LEN]; + const dname_type* to_prove; + domain_type* cover=0; assert(encloser); /* if query=a.b.c.d encloser=c.d. then proof needed for b.c.d. */ /* if query=a.b.c.d encloser=*.c.d. then proof needed for b.c.d. */ to_prove = dname_partial_copy(query->region, qname, dname_label_match_count(qname, domain_dname(encloser))+1); /* generate proof that one label below closest encloser does not exist */ -#ifdef FULL_PREHASH - hashed = nsec3_hash_dname(query->region, query->zone, to_prove); - if(nsec3_find_cover(db, query->zone, hashed, &cover)) -#else - nsec3_hash_and_find_cover(query->region, db, to_prove, query->zone, - &exact, &cover); - if (exact) -#endif + nsec3_hash_and_store(query->zone, to_prove, hash); + if(nsec3_find_cover(query->zone, hash, sizeof(hash), &cover)) { /* exact match, hash collision */ /* the hashed name of the query corresponds to an existing name. */ @@ -746,7 +800,8 @@ nsec3_add_nonexist_proof(struct query *query, struct answer *answer, RCODE_SET(query->packet, RCODE_SERVFAIL); return; } - else if (cover) { + else + { /* cover proves the qname does not exist */ nsec3_add_rrset(query, answer, AUTHORITY_SECTION, cover); } @@ -754,139 +809,89 @@ nsec3_add_nonexist_proof(struct query *query, struct answer *answer, static void nsec3_add_closest_encloser_proof( - struct query *query, struct answer *answer, - struct domain *closest_encloser, struct namedb* db, - const dname_type* qname) + struct query* query, struct answer* answer, + struct domain* closest_encloser, const dname_type* qname) { if(!closest_encloser) return; /* prove that below closest encloser nothing exists */ - nsec3_add_nonexist_proof(query, answer, closest_encloser, db, qname); + nsec3_add_nonexist_proof(query, answer, closest_encloser, qname); /* proof that closest encloser exists */ -#ifdef FULL_PREHASH - if(closest_encloser->nsec3_is_exact) -#else - if(closest_encloser->nsec3_cover) -#endif + if(closest_encloser->nsec3 && closest_encloser->nsec3->nsec3_is_exact) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - closest_encloser->nsec3_cover); + closest_encloser->nsec3->nsec3_cover); } - void nsec3_answer_wildcard(struct query *query, struct answer *answer, - struct domain *wildcard, struct namedb* db, const dname_type* qname) + struct domain *wildcard, const dname_type* qname) { if(!wildcard) return; - if(!query->zone->nsec3_soa_rr) + if(!query->zone->nsec3_param) return; - nsec3_add_nonexist_proof(query, answer, wildcard, db, qname); + nsec3_add_nonexist_proof(query, answer, wildcard, qname); } - static void nsec3_add_ds_proof(struct query *query, struct answer *answer, struct domain *domain, int delegpt) { -#ifndef FULL_PREHASH - struct domain * ds_parent_cover = NULL; - int exact = 0; -#endif /* assert we are above the zone cut */ assert(domain != query->zone->apex); - -#ifdef FULL_PREHASH - if(domain->nsec3_ds_parent_is_exact) { + if(domain->nsec3 && domain->nsec3->nsec3_ds_parent_is_exact) { /* use NSEC3 record from above the zone cut. */ nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - domain->nsec3_ds_parent_cover); - } else if (!delegpt && domain->nsec3_is_exact) { -#else - nsec3_hash_and_find_cover(query->region, NULL, domain_dname(domain), - query->zone, &exact, &ds_parent_cover); - if (exact) { - /* use NSEC3 record from above the zone cut. */ - if (ds_parent_cover) { - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - ds_parent_cover); - } - } else if (!delegpt && domain->nsec3_cover) { -#endif + domain->nsec3->nsec3_ds_parent_cover); + } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact) { nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - domain->nsec3_cover); + domain->nsec3->nsec3_cover); } else { /* prove closest provable encloser */ domain_type* par = domain->parent; - domain_type* prev_par = NULL; - assert(par); /* must be provable, thus there must be a parent */ + domain_type* prev_par = 0; -#ifdef FULL_PREHASH - while(!par->nsec3_is_exact) -#else - while(!par->nsec3_cover) -#endif + while(par && (!par->nsec3 || !par->nsec3->nsec3_is_exact)) { prev_par = par; par = par->parent; - assert(par); /* parent zone apex must be provable, thus this ends */ } + assert(par); /* parent zone apex must be provable, thus this ends */ + if(!par->nsec3) return; nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - par->nsec3_cover); + par->nsec3->nsec3_cover); /* we took several steps to go to the provable parent, so the one below it has no exact nsec3, disprove it. disprove is easy, it has a prehashed cover ptr. */ - if(prev_par) { -#ifdef FULL_PREHASH - assert(prev_par != domain && !prev_par->nsec3_is_exact); + if(prev_par && prev_par->nsec3) { + assert(prev_par != domain && + !prev_par->nsec3->nsec3_is_exact); nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - prev_par->nsec3_cover); -#else - struct domain *prev_parent_cover = NULL; - nsec3_hash_and_find_cover(query->region, NULL, - domain_dname(prev_par), query->zone, - &exact, &prev_parent_cover); - if (prev_parent_cover) { - nsec3_add_rrset(query, answer, - AUTHORITY_SECTION, prev_parent_cover); - } -#endif + prev_par->nsec3->nsec3_cover); } - - /* use NSEC3 record from above the zone cut. */ /* add optout range from parent zone */ /* note: no check of optout bit, resolver checks it */ -#ifdef FULL_PREHASH - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - domain->nsec3_ds_parent_cover); -#else - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - ds_parent_cover); -#endif + if(domain->nsec3) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + domain->nsec3->nsec3_ds_parent_cover); } } - void -nsec3_answer_nodata(struct query *query, struct answer *answer, - struct domain *original) +nsec3_answer_nodata(struct query* query, struct answer* answer, + struct domain* original) { - if(!query->zone->nsec3_soa_rr) + if(!query->zone->nsec3_param) return; - /* nodata when asking for secure delegation */ if(query->qtype == TYPE_DS) { if(original == query->zone->apex) { /* DS at zone apex, but server not authoritative for parent zone */ /* so answer at the child zone level */ -#ifdef FULL_PREHASH - if(original->nsec3_is_exact) -#else - if(original->nsec3_cover) -#endif + if(original->nsec3 && original->nsec3->nsec3_is_exact) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original->nsec3_cover); + original->nsec3->nsec3_cover); return; } /* query->zone must be the parent zone */ @@ -895,56 +900,32 @@ nsec3_answer_nodata(struct query *query, struct answer *answer, /* the nodata is result from a wildcard match */ else if (original==original->wildcard_child_closest_match && label_is_wildcard(dname_name(domain_dname(original)))) { -#ifndef FULL_PREHASH - struct domain* original_cover; - int exact; -#endif /* denial for wildcard is already there */ /* add parent proof to have a closest encloser proof for wildcard parent */ /* in other words: nsec3 matching closest encloser */ -#ifdef FULL_PREHASH - if(original->parent && original->parent->nsec3_is_exact) -#else - if(original->parent && original->parent->nsec3_cover) -#endif + if(original->parent && original->parent->nsec3 && + original->parent->nsec3->nsec3_is_exact) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original->parent->nsec3_cover); - + original->parent->nsec3->nsec3_cover); /* proof for wildcard itself */ /* in other words: nsec3 matching source of synthesis */ -#ifdef FULL_PREHASH - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original->nsec3_cover); -#else - original_cover = original->nsec3_cover; - if (!original_cover) { /* not exact */ - nsec3_hash_and_find_cover(query->region, NULL, - domain_dname(original), query->zone, - &exact, &original_cover); - } - if (original_cover) { - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original_cover); - } -#endif - + if(original->nsec3) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + original->nsec3->nsec3_cover); } - else { /* add nsec3 to prove rrset does not exist */ -#ifdef FULL_PREHASH - if(original->nsec3_is_exact) -#else - if (original->nsec3_cover != NULL) -#endif + else { /* add nsec3 to prove rrset does not exist */ + if(original->nsec3 && original->nsec3->nsec3_is_exact) { nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original->nsec3_cover); + original->nsec3->nsec3_cover); + } } } void nsec3_answer_delegation(struct query *query, struct answer *answer) { - if(!query->zone->nsec3_soa_rr) + if(!query->zone->nsec3_param) return; nsec3_add_ds_proof(query, answer, query->delegation_domain, 1); } @@ -972,14 +953,9 @@ domain_has_only_NSEC3(struct domain* domain, struct zone* zone) void nsec3_answer_authoritative(struct domain** match, struct query *query, struct answer *answer, struct domain* closest_encloser, - struct namedb* db, const dname_type* qname) + const dname_type* qname) { -#ifndef FULL_PREHASH - struct domain *cover_domain = NULL; - int exact = 0; -#endif - - if(!query->zone->nsec3_soa_rr) + if(!query->zone->nsec3_param) return; assert(match); /* there is a match, this has 1 RRset, which is NSEC3, but qtype is not. */ @@ -993,30 +969,17 @@ nsec3_answer_authoritative(struct domain** match, struct query *query, /* act as if the NSEC3 domain did not exist, name error */ *match = 0; /* all nsec3s are directly below the apex, that is closest encloser */ -#ifdef FULL_PREHASH - if(query->zone->apex->nsec3_is_exact) -#else - if(query->zone->apex->nsec3_cover) -#endif + if(query->zone->apex->nsec3 && + query->zone->apex->nsec3->nsec3_is_exact) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - query->zone->apex->nsec3_cover); - + query->zone->apex->nsec3->nsec3_cover); /* disprove the nsec3 record. */ - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - closest_encloser->nsec3_cover); + if(closest_encloser->nsec3) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3->nsec3_cover); /* disprove a wildcard */ -#ifdef FULL_PREHASH - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - query->zone->apex->nsec3_wcard_child_cover); -#else - cover_domain = NULL; - nsec3_hash_and_find_cover(query->region, db, - domain_dname(closest_encloser), - query->zone, &exact, &cover_domain); - if (cover_domain) - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - cover_domain); -#endif + if(query->zone->apex->nsec3) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + query->zone->apex->nsec3->nsec3_wcard_child_cover); if (domain_wildcard_child(query->zone->apex)) { /* wildcard exists below the domain */ /* wildcard and nsec3 domain clash. server failure. */ @@ -1036,19 +999,11 @@ nsec3_answer_authoritative(struct domain** match, struct query *query, } if(!*match) { /* name error, domain does not exist */ - nsec3_add_closest_encloser_proof(query, answer, - closest_encloser, db, qname); -#ifdef FULL_PREHASH - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - closest_encloser->nsec3_wcard_child_cover); -#else - cover_domain = NULL; - nsec3_hash_and_find_wild_cover(query->region, db, - closest_encloser, query->zone, &exact, &cover_domain); - if (cover_domain) + nsec3_add_closest_encloser_proof(query, answer, closest_encloser, + qname); + if(closest_encloser->nsec3) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - cover_domain); -#endif + closest_encloser->nsec3->nsec3_wcard_child_cover); } } diff --git a/usr.sbin/nsd/query.c b/usr.sbin/nsd/query.c index aea0a6e455d..09532677e2f 100644 --- a/usr.sbin/nsd/query.c +++ b/usr.sbin/nsd/query.c @@ -1,7 +1,7 @@ /* * query.c -- nsd(8) the resolver. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -54,14 +54,14 @@ static int add_rrset(struct query *query, static void answer_authoritative(struct nsd *nsd, struct query *q, answer_type *answer, - uint32_t domain_number, + size_t domain_number, int exact, domain_type *closest_match, domain_type *closest_encloser, const dname_type *qname); static void answer_lookup_zone(struct nsd *nsd, struct query *q, - answer_type *answer, uint32_t domain_number, + answer_type *answer, size_t domain_number, int exact, domain_type *closest_match, domain_type *closest_encloser, const dname_type *qname); @@ -113,7 +113,7 @@ query_add_compression_domain(struct query *q, domain_type *domain, uint16_t offs while (domain->parent) { DEBUG(DEBUG_NAME_COMPRESSION, 2, (LOG_INFO, "query dname: %s, number: %lu, offset: %u\n", - dname_to_string(domain_dname(domain), NULL), + domain_to_string(domain), (unsigned long) domain->number, offset)); query_put_dname_offset(q, domain, offset); @@ -165,7 +165,7 @@ query_cleanup(void *data) query_type * query_create(region_type *region, uint16_t *compressed_dname_offsets, - uint32_t compressed_dname_size) + size_t compressed_dname_size) { query_type *query = (query_type *) region_alloc_zero(region, sizeof(query_type)); @@ -212,7 +212,6 @@ query_reset(query_type *q, size_t maxlen, int is_tcp) q->qtype = 0; q->qclass = 0; q->zone = NULL; - q->domain = NULL; q->opcode = 0; q->cname_count = 0; q->delegation_domain = NULL; @@ -363,8 +362,11 @@ process_tsig(struct query* q) return NSD_RC_FORMAT; if(q->tsig.status == TSIG_OK) { if(!tsig_from_query(&q->tsig)) { - log_msg(LOG_ERR, "query: bad tsig (%s)", - tsig_error(q->tsig.error_code)); + char a[128]; + addr2str(&q->addr, a, sizeof(a)); + log_msg(LOG_ERR, "query: bad tsig (%s) for key %s from %s", + tsig_error(q->tsig.error_code), + dname_to_string(q->tsig.key_name, NULL), a); return NSD_RC_NOTAUTH; } buffer_set_limit(q->packet, q->tsig.position); @@ -372,8 +374,10 @@ process_tsig(struct query* q) tsig_prepare(&q->tsig); tsig_update(&q->tsig, q->packet, buffer_limit(q->packet)); if(!tsig_verify(&q->tsig)) { - log_msg(LOG_ERR, "query: bad tsig signature for key %s", - dname_to_string(q->tsig.key->name, NULL)); + char a[128]; + addr2str(&q->addr, a, sizeof(a)); + log_msg(LOG_ERR, "query: bad tsig signature for key %s from %s", + dname_to_string(q->tsig.key->name, NULL), a); return NSD_RC_NOTAUTH; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "query good tsig signature for %s", @@ -412,7 +416,7 @@ answer_notify(struct nsd* nsd, struct query *query) return query_error(query, rc); /* check if it passes acl */ - if((acl_num = acl_check_incoming(zone_opt->allow_notify, query, + if((acl_num = acl_check_incoming(zone_opt->pattern->allow_notify, query, &why)) != -1) { sig_atomic_t mode = NSD_PASS_TO_XFRD; @@ -421,13 +425,14 @@ answer_notify(struct nsd* nsd, struct query *query) uint32_t acl_send = htonl(acl_num); uint32_t acl_xfr; size_t pos; - assert(why); /* Find priority candidate for request XFR. -1 if no match */ acl_num_xfr = acl_check_incoming( - zone_opt->request_xfr, query, NULL); + zone_opt->pattern->request_xfr, query, NULL); + acl_xfr = htonl(acl_num_xfr); + assert(why); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "got notify %s passed acl %s %s", dname_to_string(query->qname, NULL), why->ip_address_spec, @@ -461,18 +466,19 @@ answer_notify(struct nsd* nsd, struct query *query) pos = buffer_position(query->packet); buffer_clear(query->packet); buffer_set_position(query->packet, pos); - VERBOSITY(2, (LOG_INFO, "Notify received and accepted, forward to xfrd")); + if(verbosity >= 1) { + char address[128]; + addr2str(&query->addr, address, sizeof(address)); + VERBOSITY(2, (LOG_INFO, "notify for %s from %s", + dname_to_string(query->qname, NULL), address)); + } /* tsig is added in add_additional later (if needed) */ return QUERY_PROCESSED; } - if (verbosity > 1) { + if (verbosity >= 1) { char address[128]; - if (addr2ip(query->addr, address, sizeof(address))) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "addr2ip failed")); - strlcpy(address, "[unknown]", sizeof(address)); - } - + addr2str(&query->addr, address, sizeof(address)); VERBOSITY(1, (LOG_INFO, "notify for zone %s from client %s refused, %s%s", dname_to_string(query->qname, NULL), address, @@ -548,7 +554,7 @@ find_covering_nsec(domain_type *closest_match, assert(nsec_rrset); /* loop away temporary created domains. For real ones it is &RBTREE_NULL */ - while (closest_match->node.parent == NULL) + while (closest_match->rnode == NULL) closest_match = closest_match->parent; while (closest_match) { *nsec_rrset = domain_find_rrset(closest_match, zone, TYPE_NSEC); @@ -618,7 +624,8 @@ add_additional_rrsets(struct query *query, answer_type *answer, domain_type *wildcard_child = domain_wildcard_child(match); domain_type *temp = (domain_type *) region_alloc( query->region, sizeof(domain_type)); - memcpy(&temp->node, &additional->node, sizeof(rbnode_t)); + temp->rnode = NULL; + temp->dname = additional->dname; temp->number = additional->number; temp->parent = match; temp->wildcard_child_closest_match = temp; @@ -693,7 +700,7 @@ add_rrset(struct query *query, from_name is changes to to_name by the DNAME rr. DNAME rr is from src to dest. closest encloser encloses the to_name. */ -static uint32_t +static size_t query_synthesize_cname(struct query* q, struct answer* answer, const dname_type* from_name, const dname_type* to_name, domain_type* src, domain_type* to_closest_encloser, domain_type** to_closest_match) @@ -717,15 +724,15 @@ query_synthesize_cname(struct query* q, struct answer* answer, const dname_type* return 0; newdom->is_existing = 1; newdom->parent = lastparent; - newdom->node.key = dname_partial_copy(q->region, + newdom->dname + = dname_partial_copy(q->region, from_name, domain_dname(src)->label_count + i + 1); if(dname_compare(domain_dname(newdom), q->qname) == 0) { /* 0 good for query name, otherwise new number */ newdom->number = 0; } DEBUG(DEBUG_QUERY,2, (LOG_INFO, "created temp domain src %d. %s nr %d", i, - dname_to_string(domain_dname(newdom), NULL), - newdom->number)); + domain_to_string(newdom), (int)newdom->number)); lastparent = newdom; } cname_domain = lastparent; @@ -740,11 +747,11 @@ query_synthesize_cname(struct query* q, struct answer* answer, const dname_type* return 0; newdom->is_existing = 0; newdom->parent = lastparent; - newdom->node.key = dname_partial_copy(q->region, + newdom->dname + = dname_partial_copy(q->region, to_name, domain_dname(to_closest_encloser)->label_count + i + 1); DEBUG(DEBUG_QUERY,2, (LOG_INFO, "created temp domain dest %d. %s nr %d", i, - dname_to_string(domain_dname(newdom), NULL), - newdom->number)); + domain_to_string(newdom), (int)newdom->number)); lastparent = newdom; } cname_dest = lastparent; @@ -803,7 +810,7 @@ answer_delegation(query_type *query, answer_type *answer) add_rrset(query, answer, AUTHORITY_SECTION, query->delegation_domain, rrset); #ifdef NSEC3 - } else if (query->zone->nsec3_soa_rr) { + } else if (query->zone->nsec3_param) { nsec3_answer_delegation(query, answer); #endif } else if ((rrset = domain_find_rrset(query->delegation_domain, query->zone, TYPE_NSEC))) { @@ -811,7 +818,6 @@ answer_delegation(query_type *query, answer_type *answer) query->delegation_domain, rrset); } } - query->domain = query->delegation_domain; } @@ -821,8 +827,6 @@ answer_delegation(query_type *query, answer_type *answer) static void answer_soa(struct query *query, answer_type *answer) { - query->domain = query->zone->apex; - if (query->qclass != CLASS_ANY) { add_rrset(query, answer, AUTHORITY_SECTION, @@ -849,7 +853,7 @@ answer_nodata(struct query *query, answer_type *answer, domain_type *original) } #ifdef NSEC3 - if (query->edns.dnssec_ok && query->zone->nsec3_soa_rr) { + if (query->edns.dnssec_ok && query->zone->nsec3_param) { nsec3_answer_nodata(query, answer, original); } else #endif @@ -939,16 +943,12 @@ answer_domain(struct nsd* nsd, struct query *q, answer_type *answer, domain_dname(closest_match)); q->zone = origzone; } - /* example 6.2.7 shows no NS-set from zone in auth (RFC1034) */ - q->domain = domain; return; } else { answer_nodata(q, answer, original); return; } - q->domain = domain; - if (q->qclass != CLASS_ANY && q->zone->ns_rrset && answer_needs_ns(q)) { add_rrset(q, answer, OPTIONAL_AUTHORITY_SECTION, q->zone->apex, q->zone->ns_rrset); @@ -969,7 +969,7 @@ static void answer_authoritative(struct nsd *nsd, struct query *q, answer_type *answer, - uint32_t domain_number, + size_t domain_number, int exact, domain_type *closest_match, domain_type *closest_encloser, @@ -999,16 +999,16 @@ answer_authoritative(struct nsd *nsd, name = domain_dname(closest_match); DEBUG(DEBUG_QUERY,2, (LOG_INFO, "expanding DNAME for q=%s", dname_to_string(name, NULL))); DEBUG(DEBUG_QUERY,2, (LOG_INFO, "->src is %s", - dname_to_string(domain_dname(closest_encloser), NULL))); + domain_to_string(closest_encloser))); DEBUG(DEBUG_QUERY,2, (LOG_INFO, "->dest is %s", - dname_to_string(domain_dname(dest), NULL))); + domain_to_string(dest))); /* if the DNAME set is not added we have a loop, do not follow */ added = add_rrset(q, answer, ANSWER_SECTION, closest_encloser, rrset); if(added) { domain_type* src = closest_encloser; const dname_type* newname = dname_replace(q->region, name, domain_dname(src), domain_dname(dest)); - uint32_t newnum = 0; + size_t newnum = 0; zone_type* origzone = q->zone; ++q->cname_count; if(!newname) { /* newname too long */ @@ -1047,23 +1047,26 @@ answer_authoritative(struct nsd *nsd, match = (domain_type *) region_alloc(q->region, sizeof(domain_type)); - memcpy(&match->node, &wildcard_child->node, sizeof(rbnode_t)); + match->rnode = NULL; + match->dname = wildcard_child->dname; match->parent = closest_encloser; match->wildcard_child_closest_match = match; match->number = domain_number; match->rrsets = wildcard_child->rrsets; match->is_existing = wildcard_child->is_existing; #ifdef NSEC3 - match->nsec3_cover = wildcard_child->nsec3_cover; -#ifdef FULL_PREHASH + match->nsec3 = wildcard_child->nsec3; + /* copy over these entries: match->nsec3_is_exact = wildcard_child->nsec3_is_exact; + match->nsec3_cover = wildcard_child->nsec3_cover; match->nsec3_wcard_child_cover = wildcard_child->nsec3_wcard_child_cover; match->nsec3_ds_parent_is_exact = wildcard_child->nsec3_ds_parent_is_exact; match->nsec3_ds_parent_cover = wildcard_child->nsec3_ds_parent_cover; -#endif - if (q->edns.dnssec_ok && q->zone->nsec3_soa_rr) { + */ + + if (q->edns.dnssec_ok && q->zone->nsec3_param) { /* Only add nsec3 wildcard data when do bit is set */ - nsec3_answer_wildcard(q, answer, wildcard_child, nsd->db, qname); + nsec3_answer_wildcard(q, answer, wildcard_child, qname); } #endif @@ -1080,9 +1083,9 @@ answer_authoritative(struct nsd *nsd, /* Authorative zone. */ #ifdef NSEC3 - if (q->edns.dnssec_ok && q->zone->nsec3_soa_rr) { + if (q->edns.dnssec_ok && q->zone->nsec3_param) { nsec3_answer_authoritative(&match, q, answer, - closest_encloser, nsd->db, qname); + closest_encloser, qname); } else #endif if (q->edns.dnssec_ok && zone_is_secure(q->zone)) { @@ -1131,7 +1134,7 @@ answer_authoritative(struct nsd *nsd, */ static void answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer, - uint32_t domain_number, int exact, domain_type *closest_match, + size_t domain_number, int exact, domain_type *closest_match, domain_type *closest_encloser, const dname_type *qname) { q->zone = domain_find_zone(closest_encloser); @@ -1157,8 +1160,8 @@ answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer, } /* see if the zone has expired (for secondary zones) */ - if(q->zone && q->zone->opts && zone_is_slave(q->zone->opts) - && !q->zone->is_ok) { + if(q->zone && q->zone->opts && q->zone->opts->pattern && + q->zone->opts->pattern->request_xfr != 0 && !q->zone->is_ok) { if(q->cname_count == 0) RCODE_SET(q->packet, RCODE_SERVFAIL); return; @@ -1218,18 +1221,9 @@ answer_query(struct nsd *nsd, struct query *q) return; } - q->domain = closest_encloser; answer_lookup_zone(nsd, q, &answer, 0, exact, closest_match, closest_encloser, q->qname); -#ifdef USE_ZONE_STATS - if (q->zone) { - ZTATUP2(q->zone, opcode, q->opcode); - ZTATUP2(q->zone, qtype, q->qtype); - ZTATUP2(q->zone, opcode, q->qclass); - } -#endif - offset = dname_label_offsets(q->qname)[domain_dname(closest_encloser)->label_count - 1] + QHEADERSZ; query_add_compression_domain(q, closest_encloser, offset); encode_answer(q, &answer); @@ -1323,7 +1317,7 @@ query_process(query_type *q, nsd_type *nsd) arcount = ARCOUNT(q->packet); if (arcount > 0) { - /* According to RFC 6891: + /* According to draft-ietf-dnsext-rfc2671bis-edns0-10: * "The placement flexibility for the OPT RR does not * override the need for the TSIG or SIG(0) RRs to be * the last in the additional section whenever they are @@ -1432,11 +1426,6 @@ query_add_optional(query_type *q, nsd_type *nsd) } ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); STATUP(nsd, edns); -#ifdef USE_ZONE_STATS - if (q->zone) { - ZTATUP(q->zone, edns); - } -#endif break; case EDNS_ERROR: if (q->edns.dnssec_ok) edns->error[7] = 0x80; @@ -1445,11 +1434,6 @@ query_add_optional(query_type *q, nsd_type *nsd) buffer_write(q->packet, edns->rdata_none, OPT_RDATA); ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); STATUP(nsd, ednserr); -#ifdef USE_ZONE_STATS - if (q->zone) { - ZTATUP(q->zone, ednserr); - } -#endif break; } diff --git a/usr.sbin/nsd/rdata.c b/usr.sbin/nsd/rdata.c index c913940a08d..d1ccc6effdf 100644 --- a/usr.sbin/nsd/rdata.c +++ b/usr.sbin/nsd/rdata.c @@ -1,7 +1,7 @@ /* * rdata.c -- RDATA conversion functions. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -775,9 +775,11 @@ rdata_wireformat_to_rdata_atoms(region_type *region, temp_rdatas[i].data[0] = dname->name_size; memcpy(temp_rdatas[i].data+1, dname_name(dname), dname->name_size); - } else + } else { temp_rdatas[i].domain = domain_table_insert(owners, dname); + temp_rdatas[i].domain->usage ++; + } } else { if (buffer_position(packet) + length > end) { if (required) { diff --git a/usr.sbin/nsd/region-allocator.c b/usr.sbin/nsd/region-allocator.c index 94eb33ab2aa..a57ad516daa 100644 --- a/usr.sbin/nsd/region-allocator.c +++ b/usr.sbin/nsd/region-allocator.c @@ -1,7 +1,7 @@ /* * region-allocator.c -- region based memory allocator. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -19,8 +19,12 @@ #undef ALIGNMENT #endif #define ALIGN_UP(x, s) (((x) + s - 1) & (~(s - 1))) -#define ALIGNMENT (sizeof(void *)) -#define CHECK_DOUBLE_FREE 0 /* set to 1 to perform expensive check for double recycle() */ +#if SIZEOF_OFF_T > SIZEOF_VOIDP +#define ALIGNMENT (sizeof(off_t)) +#else +#define ALIGNMENT (sizeof(void *)) +#endif +/* #define CHECK_DOUBLE_FREE 0 */ /* set to 1 to perform expensive check for double recycle() */ typedef struct cleanup cleanup_type; struct cleanup @@ -33,6 +37,11 @@ struct recycle_elem { struct recycle_elem* next; }; +struct large_elem { + struct large_elem* next; + struct large_elem* prev; +}; + struct region { size_t total_allocated; @@ -51,6 +60,7 @@ struct region size_t maximum_cleanup_count; size_t cleanup_count; cleanup_type *cleanups; + struct large_elem* large_list; size_t chunk_size; size_t large_object_size; @@ -80,6 +90,7 @@ alloc_region_base(void *(*allocator)(size_t size), result->unused_space = 0; result->recycle_bin = NULL; result->recycle_size = 0; + result->large_list = NULL; result->allocated = 0; result->data = NULL; @@ -174,6 +185,14 @@ region_destroy(region_type *region) deallocator(region->initial_data); if(region->recycle_bin) deallocator(region->recycle_bin); + if(region->large_list) { + struct large_elem* p = region->large_list, *np; + while(p) { + np = p->next; + deallocator(p); + p = np; + } + } deallocator(region); } @@ -231,19 +250,19 @@ region_alloc(region_type *region, size_t size) aligned_size = ALIGN_UP(size, ALIGNMENT); if (aligned_size >= region->large_object_size) { - result = region->allocator(size); + result = region->allocator(size + sizeof(struct large_elem)); if (!result) return NULL; - - if (!region_add_cleanup(region, region->deallocator, result)) { - region->deallocator(result); - return NULL; - } + ((struct large_elem*)result)->prev = NULL; + ((struct large_elem*)result)->next = region->large_list; + if(region->large_list) + region->large_list->prev = (struct large_elem*)result; + region->large_list = (struct large_elem*)result; region->total_allocated += size; ++region->large_objects; - return result; + return result + sizeof(struct large_elem); } if (region->recycle_bin && region->recycle_bin[aligned_size]) { @@ -330,6 +349,17 @@ region_free_all(region_type *region) region->recycle_size = 0; } + if(region->large_list) { + struct large_elem* p = region->large_list, *np; + void (*deallocator)(void *) = region->deallocator; + while(p) { + np = p->next; + deallocator(p); + p = np; + } + region->large_list = NULL; + } + region->data = region->initial_data; region->cleanup_count = 0; region->allocated = 0; @@ -352,7 +382,6 @@ void region_recycle(region_type *region, void *block, size_t size) { size_t aligned_size; - size_t i; if(!block || !region->recycle_bin) return; @@ -367,6 +396,7 @@ region_recycle(region_type *region, void *block, size_t size) /* we rely on the fact that ALIGNMENT is void* so the next will fit */ assert(aligned_size >= sizeof(struct recycle_elem)); +#ifdef CHECK_DOUBLE_FREE if(CHECK_DOUBLE_FREE) { /* make sure the same ptr is not freed twice. */ struct recycle_elem *p = region->recycle_bin[aligned_size]; @@ -375,29 +405,27 @@ region_recycle(region_type *region, void *block, size_t size) p = p->next; } } +#endif elem->next = region->recycle_bin[aligned_size]; region->recycle_bin[aligned_size] = elem; region->recycle_size += aligned_size; region->unused_space -= aligned_size - size; return; - } - - /* a large allocation */ - region->total_allocated -= size; - --region->large_objects; - for(i=0; i<region->cleanup_count; i++) { - while(region->cleanups[i].data == block) { - /* perform action (deallocator) on block */ - region->cleanups[i].action(block); - region->cleanups[i].data = NULL; - /* remove cleanup - move last entry here, check this one again */ - --region->cleanup_count; - region->cleanups[i].action = - region->cleanups[region->cleanup_count].action; - region->cleanups[i].data = - region->cleanups[region->cleanup_count].data; - } + } else { + struct large_elem* l; + + /* a large allocation */ + region->total_allocated -= size; + --region->large_objects; + + l = (struct large_elem*)(block-sizeof(struct large_elem)); + if(l->prev) + l->prev->next = l->next; + else region->large_list = l->next; + if(l->next) + l->next->prev = l->prev; + region->deallocator(l); } } @@ -434,6 +462,16 @@ size_t region_get_recycle_size(region_type* region) return region->recycle_size; } +size_t region_get_mem(region_type* region) +{ + return region->total_allocated; +} + +size_t region_get_mem_unused(region_type* region) +{ + return region->unused_space; +} + /* debug routine, includes here to keep base region-allocator independent */ #undef ALIGN_UP #include "util.h" diff --git a/usr.sbin/nsd/rrl.c b/usr.sbin/nsd/rrl.c index 7eb571f2aea..1c5e9c050f0 100644 --- a/usr.sbin/nsd/rrl.c +++ b/usr.sbin/nsd/rrl.c @@ -6,8 +6,6 @@ */ #include "config.h" #include <errno.h> -#include <ctype.h> -#include "dns.h" #include "rrl.h" #include "util.h" #include "lookup3.h" @@ -59,37 +57,6 @@ static uint32_t rrl_whitelist_ratelimit = RRL_WLIST_LIMIT; /* 2x qps */ static void** rrl_maps = NULL; static size_t rrl_maps_num = 0; -/* from NSD4 for RRL logs */ -static char* wiredname2str(const uint8_t* dname) -{ - static char buf[MAXDOMAINLEN*5+3]; - char* p = buf; - uint8_t lablen; - if(*dname == 0) { - strlcpy(buf, ".", sizeof(buf)); - return buf; - } - lablen = *dname++; - while(lablen) { - while(lablen--) { - uint8_t ch = *dname++; - if (isalnum(ch) || ch == '-' || ch == '_') { - *p++ = ch; - } else if (ch == '.' || ch == '\\') { - *p++ = '\\'; - *p++ = ch; - } else { - snprintf(p, 5, "\\%03u", (unsigned int)ch); - p += 4; - } - } - lablen = *dname++; - *p++ = '.'; - } - *p++ = 0; - return buf; -} - void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm, size_t sm, size_t plf, size_t pls) { @@ -133,6 +100,13 @@ void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm, size_t sm, #endif } +void rrl_set_limit(size_t lm, size_t wlm, size_t sm) +{ + rrl_ratelimit = lm*2; + rrl_whitelist_ratelimit = wlm*2; + rrl_slip_ratio = sm; +} + void rrl_init(size_t ch) { if(!rrl_maps || ch >= rrl_maps_num) @@ -305,13 +279,13 @@ static void examine_query(query_type* query, uint32_t* hash, uint64_t* source, uint16_t c, c2; /* size with 16 bytes to spare */ uint8_t buf[MAXDOMAINLEN + sizeof(*source) + sizeof(c) + 16]; - const uint8_t* dname = NULL; size_t dname_len; + const uint8_t* dname = NULL; size_t dname_len = 0; uint32_t r = 0x267fcd16; *source = rrl_get_source(query, &c2); c = rrl_classify(query, &dname, &dname_len); if(query->zone && query->zone->opts && - (query->zone->opts->rrl_whitelist & c)) + (query->zone->opts->pattern->rrl_whitelist & c)) *lm = rrl_whitelist_ratelimit; if(*lm == 0) return; c |= c2; @@ -354,14 +328,11 @@ rrl_msg(query_type* query, const char* str) uint64_t s; char address[128]; if(verbosity < 2) return; - if (addr2ip(query->addr, address, sizeof(address))) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "addr2ip failed")); - strlcpy(address, "[unknown]", sizeof(address)); - } + addr2str(&query->addr, address, sizeof(address)); s = rrl_get_source(query, &c2); c = rrl_classify(query, &d, &d_len) | c2; if(query->zone && query->zone->opts && - (query->zone->opts->rrl_whitelist & c)) + (query->zone->opts->pattern->rrl_whitelist & c)) wl = 1; log_msg(LOG_INFO, "ratelimit %s %s type %s%s target %s query %s %s", str, d?wiredname2str(d):"", rrltype2str(c), @@ -392,10 +363,7 @@ uint32_t rrl_update(query_type* query, uint32_t hash, uint64_t source, if(verbosity >=2 && used_to_block(b->rate, b->counter, rrl_ratelimit)) { char address[128]; - if (addr2ip(query->addr, address, sizeof(address))) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "addr2ip failed")); - strlcpy(address, "[unknown]", sizeof(address)); - } + addr2str(&query->addr, address, sizeof(address)); log_msg(LOG_INFO, "ratelimit unblock ~ type %s target %s query %s %s (%s collision)", rrltype2str(b->flags), rrlsource2str(b->source, b->flags), @@ -477,7 +445,7 @@ int rrl_process_query(query_type* query) query_state_type rrl_slip(query_type* query) { - /* discard number of packets, randomly */ + /* discard number the packets, randomly */ #ifdef HAVE_ARC4RANDOM if((rrl_slip_ratio > 0) && ((rrl_slip_ratio == 1) || ((arc4random() % rrl_slip_ratio) == 0))) { #else diff --git a/usr.sbin/nsd/server.c b/usr.sbin/nsd/server.c index 7aecf03e650..33b3fb88666 100644 --- a/usr.sbin/nsd/server.c +++ b/usr.sbin/nsd/server.c @@ -1,7 +1,7 @@ /* * server.c -- nsd(8) network input/output * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -33,6 +33,11 @@ #ifndef SHUT_WR #define SHUT_WR 1 #endif +#ifndef USE_MINI_EVENT +#include <event.h> +#else +#include "mini_event.h" +#endif #include <openssl/rand.h> @@ -41,12 +46,17 @@ #include "netio.h" #include "xfrd.h" #include "xfrd-tcp.h" +#include "xfrd-disk.h" #include "difffile.h" #include "nsec3.h" #include "ipc.h" +#include "udb.h" +#include "remote.h" #include "lookup3.h" #include "rrl.h" +#define RELOAD_SYNC_TIMEOUT 25 /* seconds */ + /* * Data for the UDP handlers. */ @@ -57,19 +67,33 @@ struct udp_handler_data query_type *query; }; -/* - * Data for the TCP accept handlers. Most data is simply passed along - * to the TCP connection handler. - */ struct tcp_accept_handler_data { struct nsd *nsd; struct nsd_socket *socket; - size_t tcp_accept_handler_count; - netio_handler_type *tcp_accept_handlers; + int event_added; + struct event event; }; -int slowaccept; -struct timespec slowaccept_timeout; +/* + * These globals are used to enable the TCP accept handlers + * when the number of TCP connection drops below the maximum + * number of TCP connections. + */ +static size_t tcp_accept_handler_count; +static struct tcp_accept_handler_data* tcp_accept_handlers; + +static struct event slowaccept_event; +static int slowaccept; + +#ifndef NONBLOCKING_IS_BROKEN +# define NUM_RECV_PER_SELECT 100 +#endif + +#if (!defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG)) +struct mmsghdr msgs[NUM_RECV_PER_SELECT]; +struct iovec iovecs[NUM_RECV_PER_SELECT]; +struct query *queries[NUM_RECV_PER_SELECT]; +#endif /* * Data for the TCP connection handlers. @@ -104,14 +128,6 @@ struct tcp_handler_data query_type* query; /* - * These fields are used to enable the TCP accept handlers - * when the number of TCP connection drops below the maximum - * number of TCP connections. - */ - size_t tcp_accept_handler_count; - netio_handler_type *tcp_accept_handlers; - - /* * The query_state is used to remember if we are performing an * AXFR, if we're done processing, or if we should discard the * query and connection. @@ -119,6 +135,11 @@ struct tcp_handler_data query_state_type query_state; /* + * The event for the file descriptor and tcp timeout + */ + struct event event; + + /* * The bytes_transmitted field is used to remember the number * of bytes transmitted when receiving or sending a DNS * packet. The count includes the two additional bytes used @@ -135,9 +156,7 @@ struct tcp_handler_data /* * Handle incoming queries on the UDP server sockets. */ -static void handle_udp(netio_type *netio, - netio_handler_type *handler, - netio_event_types_type event_types); +static void handle_udp(int fd, short event, void* arg); /* * Handle incoming connections on the TCP sockets. These handlers @@ -148,27 +167,21 @@ static void handle_udp(netio_type *netio, * NETIO_EVENT_NONE type. This is done using the function * configure_tcp_accept_handlers. */ -static void handle_tcp_accept(netio_type *netio, - netio_handler_type *handler, - netio_event_types_type event_types); +static void handle_tcp_accept(int fd, short event, void* arg); /* * Handle incoming queries on a TCP connection. The TCP connections * are configured to be non-blocking and the handler may be called * multiple times before a complete query is received. */ -static void handle_tcp_reading(netio_type *netio, - netio_handler_type *handler, - netio_event_types_type event_types); +static void handle_tcp_reading(int fd, short event, void* arg); /* * Handle outgoing responses on a TCP connection. The TCP connections * are configured to be non-blocking and the handler may be called * multiple times before a complete response is sent. */ -static void handle_tcp_writing(netio_type *netio, - netio_handler_type *handler, - netio_event_types_type event_types); +static void handle_tcp_writing(int fd, short event, void* arg); /* * Send all children the quit nonblocking, then close pipe. @@ -183,18 +196,9 @@ static void set_children_stats(struct nsd* nsd); #endif /* BIND8_STATS */ /* - * Change the event types the HANDLERS are interested in to - * EVENT_TYPES. + * Change the event types the HANDLERS are interested in to EVENT_TYPES. */ -static void configure_handler_event_types(size_t count, - netio_handler_type *handlers, - netio_event_types_type event_types); - -/* - * start xfrdaemon (again). - */ -static pid_t -server_start_xfrd(struct nsd *nsd, netio_handler_type* handler); +static void configure_handler_event_types(short event_types); static uint16_t *compressed_dname_offsets = 0; static uint32_t compression_table_capacity = 0; @@ -252,6 +256,9 @@ restart_child_servers(struct nsd *nsd, region_type* region, netio_type* netio, default: /* SERVER MAIN */ close(nsd->children[i].parent_fd); nsd->children[i].parent_fd = -1; + if (fcntl(nsd->children[i].child_fd, F_SETFL, O_NONBLOCK) == -1) { + log_msg(LOG_ERR, "cannot fcntl pipe: %s", strerror(errno)); + } if(!nsd->children[i].handler) { ipc_data = (struct main_ipc_handler_data*) region_alloc( @@ -265,8 +272,6 @@ restart_child_servers(struct nsd *nsd, region_type* region, netio_type* netio, ipc_data->got_bytes = 0; ipc_data->total_bytes = 0; ipc_data->acl_num = 0; - ipc_data->busy_writing_zone_state = 0; - ipc_data->write_conn = xfrd_tcp_create(region); nsd->children[i].handler = (struct netio_handler*) region_alloc( region, sizeof(struct netio_handler)); nsd->children[i].handler->fd = nsd->children[i].child_fd; @@ -280,25 +285,32 @@ restart_child_servers(struct nsd *nsd, region_type* region, netio_type* netio, ipc_data = (struct main_ipc_handler_data*) nsd->children[i].handler->user_data; ipc_data->forward_mode = 0; - ipc_data->busy_writing_zone_state = 0; /* restart - update fd */ nsd->children[i].handler->fd = nsd->children[i].child_fd; break; case 0: /* CHILD */ + /* the child need not be able to access the + * nsd.db file */ + namedb_close_udb(nsd->db); nsd->pid = 0; nsd->child_count = 0; nsd->server_kind = nsd->children[i].kind; nsd->this_child = &nsd->children[i]; /* remove signal flags inherited from parent the parent will handle them. */ + nsd->signal_hint_reload_hup = 0; nsd->signal_hint_reload = 0; nsd->signal_hint_child = 0; nsd->signal_hint_quit = 0; nsd->signal_hint_shutdown = 0; nsd->signal_hint_stats = 0; nsd->signal_hint_statsusr = 0; + close(*xfrd_sock_p); close(nsd->this_child->child_fd); nsd->this_child->child_fd = -1; + if (fcntl(nsd->this_child->parent_fd, F_SETFL, O_NONBLOCK) == -1) { + log_msg(LOG_ERR, "cannot fcntl pipe: %s", strerror(errno)); + } server_child(nsd); /* NOTREACH */ exit(0); @@ -384,6 +396,60 @@ server_init(struct nsd *nsd) return -1; } +#if defined(SO_RCVBUF) || defined(SO_SNDBUF) + if(1) { + int rcv = 1*1024*1024; + int snd = 1*1024*1024; + +#ifdef SO_RCVBUF +# ifdef SO_RCVBUFFORCE + if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv, + (socklen_t)sizeof(rcv)) < 0) { + if(errno != EPERM && errno != ENOBUFS) { + log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUFFORCE, " + "...) failed: %s", strerror(errno)); + return -1; + } +# else + if(1) { +# endif /* SO_RCVBUFFORCE */ + if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv, + (socklen_t)sizeof(rcv)) < 0) { + if(errno != ENOBUFS) { + log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUF, " + "...) failed: %s", strerror(errno)); + return -1; + } + } + } +#endif /* SO_RCVBUF */ + +#ifdef SO_SNDBUF +# ifdef SO_SNDBUFFORCE + if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd, + (socklen_t)sizeof(snd)) < 0) { + if(errno != EPERM && errno != ENOBUFS) { + log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUFFORCE, " + "...) failed: %s", strerror(errno)); + return -1; + } +# else + if(1) { +# endif /* SO_SNDBUFFORCE */ + if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_SNDBUF, (void*)&snd, + (socklen_t)sizeof(snd)) < 0) { + if(errno != ENOBUFS) { + log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUF, " + "...) failed: %s", strerror(errno)); + return -1; + } + } + } +#endif /* SO_SNDBUF */ + + } +#endif /* defined(SO_RCVBUF) || defined(SO_SNDBUF) */ + #if defined(INET6) if (nsd->udp[i].addr->ai_family == AF_INET6) { # if defined(IPV6_V6ONLY) @@ -596,21 +662,19 @@ server_prepare(struct nsd *nsd) #endif /* RATELIMIT */ /* Open the database... */ - if ((nsd->db = namedb_open(nsd->dbfile, nsd->options, nsd->child_count)) == NULL) { + if ((nsd->db = namedb_open(nsd->dbfile, nsd->options)) == NULL) { log_msg(LOG_ERR, "unable to open the database %s: %s", nsd->dbfile, strerror(errno)); + unlink(nsd->task[0]->fname); + unlink(nsd->task[1]->fname); + xfrd_del_tempdir(nsd); return -1; } - - /* Read diff file */ - if(!diff_read_file(nsd->db, nsd->options, NULL, nsd->child_count)) { - log_msg(LOG_ERR, "The diff file contains errors. Will continue " - "without it"); - } - -#ifdef NSEC3 - prehash(nsd->db, 0); -#endif + /* check if zone files have been modified */ + /* NULL for taskudb because we send soainfo in a moment, batched up, + * for all zones */ + if(nsd->options->zonefiles_check) + namedb_check_zonefiles(nsd->db, nsd->options, NULL, NULL); compression_table_capacity = 0; initialize_dname_compression_tables(nsd); @@ -685,62 +749,199 @@ server_shutdown(struct nsd *nsd) } } - log_finalize(); tsig_finalize(); +#ifdef HAVE_SSL + daemon_remote_delete(nsd->rc); /* ssl-delete secret keys */ +#endif +#if 0 /* OS collects memory pages */ nsd_options_destroy(nsd->options); region_destroy(nsd->region); - +#endif + log_finalize(); exit(0); } -static pid_t -server_start_xfrd(struct nsd *nsd, netio_handler_type* handler) +void +server_prepare_xfrd(struct nsd* nsd) +{ + char tmpfile[256]; + /* create task mmaps */ + nsd->mytask = 0; + snprintf(tmpfile, sizeof(tmpfile), "%snsd.%u.task.0", + nsd->options->xfrdir, (unsigned)getpid()); + nsd->task[0] = task_file_create(tmpfile); + if(!nsd->task[0]) + exit(1); + snprintf(tmpfile, sizeof(tmpfile), "%snsd.%u.task.1", + nsd->options->xfrdir, (unsigned)getpid()); + nsd->task[1] = task_file_create(tmpfile); + if(!nsd->task[1]) { + unlink(nsd->task[0]->fname); + exit(1); + } + assert(udb_base_get_userdata(nsd->task[0])->data == 0); + assert(udb_base_get_userdata(nsd->task[1])->data == 0); + /* create xfrd listener structure */ + nsd->xfrd_listener = region_alloc(nsd->region, + sizeof(netio_handler_type)); + nsd->xfrd_listener->user_data = (struct ipc_handler_conn_data*) + region_alloc(nsd->region, sizeof(struct ipc_handler_conn_data)); + nsd->xfrd_listener->fd = -1; + ((struct ipc_handler_conn_data*)nsd->xfrd_listener->user_data)->nsd = + nsd; + ((struct ipc_handler_conn_data*)nsd->xfrd_listener->user_data)->conn = + xfrd_tcp_create(nsd->region, QIOBUFSZ); +} + + +void +server_start_xfrd(struct nsd *nsd, int del_db, int reload_active) { pid_t pid; int sockets[2] = {0,0}; - zone_type* zone; struct ipc_handler_conn_data *data; - /* no need to send updates for zones, because xfrd will read from fork-memory */ - for(zone = nsd->db->zones; zone; zone=zone->next) { - zone->updated = 0; - } - if(handler->fd != -1) - close(handler->fd); + if(nsd->xfrd_listener->fd != -1) + close(nsd->xfrd_listener->fd); + if(del_db) { + /* recreate taskdb that xfrd was using, it may be corrupt */ + /* we (or reload) use nsd->mytask, and xfrd uses the other */ + char* tmpfile = nsd->task[1-nsd->mytask]->fname; + nsd->task[1-nsd->mytask]->fname = NULL; + /* free alloc already, so udb does not shrink itself */ + udb_alloc_delete(nsd->task[1-nsd->mytask]->alloc); + nsd->task[1-nsd->mytask]->alloc = NULL; + udb_base_free(nsd->task[1-nsd->mytask]); + /* create new file, overwrite the old one */ + nsd->task[1-nsd->mytask] = task_file_create(tmpfile); + free(tmpfile); + } if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { log_msg(LOG_ERR, "startxfrd failed on socketpair: %s", strerror(errno)); - return -1; + return; } pid = fork(); switch (pid) { case -1: log_msg(LOG_ERR, "fork xfrd failed: %s", strerror(errno)); break; - case 0: - /* CHILD: close first socket, use second one */ + default: + /* PARENT: close first socket, use second one */ close(sockets[0]); - xfrd_init(sockets[1], nsd); + if (fcntl(sockets[1], F_SETFL, O_NONBLOCK) == -1) { + log_msg(LOG_ERR, "cannot fcntl pipe: %s", strerror(errno)); + } + if(del_db) xfrd_free_namedb(nsd); + /* use other task than I am using, since if xfrd died and is + * restarted, the reload is using nsd->mytask */ + nsd->mytask = 1 - nsd->mytask; + xfrd_init(sockets[1], nsd, del_db, reload_active); /* ENOTREACH */ break; - default: - /* PARENT: close second socket, use first one */ + case 0: + /* CHILD: close second socket, use first one */ close(sockets[1]); - handler->fd = sockets[0]; + if (fcntl(sockets[0], F_SETFL, O_NONBLOCK) == -1) { + log_msg(LOG_ERR, "cannot fcntl pipe: %s", strerror(errno)); + } + nsd->xfrd_listener->fd = sockets[0]; break; } - /* PARENT only */ - handler->timeout = NULL; - handler->event_types = NETIO_EVENT_READ; - handler->event_handler = parent_handle_xfrd_command; + /* server-parent only */ + nsd->xfrd_listener->timeout = NULL; + nsd->xfrd_listener->event_types = NETIO_EVENT_READ; + nsd->xfrd_listener->event_handler = parent_handle_xfrd_command; /* clear ongoing ipc reads */ - data = (struct ipc_handler_conn_data *) handler->user_data; + data = (struct ipc_handler_conn_data *) nsd->xfrd_listener->user_data; data->conn->is_reading = 0; - return pid; +} + +/** add all soainfo to taskdb */ +static void +add_all_soa_to_task(struct nsd* nsd, struct udb_base* taskudb) +{ + struct radnode* n; + udb_ptr task_last; /* last task, mytask is empty so NULL */ + /* add all SOA INFO to mytask */ + udb_ptr_init(&task_last, taskudb); + for(n=radix_first(nsd->db->zonetree); n; n=radix_next(n)) { + task_new_soainfo(taskudb, &task_last, (zone_type*)n->elem); + } + udb_ptr_unlink(&task_last, taskudb); +} + +void +server_send_soa_xfrd(struct nsd* nsd, int shortsoa) +{ + /* normally this exchanges the SOA from nsd->xfrd and the expire back. + * parent fills one taskdb with soas, xfrd fills other with expires. + * then they exchange and process. + * shortsoa: xfrd crashes and needs to be restarted and one taskdb + * may be in use by reload. Fill SOA in taskdb and give to xfrd. + * expire notifications can be sent back via a normal reload later + * (xfrd will wait for current running reload to finish if any). + */ + sig_atomic_t cmd = 0; +#ifdef BIND8_STATS + pid_t mypid; +#endif + int xfrd_sock = nsd->xfrd_listener->fd; + struct udb_base* taskudb = nsd->task[nsd->mytask]; + udb_ptr t; + if(shortsoa) { + /* put SOA in xfrd task because mytask may be in use */ + taskudb = nsd->task[1-nsd->mytask]; + } + + add_all_soa_to_task(nsd, taskudb); + if(!shortsoa) { + /* wait for xfrd to signal task is ready, RELOAD signal */ + if(block_read(nsd, xfrd_sock, &cmd, sizeof(cmd), -1) != sizeof(cmd) || + cmd != NSD_RELOAD) { + log_msg(LOG_ERR, "did not get start signal from xfrd"); + exit(1); + } + } + /* give xfrd our task, signal it with RELOAD_DONE */ + task_process_sync(taskudb); + cmd = NSD_RELOAD_DONE; + if(!write_socket(xfrd_sock, &cmd, sizeof(cmd))) { + log_msg(LOG_ERR, "problems sending soa end from reload %d to xfrd: %s", + (int)nsd->pid, strerror(errno)); + } +#ifdef BIND8_STATS + mypid = getpid(); + if(!write_socket(nsd->xfrd_listener->fd, &mypid, sizeof(mypid))) { + log_msg(LOG_ERR, "problems sending reloadpid to xfrd: %s", + strerror(errno)); + } +#endif + + if(!shortsoa) { + /* process the xfrd task works (expiry data) */ + nsd->mytask = 1 - nsd->mytask; + taskudb = nsd->task[nsd->mytask]; + task_remap(taskudb); + udb_ptr_new(&t, taskudb, udb_base_get_userdata(taskudb)); + while(!udb_ptr_is_null(&t)) { + task_process_expire(nsd->db, TASKLIST(&t)); + udb_ptr_set_rptr(&t, taskudb, &TASKLIST(&t)->next); + } + udb_ptr_unlink(&t, taskudb); + task_clear(taskudb); + + /* tell xfrd that the task is emptied, signal with RELOAD_DONE */ + cmd = NSD_RELOAD_DONE; + if(!write_socket(xfrd_sock, &cmd, sizeof(cmd))) { + log_msg(LOG_ERR, "problems sending soa end from reload %d to xfrd: %s", + (int)nsd->pid, strerror(errno)); + } + } } /* pass timeout=-1 for blocking. Returns size, 0, -1(err), or -2(timeout) */ -static ssize_t +ssize_t block_read(struct nsd* nsd, int s, void* p, ssize_t sz, int timeout) { uint8_t* buf = (uint8_t*) p; @@ -795,58 +996,121 @@ block_read(struct nsd* nsd, int s, void* p, ssize_t sz, int timeout) return total; } +static void +reload_process_tasks(struct nsd* nsd, udb_ptr* last_task, int cmdsocket) +{ + sig_atomic_t cmd = NSD_QUIT_SYNC; + udb_ptr t, next; + udb_base* u = nsd->task[nsd->mytask]; + udb_ptr_init(&next, u); + udb_ptr_new(&t, u, udb_base_get_userdata(u)); + udb_base_set_userdata(u, 0); + while(!udb_ptr_is_null(&t)) { + /* store next in list so this one can be deleted or reused */ + udb_ptr_set_rptr(&next, u, &TASKLIST(&t)->next); + udb_rptr_zero(&TASKLIST(&t)->next, u); + + /* process task t */ + /* append results for task t and update last_task */ + task_process_in_reload(nsd, u, last_task, &t); + + /* go to next */ + udb_ptr_set_ptr(&t, u, &next); + + /* if the parent has quit, we must quit too, poll the fd for cmds */ + if(block_read(nsd, cmdsocket, &cmd, sizeof(cmd), 0) == sizeof(cmd)) { + DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: ipc command from main %d", (int)cmd)); + if(cmd == NSD_QUIT) { + DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: quit to follow nsd")); + /* sync to disk (if needed) */ + udb_base_sync(nsd->db->udb, 0); + /* unlink files of remainder of tasks */ + while(!udb_ptr_is_null(&t)) { + if(TASKLIST(&t)->task_type == task_apply_xfr) { + xfrd_unlink_xfrfile(nsd, TASKLIST(&t)->yesno); + } + udb_ptr_set_rptr(&t, u, &TASKLIST(&t)->next); + } + udb_ptr_unlink(&t, u); + udb_ptr_unlink(&next, u); + exit(0); + } + } + + } + udb_ptr_unlink(&t, u); + udb_ptr_unlink(&next, u); +} + +#ifdef BIND8_STATS +static void +parent_send_stats(struct nsd* nsd, int cmdfd) +{ + size_t i; + if(!write_socket(cmdfd, &nsd->st, sizeof(nsd->st))) { + log_msg(LOG_ERR, "could not write stats to reload"); + return; + } + for(i=0; i<nsd->child_count; i++) + if(!write_socket(cmdfd, &nsd->children[i].query_count, + sizeof(stc_t))) { + log_msg(LOG_ERR, "could not write stats to reload"); + return; + } +} + +static void +reload_do_stats(int cmdfd, struct nsd* nsd, udb_ptr* last) +{ + struct nsdst s; + stc_t* p; + size_t i; + if(block_read(nsd, cmdfd, &s, sizeof(s), + RELOAD_SYNC_TIMEOUT) != sizeof(s)) { + log_msg(LOG_ERR, "could not read stats from oldpar"); + return; + } + s.db_disk = nsd->db->udb->base_size; + s.db_mem = region_get_mem(nsd->db->region); + p = (stc_t*)task_new_stat_info(nsd->task[nsd->mytask], last, &s, + nsd->child_count); + if(!p) return; + for(i=0; i<nsd->child_count; i++) { + if(block_read(nsd, cmdfd, p++, sizeof(stc_t), 1)!=sizeof(stc_t)) + return; + } +} +#endif /* BIND8_STATS */ + /* * Reload the database, stop parent, re-fork children and continue. * as server_main. */ static void server_reload(struct nsd *nsd, region_type* server_region, netio_type* netio, - int cmdsocket, int* xfrd_sock_p) + int cmdsocket) { - pid_t old_pid; +#ifdef BIND8_STATS + pid_t mypid; +#endif sig_atomic_t cmd = NSD_QUIT_SYNC; - zone_type* zone; - int xfrd_sock = *xfrd_sock_p; int ret; + udb_ptr last_task; + + /* see what tasks we got from xfrd */ + task_remap(nsd->task[nsd->mytask]); + udb_ptr_init(&last_task, nsd->task[nsd->mytask]); + reload_process_tasks(nsd, &last_task, cmdsocket); - if(db_crc_different(nsd->db) == 0) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, - "CRC the same. skipping %s.", nsd->db->filename)); - } else { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, - "CRC different. reread of %s.", nsd->db->filename)); - namedb_close(nsd->db); - if ((nsd->db = namedb_open(nsd->dbfile, nsd->options, - nsd->child_count)) == NULL) { - log_msg(LOG_ERR, "unable to reload the database: %s", strerror(errno)); - exit(1); - } -#if defined(NSEC3) && !defined(FULL_PREHASH) - prehash(nsd->db, 0); -#endif - } - if(!diff_read_file(nsd->db, nsd->options, NULL, nsd->child_count)) { - log_msg(LOG_ERR, "unable to load the diff file: %s", nsd->options->difffile); - exit(1); - } - log_msg(LOG_INFO, "memory recyclebin holds %lu bytes", (unsigned long) - region_get_recycle_size(nsd->db->region)); #ifndef NDEBUG if(nsd_debug_level >= 1) region_log_stats(nsd->db->region); #endif /* NDEBUG */ -#ifdef NSEC3 -#ifdef FULL_PREHASH - prehash(nsd->db, 1); -#endif /* FULL_PREHASH */ -#endif /* NSEC3 */ + /* sync to disk (if needed) */ + udb_base_sync(nsd->db->udb, 0); initialize_dname_compression_tables(nsd); - /* Get our new process id */ - old_pid = nsd->pid; - nsd->pid = getpid(); - #ifdef BIND8_STATS /* Restart dumping stats if required. */ time(&nsd->st.boot); @@ -854,38 +1118,29 @@ server_reload(struct nsd *nsd, region_type* server_region, netio_type* netio, #endif /* Start new child processes */ - if (server_start_children(nsd, server_region, netio, xfrd_sock_p) != 0) { - send_children_quit(nsd); /* no wait required */ + if (server_start_children(nsd, server_region, netio, &nsd-> + xfrd_listener->fd) != 0) { + send_children_quit(nsd); exit(1); } /* if the parent has quit, we must quit too, poll the fd for cmds */ if(block_read(nsd, cmdsocket, &cmd, sizeof(cmd), 0) == sizeof(cmd)) { - DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: ipc command from main %d", cmd)); + DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: ipc command from main %d", (int)cmd)); if(cmd == NSD_QUIT) { DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: quit to follow nsd")); - send_children_quit(nsd); /* no wait required */ + send_children_quit(nsd); exit(0); } } - /* Overwrite pid before closing old parent, to avoid race condition: - * - parent process already closed - * - pidfile still contains old_pid - * - control script contacts parent process, using contents of pidfile - */ - if (writepid(nsd) == -1) { - log_msg(LOG_ERR, "cannot overwrite the pidfile %s: %s", nsd->pidfile, strerror(errno)); - } - -#define RELOAD_SYNC_TIMEOUT 25 /* seconds */ /* Send quit command to parent: blocking, wait for receipt. */ do { DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: ipc send quit to main")); if (!write_socket(cmdsocket, &cmd, sizeof(cmd))) { - log_msg(LOG_ERR, "problems sending command from reload %d to oldnsd %d: %s", - (int)nsd->pid, (int)old_pid, strerror(errno)); + log_msg(LOG_ERR, "problems sending command from reload to oldnsd: %s", + strerror(errno)); } /* blocking: wait for parent to really quit. (it sends RELOAD as ack) */ DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: ipc wait for ack main")); @@ -903,77 +1158,29 @@ server_reload(struct nsd *nsd, region_type* server_region, netio_type* netio, if(cmd == NSD_QUIT) { /* small race condition possible here, parent got quit cmd. */ send_children_quit(nsd); - unlinkpid(nsd->pidfile); exit(1); } assert(ret==-1 || ret == 0 || cmd == NSD_RELOAD); - - /* inform xfrd of new SOAs */ - cmd = NSD_SOA_BEGIN; - if(!write_socket(xfrd_sock, &cmd, sizeof(cmd))) { - log_msg(LOG_ERR, "problems sending soa begin from reload %d to xfrd: %s", - (int)nsd->pid, strerror(errno)); - } - for(zone= nsd->db->zones; zone; zone = zone->next) { - uint16_t sz; - const dname_type *dname_ns=0, *dname_em=0; - if(zone->updated == 0) - continue; - DEBUG(DEBUG_IPC,1, (LOG_INFO, "nsd: sending soa info for zone %s", - dname_to_string(domain_dname(zone->apex),0))); - cmd = NSD_SOA_INFO; - sz = dname_total_size(domain_dname(zone->apex)); - if(zone->soa_rrset) { - dname_ns = domain_dname( - rdata_atom_domain(zone->soa_rrset->rrs[0].rdatas[0])); - dname_em = domain_dname( - rdata_atom_domain(zone->soa_rrset->rrs[0].rdatas[1])); - sz += sizeof(uint32_t)*6 + sizeof(uint8_t)*2 - + dname_ns->name_size + dname_em->name_size; - } - sz = htons(sz); - /* use blocking writes */ - if(!write_socket(xfrd_sock, &cmd, sizeof(cmd)) || - !write_socket(xfrd_sock, &sz, sizeof(sz)) || - !write_socket(xfrd_sock, domain_dname(zone->apex), - dname_total_size(domain_dname(zone->apex)))) - { - log_msg(LOG_ERR, "problems sending soa info from reload %d to xfrd: %s", - (int)nsd->pid, strerror(errno)); - } - if(zone->soa_rrset) { - uint32_t ttl = htonl(zone->soa_rrset->rrs[0].ttl); - assert(dname_ns && dname_em); - assert(zone->soa_rrset->rr_count > 0); - assert(rrset_rrtype(zone->soa_rrset) == TYPE_SOA); - assert(zone->soa_rrset->rrs[0].rdata_count == 7); - if(!write_socket(xfrd_sock, &ttl, sizeof(uint32_t)) - || !write_socket(xfrd_sock, &dname_ns->name_size, sizeof(uint8_t)) - || !write_socket(xfrd_sock, dname_name(dname_ns), dname_ns->name_size) - || !write_socket(xfrd_sock, &dname_em->name_size, sizeof(uint8_t)) - || !write_socket(xfrd_sock, dname_name(dname_em), dname_em->name_size) - || !write_socket(xfrd_sock, rdata_atom_data( - zone->soa_rrset->rrs[0].rdatas[2]), sizeof(uint32_t)) - || !write_socket(xfrd_sock, rdata_atom_data( - zone->soa_rrset->rrs[0].rdatas[3]), sizeof(uint32_t)) - || !write_socket(xfrd_sock, rdata_atom_data( - zone->soa_rrset->rrs[0].rdatas[4]), sizeof(uint32_t)) - || !write_socket(xfrd_sock, rdata_atom_data( - zone->soa_rrset->rrs[0].rdatas[5]), sizeof(uint32_t)) - || !write_socket(xfrd_sock, rdata_atom_data( - zone->soa_rrset->rrs[0].rdatas[6]), sizeof(uint32_t))) - { - log_msg(LOG_ERR, "problems sending soa info from reload %d to xfrd: %s", - (int)nsd->pid, strerror(errno)); - } - } - zone->updated = 0; +#ifdef BIND8_STATS + reload_do_stats(cmdsocket, nsd, &last_task); +#endif + udb_ptr_unlink(&last_task, nsd->task[nsd->mytask]); + task_process_sync(nsd->task[nsd->mytask]); + + /* send soainfo to the xfrd process, signal it that reload is done, + * it picks up the taskudb */ + cmd = NSD_RELOAD_DONE; + if(!write_socket(nsd->xfrd_listener->fd, &cmd, sizeof(cmd))) { + log_msg(LOG_ERR, "problems sending reload_done xfrd: %s", + strerror(errno)); } - cmd = NSD_SOA_END; - if(!write_socket(xfrd_sock, &cmd, sizeof(cmd))) { - log_msg(LOG_ERR, "problems sending soa end from reload %d to xfrd: %s", - (int)nsd->pid, strerror(errno)); +#ifdef BIND8_STATS + mypid = getpid(); + if(!write_socket(nsd->xfrd_listener->fd, &mypid, sizeof(mypid))) { + log_msg(LOG_ERR, "problems sending reloadpid to xfrd: %s", + strerror(errno)); } +#endif /* try to reopen file */ if (nsd->file_rotation_ok) @@ -1004,6 +1211,10 @@ server_signal_mode(struct nsd *nsd) nsd->signal_hint_reload = 0; return NSD_RELOAD; } + else if(nsd->signal_hint_reload_hup) { + nsd->signal_hint_reload_hup = 0; + return NSD_RELOAD_REQ; + } else if(nsd->signal_hint_stats) { nsd->signal_hint_stats = 0; #ifdef BIND8_STATS @@ -1028,37 +1239,23 @@ server_main(struct nsd *nsd) region_type *server_region = region_create(xalloc, free); netio_type *netio = netio_create(server_region); netio_handler_type reload_listener; - netio_handler_type xfrd_listener; int reload_sockets[2] = {-1, -1}; struct timespec timeout_spec; - int fd; int status; pid_t child_pid; pid_t reload_pid = -1; - pid_t xfrd_pid = -1; sig_atomic_t mode; -#ifdef RATELIMIT - rrl_init((nsd->this_child - nsd->children)/sizeof(nsd->children[0])); -#endif - /* Ensure we are the main process */ assert(nsd->server_kind == NSD_SERVER_MAIN); - xfrd_listener.user_data = (struct ipc_handler_conn_data*)region_alloc( - server_region, sizeof(struct ipc_handler_conn_data)); - xfrd_listener.fd = -1; - ((struct ipc_handler_conn_data*)xfrd_listener.user_data)->nsd = nsd; - ((struct ipc_handler_conn_data*)xfrd_listener.user_data)->conn = - xfrd_tcp_create(server_region); - - /* Start the XFRD process */ - xfrd_pid = server_start_xfrd(nsd, &xfrd_listener); - netio_add_handler(netio, &xfrd_listener); + /* Add listener for the XFRD process */ + netio_add_handler(netio, nsd->xfrd_listener); /* Start the child processes that handle incoming queries */ - if (server_start_children(nsd, server_region, netio, &xfrd_listener.fd) != 0) { - send_children_quit(nsd); /* no wait required */ + if (server_start_children(nsd, server_region, netio, + &nsd->xfrd_listener->fd) != 0) { + send_children_quit(nsd); exit(1); } reload_listener.fd = -1; @@ -1087,27 +1284,34 @@ server_main(struct nsd *nsd) "server %d died unexpectedly with status %d, restarting", (int) child_pid, status); restart_child_servers(nsd, server_region, netio, - &xfrd_listener.fd); + &nsd->xfrd_listener->fd); } else if (child_pid == reload_pid) { - sig_atomic_t cmd = NSD_SOA_END; + sig_atomic_t cmd = NSD_RELOAD_DONE; +#ifdef BIND8_STATS + pid_t mypid; +#endif log_msg(LOG_WARNING, "Reload process %d failed with status %d, continuing with old database", (int) child_pid, status); reload_pid = -1; - if(reload_listener.fd > 0) close(reload_listener.fd); + if(reload_listener.fd != -1) close(reload_listener.fd); reload_listener.fd = -1; reload_listener.event_types = NETIO_EVENT_NONE; + task_process_sync(nsd->task[nsd->mytask]); /* inform xfrd reload attempt ended */ - if(!write_socket(xfrd_listener.fd, &cmd, sizeof(cmd))) { + if(!write_socket(nsd->xfrd_listener->fd, + &cmd, sizeof(cmd)) == -1) { log_msg(LOG_ERR, "problems " "sending SOAEND to xfrd: %s", strerror(errno)); } - } else if (child_pid == xfrd_pid) { - log_msg(LOG_WARNING, - "xfrd process %d failed with status %d, restarting ", - (int) child_pid, status); - xfrd_pid = server_start_xfrd(nsd, &xfrd_listener); +#ifdef BIND8_STATS + mypid = getpid(); + if(!write_socket(nsd->xfrd_listener->fd, &mypid, sizeof(mypid))) { + log_msg(LOG_ERR, "problems sending reloadpid to xfrd: %s", + strerror(errno)); + } +#endif } else { log_msg(LOG_WARNING, "Unknown child %d terminated with status %d", @@ -1135,18 +1339,30 @@ server_main(struct nsd *nsd) } break; + case NSD_RELOAD_REQ: { + sig_atomic_t cmd = NSD_RELOAD_REQ; + log_msg(LOG_WARNING, "SIGHUP received, reloading..."); + DEBUG(DEBUG_IPC,1, (LOG_INFO, + "main: ipc send reload_req to xfrd")); + if(!write_socket(nsd->xfrd_listener->fd, + &cmd, sizeof(cmd))) { + log_msg(LOG_ERR, "server_main: could not send " + "reload_req to xfrd: %s", strerror(errno)); + } + nsd->mode = NSD_RUN; + } break; case NSD_RELOAD: /* Continue to run nsd after reload */ nsd->mode = NSD_RUN; - + DEBUG(DEBUG_IPC,1, (LOG_INFO, "reloading...")); if (reload_pid != -1) { log_msg(LOG_WARNING, "Reload already in progress (pid = %d)", (int) reload_pid); break; } - log_msg(LOG_WARNING, "signal received, reloading..."); - + /* switch the mytask to keep track of who owns task*/ + nsd->mytask = 1 - nsd->mytask; if (socketpair(AF_UNIX, SOCK_STREAM, 0, reload_sockets) == -1) { log_msg(LOG_ERR, "reload failed on socketpair: %s", strerror(errno)); reload_pid = -1; @@ -1163,12 +1379,13 @@ server_main(struct nsd *nsd) /* CHILD */ close(reload_sockets[0]); server_reload(nsd, server_region, netio, - reload_sockets[1], &xfrd_listener.fd); + reload_sockets[1]); DEBUG(DEBUG_IPC,2, (LOG_INFO, "Reload exited to become new main")); close(reload_sockets[1]); DEBUG(DEBUG_IPC,2, (LOG_INFO, "Reload closed")); /* drop stale xfrd ipc data */ - ((struct ipc_handler_conn_data*)xfrd_listener.user_data) + ((struct ipc_handler_conn_data*)nsd-> + xfrd_listener->user_data) ->conn->is_reading = 0; reload_pid = -1; reload_listener.fd = -1; @@ -1196,7 +1413,8 @@ server_main(struct nsd *nsd) /* stop xfrd ipc writes in progress */ DEBUG(DEBUG_IPC,1, (LOG_INFO, "main: ipc send indication reload")); - if(!write_socket(xfrd_listener.fd, &cmd, sizeof(cmd))) { + if(!write_socket(nsd->xfrd_listener->fd, + &cmd, sizeof(cmd))) { log_msg(LOG_ERR, "server_main: could not send reload " "indication to xfrd: %s", strerror(errno)); } @@ -1216,19 +1434,23 @@ server_main(struct nsd *nsd) log_msg(LOG_ERR, "server_main: " "could not ack quit: %s", strerror(errno)); } +#ifdef BIND8_STATS + parent_send_stats(nsd, reload_listener.fd); +#endif /* BIND8_STATS */ close(reload_listener.fd); } + DEBUG(DEBUG_IPC,1, (LOG_INFO, "server_main: shutdown sequence")); /* only quit children after xfrd has acked */ - send_children_quit(nsd); /* no wait required */ + send_children_quit(nsd); - namedb_fd_close(nsd->db); +#if 0 /* OS collects memory pages */ region_destroy(server_region); +#endif server_shutdown(nsd); /* ENOTREACH */ break; case NSD_SHUTDOWN: - send_children_quit_and_wait(nsd); log_msg(LOG_WARNING, "signal received, shutting down..."); break; case NSD_REAP_CHILDREN: @@ -1242,20 +1464,24 @@ server_main(struct nsd *nsd) nsd->mode = NSD_RUN; break; default: - log_msg(LOG_WARNING, "NSD main server mode invalid: %d", nsd->mode); + log_msg(LOG_WARNING, "NSD main server mode invalid: %d", (int)nsd->mode); nsd->mode = NSD_RUN; break; } } - /* Truncate the pid file. */ - if ((fd = open(nsd->pidfile, O_WRONLY | O_TRUNC, 0644)) == -1) { - log_msg(LOG_ERR, "can not truncate the pid file %s: %s", nsd->pidfile, strerror(errno)); - } else { - close(fd); - } + /* close opened ports to avoid race with restart of nsd */ + server_close_all_sockets(nsd->udp, nsd->ifs); + server_close_all_sockets(nsd->tcp, nsd->ifs); +#ifdef HAVE_SSL + daemon_remote_close(nsd->rc); +#endif + send_children_quit_and_wait(nsd); + /* Unlink it if possible... */ unlinkpid(nsd->pidfile); + unlink(nsd->task[0]->fname); + unlink(nsd->task[1]->fname); if(reload_listener.fd != -1) { sig_atomic_t cmd = NSD_QUIT; @@ -1268,22 +1494,24 @@ server_main(struct nsd *nsd) fsync(reload_listener.fd); close(reload_listener.fd); } - if(xfrd_listener.fd != -1) { + if(nsd->xfrd_listener->fd != -1) { /* complete quit, stop xfrd */ sig_atomic_t cmd = NSD_QUIT; DEBUG(DEBUG_IPC,1, (LOG_INFO, "main: ipc send quit to xfrd")); - if(!write_socket(xfrd_listener.fd, &cmd, sizeof(cmd))) { + if(!write_socket(nsd->xfrd_listener->fd, &cmd, sizeof(cmd))) { log_msg(LOG_ERR, "server_main: could not send quit to xfrd: %s", strerror(errno)); } - fsync(xfrd_listener.fd); - close(xfrd_listener.fd); - (void)kill(xfrd_pid, SIGTERM); + fsync(nsd->xfrd_listener->fd); + close(nsd->xfrd_listener->fd); + (void)kill(nsd->pid, SIGTERM); } + xfrd_del_tempdir(nsd); - namedb_fd_close(nsd->db); +#if 0 /* OS collects memory pages */ region_destroy(server_region); +#endif server_shutdown(nsd); } @@ -1293,6 +1521,44 @@ server_process_query(struct nsd *nsd, struct query *query) return query_process(query, nsd); } +static query_state_type +server_process_query_udp(struct nsd *nsd, struct query *query) +{ +#ifdef RATELIMIT + if(query_process(query, nsd) != QUERY_DISCARDED) { + if(rrl_process_query(query)) + return rrl_slip(query); + else return QUERY_PROCESSED; + } + return QUERY_DISCARDED; +#else + return query_process(query, nsd); +#endif +} + +struct event_base* +nsd_child_event_base(void) +{ + struct event_base* base; +#ifdef USE_MINI_EVENT + static time_t secs; + static struct timeval now; + base = event_init(&secs, &now); +#else +# if defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP) + /* libev */ + base = (struct event_base *)ev_default_loop(EVFLAG_AUTO); +# else + /* libevent */ +# ifdef HAVE_EVENT_BASE_NEW + base = event_base_new(); +# else + base = event_init(); +# endif +# endif +#endif + return base; +} /* * Serve DNS requests. @@ -1302,11 +1568,19 @@ server_child(struct nsd *nsd) { size_t i; region_type *server_region = region_create(xalloc, free); - netio_type *netio = netio_create(server_region); - netio_handler_type *tcp_accept_handlers; + struct event_base* event_base = nsd_child_event_base(); query_type *udp_query; sig_atomic_t mode; + if(!event_base) { + log_msg(LOG_ERR, "nsd server could not create event base"); + exit(1); + } + +#ifdef RATELIMIT + rrl_init((nsd->this_child - nsd->children)/sizeof(nsd->children[0])); +#endif + assert(nsd->server_kind != NSD_SERVER_MAIN); DEBUG(DEBUG_IPC, 2, (LOG_INFO, "child process started")); @@ -1318,29 +1592,45 @@ server_child(struct nsd *nsd) } if (nsd->this_child && nsd->this_child->parent_fd != -1) { - netio_handler_type *handler; - - handler = (netio_handler_type *) region_alloc( - server_region, sizeof(netio_handler_type)); - handler->fd = nsd->this_child->parent_fd; - handler->timeout = NULL; - handler->user_data = (struct ipc_handler_conn_data*)region_alloc( + struct event *handler; + struct ipc_handler_conn_data* user_data = + (struct ipc_handler_conn_data*)region_alloc( server_region, sizeof(struct ipc_handler_conn_data)); - ((struct ipc_handler_conn_data*)handler->user_data)->nsd = nsd; - ((struct ipc_handler_conn_data*)handler->user_data)->conn = - xfrd_tcp_create(server_region); - handler->event_types = NETIO_EVENT_READ; - handler->event_handler = child_handle_parent_command; - netio_add_handler(netio, handler); + user_data->nsd = nsd; + user_data->conn = xfrd_tcp_create(server_region, QIOBUFSZ); + + handler = (struct event*) region_alloc( + server_region, sizeof(*handler)); + event_set(handler, nsd->this_child->parent_fd, EV_PERSIST| + EV_READ, child_handle_parent_command, user_data); + if(event_base_set(event_base, handler) != 0) + log_msg(LOG_ERR, "nsd ipcchild: event_base_set failed"); + if(event_add(handler, NULL) != 0) + log_msg(LOG_ERR, "nsd ipcchild: event_add failed"); } if (nsd->server_kind & NSD_SERVER_UDP) { +#if (defined(NONBLOCKING_IS_BROKEN) || !defined(HAVE_RECVMMSG)) udp_query = query_create(server_region, compressed_dname_offsets, compression_table_size); - +#else + udp_query = NULL; + memset(msgs, 0, sizeof(msgs)); + for (i = 0; i < NUM_RECV_PER_SELECT; i++) { + queries[i] = query_create(server_region, + compressed_dname_offsets, compression_table_size); + query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0); + iovecs[i].iov_base = buffer_begin(queries[i]->packet); + iovecs[i].iov_len = buffer_remaining(queries[i]->packet);; + msgs[i].msg_hdr.msg_iov = &iovecs[i]; + msgs[i].msg_hdr.msg_iovlen = 1; + msgs[i].msg_hdr.msg_name = &queries[i]->addr; + msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen; + } +#endif for (i = 0; i < nsd->ifs; ++i) { struct udp_handler_data *data; - netio_handler_type *handler; + struct event *handler; data = (struct udp_handler_data *) region_alloc( server_region, @@ -1349,14 +1639,14 @@ server_child(struct nsd *nsd) data->nsd = nsd; data->socket = &nsd->udp[i]; - handler = (netio_handler_type *) region_alloc( - server_region, sizeof(netio_handler_type)); - handler->fd = nsd->udp[i].s; - handler->timeout = NULL; - handler->user_data = data; - handler->event_types = NETIO_EVENT_READ; - handler->event_handler = handle_udp; - netio_add_handler(netio, handler); + handler = (struct event*) region_alloc( + server_region, sizeof(*handler)); + event_set(handler, nsd->udp[i].s, EV_PERSIST|EV_READ, + handle_udp, data); + if(event_base_set(event_base, handler) != 0) + log_msg(LOG_ERR, "nsd udp: event_base_set failed"); + if(event_add(handler, NULL) != 0) + log_msg(LOG_ERR, "nsd udp: event_add failed"); } } @@ -1365,30 +1655,25 @@ server_child(struct nsd *nsd) * and disable them based on the current number of active TCP * connections. */ - tcp_accept_handlers = (netio_handler_type *) region_alloc( - server_region, nsd->ifs * sizeof(netio_handler_type)); + tcp_accept_handler_count = nsd->ifs; + tcp_accept_handlers = (struct tcp_accept_handler_data*) region_alloc( + server_region, nsd->ifs * sizeof(*tcp_accept_handlers)); if (nsd->server_kind & NSD_SERVER_TCP) { for (i = 0; i < nsd->ifs; ++i) { - struct tcp_accept_handler_data *data; - netio_handler_type *handler; - - data = (struct tcp_accept_handler_data *) region_alloc( - server_region, - sizeof(struct tcp_accept_handler_data)); + struct event *handler = &tcp_accept_handlers[i].event; + struct tcp_accept_handler_data* data = + &tcp_accept_handlers[i]; data->nsd = nsd; data->socket = &nsd->tcp[i]; - data->tcp_accept_handler_count = nsd->ifs; - data->tcp_accept_handlers = tcp_accept_handlers; - - handler = &tcp_accept_handlers[i]; - handler->fd = nsd->tcp[i].s; - handler->timeout = NULL; - handler->user_data = data; - handler->event_types = NETIO_EVENT_READ | NETIO_EVENT_ACCEPT; - handler->event_handler = handle_tcp_accept; - netio_add_handler(netio, handler); + event_set(handler, nsd->tcp[i].s, EV_PERSIST|EV_READ, + handle_tcp_accept, data); + if(event_base_set(event_base, handler) != 0) + log_msg(LOG_ERR, "nsd tcp: event_base_set failed"); + if(event_add(handler, NULL) != 0) + log_msg(LOG_ERR, "nsd tcp: event_add failed"); + data->event_added = 1; } - } + } else tcp_accept_handler_count = 0; /* The main loop... */ while ((mode = nsd->mode) != NSD_QUIT) { @@ -1425,9 +1710,9 @@ server_child(struct nsd *nsd) } else if(mode == NSD_RUN) { /* Wait for a query... */ - if (netio_dispatch(netio, NULL, NULL) == -1) { + if(event_base_loop(event_base, EVLOOP_ONCE) == -1) { if (errno != EINTR) { - log_msg(LOG_ERR, "netio_dispatch failed: %s", strerror(errno)); + log_msg(LOG_ERR, "dispatch failed: %s", strerror(errno)); break; } } @@ -1435,7 +1720,7 @@ server_child(struct nsd *nsd) /* ignore here, quit */ } else { log_msg(LOG_ERR, "mode bad value %d, back to service.", - mode); + (int)mode); nsd->mode = NSD_RUN; } } @@ -1444,96 +1729,208 @@ server_child(struct nsd *nsd) bind8_stats(nsd); #endif /* BIND8_STATS */ - namedb_fd_close(nsd->db); +#if 0 /* OS collects memory pages */ + event_base_free(event_base); region_destroy(server_region); - server_shutdown(nsd); -} - -static query_state_type -server_process_query_udp(struct nsd *nsd, struct query *query) -{ -#ifdef RATELIMIT - if(query_process(query, nsd) != QUERY_DISCARDED) { - if(rrl_process_query(query)) - return rrl_slip(query); - else return QUERY_PROCESSED; - } - return QUERY_DISCARDED; -#else - return query_process(query, nsd); #endif + server_shutdown(nsd); } +#if defined(HAVE_SENDMMSG) && !defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG) static void -handle_udp(netio_type *ATTR_UNUSED(netio), - netio_handler_type *handler, - netio_event_types_type event_types) +handle_udp(int fd, short event, void* arg) { - struct udp_handler_data *data - = (struct udp_handler_data *) handler->user_data; - int received, sent; - struct query *q = data->query; + struct udp_handler_data *data = (struct udp_handler_data *) arg; + int received, sent, recvcount, i; + struct query *q; - if (!(event_types & NETIO_EVENT_READ)) { + if (!(event & EV_READ)) { return; } + recvcount = recvmmsg(fd, msgs, NUM_RECV_PER_SELECT, 0, NULL); + /* this printf strangely gave a performance increase on Linux */ + /* printf("recvcount %d \n", recvcount); */ + if (recvcount == -1) { + if (errno != EAGAIN && errno != EINTR) { + log_msg(LOG_ERR, "recvmmsg failed: %s", strerror(errno)); + STATUP(data->nsd, rxerr); + } + /* Simply no data available */ + return; + } + for (i = 0; i < recvcount; i++) { + loopstart: + received = msgs[i].msg_len; + q = queries[i]; + if (received == -1) { + log_msg(LOG_ERR, "recvmmsg %d failed %s", i, strerror( + msgs[i].msg_hdr.msg_flags)); + STATUP(data->nsd, rxerr); + query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0); + iovecs[i].iov_len = buffer_remaining(q->packet); + goto swap_drop; + } - /* Account... */ + /* Account... */ + if (data->socket->addr->ai_family == AF_INET) { + STATUP(data->nsd, qudp); + } else if (data->socket->addr->ai_family == AF_INET6) { + STATUP(data->nsd, qudp6); + } + + buffer_skip(q->packet, received); + buffer_flip(q->packet); + + /* Process and answer the query... */ + if (server_process_query_udp(data->nsd, q) != QUERY_DISCARDED) { + if (RCODE(q->packet) == RCODE_OK && !AA(q->packet)) { + STATUP(data->nsd, nona); + } + + /* Add EDNS0 and TSIG info if necessary. */ + query_add_optional(q, data->nsd); + + buffer_flip(q->packet); + iovecs[i].iov_len = buffer_remaining(q->packet); #ifdef BIND8_STATS - if (data->socket->addr->ai_family == AF_INET) { - STATUP(data->nsd, qudp); - } else if (data->socket->addr->ai_family == AF_INET6) { - STATUP(data->nsd, qudp6); + /* Account the rcode & TC... */ + STATUP2(data->nsd, rcode, RCODE(q->packet)); + if (TC(q->packet)) + STATUP(data->nsd, truncated); +#endif /* BIND8_STATS */ + } else { + query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0); + iovecs[i].iov_len = buffer_remaining(q->packet); + swap_drop: + STATUP(data->nsd, dropped); + if(i != recvcount-1) { + /* swap with last and decrease recvcount */ + struct mmsghdr mtmp = msgs[i]; + struct iovec iotmp = iovecs[i]; + recvcount--; + msgs[i] = msgs[recvcount]; + iovecs[i] = iovecs[recvcount]; + queries[i] = queries[recvcount]; + msgs[recvcount] = mtmp; + iovecs[recvcount] = iotmp; + queries[recvcount] = q; + msgs[i].msg_hdr.msg_iov = &iovecs[i]; + msgs[recvcount].msg_hdr.msg_iov = &iovecs[recvcount]; + goto loopstart; + } else { recvcount --; } + } } -#endif - /* Initialize the query... */ - query_reset(q, UDP_MAX_MESSAGE_LEN, 0); + /* send until all are sent */ + i = 0; + while(i<recvcount) { + sent = sendmmsg(fd, &msgs[i], recvcount-i, 0); + if(sent == -1) { + log_msg(LOG_ERR, "sendmmsg failed: %s", strerror(errno)); +#ifdef BIND8_STATS + data->nsd->st.txerr += recvcount-i; +#endif /* BIND8_STATS */ + break; + } + i += sent; + } + for(i=0; i<recvcount; i++) { + query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0); + iovecs[i].iov_len = buffer_remaining(queries[i]->packet); + } +} - received = recvfrom(handler->fd, - buffer_begin(q->packet), - buffer_remaining(q->packet), - 0, - (struct sockaddr *)&q->addr, - &q->addrlen); - if (received == -1) { +#else /* defined(HAVE_SENDMMSG) && !defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG) */ + +static void +handle_udp(int fd, short event, void* arg) +{ + struct udp_handler_data *data = (struct udp_handler_data *) arg; + int received, sent; +#ifndef NONBLOCKING_IS_BROKEN +#ifdef HAVE_RECVMMSG + int recvcount; +#endif /* HAVE_RECVMMSG */ + int i; +#endif /* NONBLOCKING_IS_BROKEN */ + struct query *q; +#if (defined(NONBLOCKING_IS_BROKEN) || !defined(HAVE_RECVMMSG)) + q = data->query; +#endif + + if (!(event & EV_READ)) { + return; + } +#ifndef NONBLOCKING_IS_BROKEN +#ifdef HAVE_RECVMMSG + recvcount = recvmmsg(fd, msgs, NUM_RECV_PER_SELECT, 0, NULL); + /* this printf strangely gave a performance increase on Linux */ + /* printf("recvcount %d \n", recvcount); */ + if (recvcount == -1) { if (errno != EAGAIN && errno != EINTR) { - log_msg(LOG_ERR, "recvfrom failed: %s", strerror(errno)); + log_msg(LOG_ERR, "recvmmsg failed: %s", strerror(errno)); STATUP(data->nsd, rxerr); - /* No zone statup */ } - } else { + /* Simply no data available */ + return; + } + for (i = 0; i < recvcount; i++) { + received = msgs[i].msg_len; + msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen; + if (received == -1) { + log_msg(LOG_ERR, "recvmmsg failed"); + STATUP(data->nsd, rxerr); + /* the error can be found in msgs[i].msg_hdr.msg_flags */ + query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0); + continue; + } + q = queries[i]; +#else + for(i=0; i<NUM_RECV_PER_SELECT; i++) { +#endif /* HAVE_RECVMMSG */ +#endif /* NONBLOCKING_IS_BROKEN */ + +#if (defined(NONBLOCKING_IS_BROKEN) || !defined(HAVE_RECVMMSG)) + /* Initialize the query... */ + query_reset(q, UDP_MAX_MESSAGE_LEN, 0); + + received = recvfrom(fd, + buffer_begin(q->packet), + buffer_remaining(q->packet), + 0, + (struct sockaddr *)&q->addr, + &q->addrlen); + if (received == -1) { + if (errno != EAGAIN && errno != EINTR) { + log_msg(LOG_ERR, "recvfrom failed: %s", strerror(errno)); + STATUP(data->nsd, rxerr); + } + return; + } +#endif /* NONBLOCKING_IS_BROKEN || !HAVE_RECVMMSG */ + + /* Account... */ + if (data->socket->addr->ai_family == AF_INET) { + STATUP(data->nsd, qudp); + } else if (data->socket->addr->ai_family == AF_INET6) { + STATUP(data->nsd, qudp6); + } + buffer_skip(q->packet, received); buffer_flip(q->packet); /* Process and answer the query... */ if (server_process_query_udp(data->nsd, q) != QUERY_DISCARDED) { -#ifdef BIND8_STATS if (RCODE(q->packet) == RCODE_OK && !AA(q->packet)) { STATUP(data->nsd, nona); -# ifdef USE_ZONE_STATS - if (q->zone) - ZTATUP(q->zone, nona); -# endif } -# ifdef USE_ZONE_STATS - if (q->zone) { - if (data->socket->addr->ai_family == AF_INET) { - ZTATUP(q->zone, qudp); - } else if (data->socket->addr->ai_family == AF_INET6) { - ZTATUP(q->zone, qudp6); - } - } -# endif -#endif - /* Add EDNS0 and TSIG info if necessary. */ query_add_optional(q, data->nsd); buffer_flip(q->packet); - sent = sendto(handler->fd, + sent = sendto(fd, buffer_begin(q->packet), buffer_remaining(q->packet), 0, @@ -1542,62 +1939,43 @@ handle_udp(netio_type *ATTR_UNUSED(netio), if (sent == -1) { log_msg(LOG_ERR, "sendto failed: %s", strerror(errno)); STATUP(data->nsd, txerr); - -#ifdef USE_ZONE_STATS - if (q->zone) - ZTATUP(q->zone, txerr); -#endif } else if ((size_t) sent != buffer_remaining(q->packet)) { log_msg(LOG_ERR, "sent %d in place of %d bytes", sent, (int) buffer_remaining(q->packet)); -#ifdef BIND8_STATS } else { +#ifdef BIND8_STATS /* Account the rcode & TC... */ STATUP2(data->nsd, rcode, RCODE(q->packet)); -# ifdef USE_ZONE_STATS - if (q->zone) - ZTATUP2(q->zone, rcode, RCODE(q->packet)); -# endif - if (TC(q->packet)) { + if (TC(q->packet)) STATUP(data->nsd, truncated); -# ifdef USE_ZONE_STATS - if (q->zone) - ZTATUP(q->zone, truncated); -# endif - } #endif /* BIND8_STATS */ } -#ifdef BIND8_STATS } else { STATUP(data->nsd, dropped); -# ifdef USE_ZONE_STATS - if (q->zone) { - ZTATUP(q->zone, dropped); - } -# endif -#endif } +#ifndef NONBLOCKING_IS_BROKEN +#ifdef HAVE_RECVMMSG + query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0); +#endif } +#endif } +#endif /* defined(HAVE_SENDMMSG) && !defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG) */ static void -cleanup_tcp_handler(netio_type *netio, netio_handler_type *handler) +cleanup_tcp_handler(struct tcp_handler_data* data) { - struct tcp_handler_data *data - = (struct tcp_handler_data *) handler->user_data; - netio_remove_handler(netio, handler); - close(handler->fd); - slowaccept = 0; + event_del(&data->event); + close(data->event.ev_fd); /* * Enable the TCP accept handlers when the current number of * TCP connections is about to drop below the maximum number * of TCP connections. */ - if (data->nsd->current_tcp_count == data->nsd->maximum_tcp_count) { - configure_handler_event_types(data->tcp_accept_handler_count, - data->tcp_accept_handlers, - NETIO_EVENT_READ); + if (slowaccept || data->nsd->current_tcp_count == data->nsd->maximum_tcp_count) { + configure_handler_event_types(EV_READ|EV_PERSIST); + slowaccept = 0; } --data->nsd->current_tcp_count; assert(data->nsd->current_tcp_count >= 0); @@ -1606,28 +1984,27 @@ cleanup_tcp_handler(netio_type *netio, netio_handler_type *handler) } static void -handle_tcp_reading(netio_type *netio, - netio_handler_type *handler, - netio_event_types_type event_types) +handle_tcp_reading(int fd, short event, void* arg) { - struct tcp_handler_data *data - = (struct tcp_handler_data *) handler->user_data; + struct tcp_handler_data *data = (struct tcp_handler_data *) arg; ssize_t received; + struct event_base* ev_base; + struct timeval timeout; - if (event_types & NETIO_EVENT_TIMEOUT) { + if ((event & EV_TIMEOUT)) { /* Connection timed out. */ - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } if (data->nsd->tcp_query_count > 0 && data->query_count >= data->nsd->tcp_query_count) { /* No more queries allowed on this tcp connection. */ - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } - assert(event_types & NETIO_EVENT_READ); + assert((event & EV_READ)); if (data->bytes_transmitted == 0) { query_reset(data->query, TCP_MAX_MESSAGE_LEN, 1); @@ -1637,7 +2014,7 @@ handle_tcp_reading(netio_type *netio, * Check if we received the leading packet length bytes yet. */ if (data->bytes_transmitted < sizeof(uint16_t)) { - received = read(handler->fd, + received = read(fd, (char *) &data->query->tcplen + data->bytes_transmitted, sizeof(uint16_t) - data->bytes_transmitted); @@ -1649,16 +2026,18 @@ handle_tcp_reading(netio_type *netio, */ return; } else { + char buf[48]; + addr2str(&data->query->addr, buf, sizeof(buf)); #ifdef ECONNRESET if (verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ - log_msg(LOG_ERR, "failed reading from tcp: %s", strerror(errno)); - cleanup_tcp_handler(netio, handler); + log_msg(LOG_ERR, "failed reading from %s tcp: %s", buf, strerror(errno)); + cleanup_tcp_handler(data); return; } } else if (received == 0) { /* EOF */ - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } @@ -1685,13 +2064,13 @@ handle_tcp_reading(netio_type *netio, */ if (data->query->tcplen < QHEADERSZ + 1 + sizeof(uint16_t) + sizeof(uint16_t)) { VERBOSITY(2, (LOG_WARNING, "packet too small, dropping tcp connection")); - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } if (data->query->tcplen > data->query->maxlen) { VERBOSITY(2, (LOG_WARNING, "insufficient tcp buffer, dropping connection")); - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } @@ -1701,7 +2080,7 @@ handle_tcp_reading(netio_type *netio, assert(buffer_remaining(data->query->packet) > 0); /* Read the (remaining) query data. */ - received = read(handler->fd, + received = read(fd, buffer_current(data->query->packet), buffer_remaining(data->query->packet)); if (received == -1) { @@ -1712,16 +2091,18 @@ handle_tcp_reading(netio_type *netio, */ return; } else { + char buf[48]; + addr2str(&data->query->addr, buf, sizeof(buf)); #ifdef ECONNRESET if (verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ - log_msg(LOG_ERR, "failed reading from tcp: %s", strerror(errno)); - cleanup_tcp_handler(netio, handler); + log_msg(LOG_ERR, "failed reading from %s tcp: %s", buf, strerror(errno)); + cleanup_tcp_handler(data); return; } } else if (received == 0) { /* EOF */ - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } @@ -1738,17 +2119,15 @@ handle_tcp_reading(netio_type *netio, assert(buffer_position(data->query->packet) == data->query->tcplen); /* Account... */ -#ifdef BIND8_STATS -# ifndef INET6 - STATUP(data->nsd, ctcp); -# else +#ifndef INET6 + STATUP(data->nsd, ctcp); +#else if (data->query->addr.ss_family == AF_INET) { STATUP(data->nsd, ctcp); } else if (data->query->addr.ss_family == AF_INET6) { STATUP(data->nsd, ctcp6); } -# endif -#endif /* BIND8_STATS */ +#endif /* We have a complete query, process it. */ @@ -1760,42 +2139,16 @@ handle_tcp_reading(netio_type *netio, if (data->query_state == QUERY_DISCARDED) { /* Drop the packet and the entire connection... */ STATUP(data->nsd, dropped); -#if defined(BIND8_STATS) && defined(USE_ZONE_STATS) - if (data->query->zone) { - ZTATUP(data->query->zone, dropped); - } -#endif - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } -#ifdef BIND8_STATS if (RCODE(data->query->packet) == RCODE_OK && !AA(data->query->packet)) { STATUP(data->nsd, nona); -# ifdef USE_ZONE_STATS - if (data->query->zone) - ZTATUP(data->query->zone, nona); -# endif } -# ifdef USE_ZONE_STATS - if (data->query->zone) { -# ifndef INET6 - ZTATUP(data->query->zone, ctcp); -# else - if (data->query->addr.ss_family == AF_INET) { - ZTATUP(data->query->zone, ctcp); - } else if (data->query->addr.ss_family == AF_INET6) { - ZTATUP(data->query->zone, ctcp6); - } -# endif - } -# endif /* USE_ZONE_STATS */ - -#endif /* BIND8_STATS */ - query_add_optional(data->query, data->nsd); /* Switch to the tcp write handler. */ @@ -1803,31 +2156,37 @@ handle_tcp_reading(netio_type *netio, data->query->tcplen = buffer_remaining(data->query->packet); data->bytes_transmitted = 0; - handler->timeout->tv_sec = data->nsd->tcp_timeout; - handler->timeout->tv_nsec = 0L; - timespec_add(handler->timeout, netio_current_time(netio)); - - handler->event_types = NETIO_EVENT_WRITE | NETIO_EVENT_TIMEOUT; - handler->event_handler = handle_tcp_writing; + timeout.tv_sec = data->nsd->tcp_timeout; + timeout.tv_usec = 0L; + + ev_base = data->event.ev_base; + event_del(&data->event); + event_set(&data->event, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT, + handle_tcp_writing, data); + if(event_base_set(ev_base, &data->event) != 0) + log_msg(LOG_ERR, "event base set tcpr failed"); + if(event_add(&data->event, &timeout) != 0) + log_msg(LOG_ERR, "event add tcpr failed"); + /* see if we can write the answer right away(usually so,EAGAIN ifnot)*/ + handle_tcp_writing(fd, EV_WRITE, data); } static void -handle_tcp_writing(netio_type *netio, - netio_handler_type *handler, - netio_event_types_type event_types) +handle_tcp_writing(int fd, short event, void* arg) { - struct tcp_handler_data *data - = (struct tcp_handler_data *) handler->user_data; + struct tcp_handler_data *data = (struct tcp_handler_data *) arg; ssize_t sent; struct query *q = data->query; + struct timeval timeout; + struct event_base* ev_base; - if (event_types & NETIO_EVENT_TIMEOUT) { + if ((event & EV_TIMEOUT)) { /* Connection timed out. */ - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } - assert(event_types & NETIO_EVENT_WRITE); + assert((event & EV_WRITE)); if (data->bytes_transmitted < sizeof(q->tcplen)) { /* Writing the response packet length. */ @@ -1838,9 +2197,9 @@ handle_tcp_writing(netio_type *netio, iov[0].iov_len = sizeof(n_tcplen) - data->bytes_transmitted; iov[1].iov_base = buffer_begin(q->packet); iov[1].iov_len = buffer_limit(q->packet); - sent = writev(handler->fd, iov, 2); + sent = writev(fd, iov, 2); #else /* HAVE_WRITEV */ - sent = write(handler->fd, + sent = write(fd, (const char *) &n_tcplen + data->bytes_transmitted, sizeof(n_tcplen) - data->bytes_transmitted); #endif /* HAVE_WRITEV */ @@ -1856,10 +2215,10 @@ handle_tcp_writing(netio_type *netio, if(verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ #ifdef EPIPE - if(verbosity >= 2 || errno != EPIPE) + if(verbosity >= 2 || errno != EPIPE) #endif /* EPIPE 'broken pipe' */ - log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); - cleanup_tcp_handler(netio, handler); + log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); + cleanup_tcp_handler(data); return; } } @@ -1878,9 +2237,9 @@ handle_tcp_writing(netio_type *netio, /* handle potential 'packet done' code */ goto packet_could_be_done; #endif - } - - sent = write(handler->fd, + } + + sent = write(fd, buffer_current(q->packet), buffer_remaining(q->packet)); if (sent == -1) { @@ -1895,10 +2254,10 @@ handle_tcp_writing(netio_type *netio, if(verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ #ifdef EPIPE - if(verbosity >= 2 || errno != EPIPE) + if(verbosity >= 2 || errno != EPIPE) #endif /* EPIPE 'broken pipe' */ log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); - cleanup_tcp_handler(netio, handler); + cleanup_tcp_handler(data); return; } } @@ -1930,9 +2289,16 @@ handle_tcp_writing(netio_type *netio, q->tcplen = buffer_remaining(q->packet); data->bytes_transmitted = 0; /* Reset timeout. */ - handler->timeout->tv_sec = data->nsd->tcp_timeout; - handler->timeout->tv_nsec = 0; - timespec_add(handler->timeout, netio_current_time(netio)); + timeout.tv_sec = data->nsd->tcp_timeout; + timeout.tv_usec = 0L; + ev_base = data->event.ev_base; + event_del(&data->event); + event_set(&data->event, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT, + handle_tcp_writing, data); + if(event_base_set(ev_base, &data->event) != 0) + log_msg(LOG_ERR, "event base set tcpw failed"); + if(event_add(&data->event, &timeout) != 0) + log_msg(LOG_ERR, "event add tcpw failed"); /* * Write data if/when the socket is writable @@ -1949,44 +2315,56 @@ handle_tcp_writing(netio_type *netio, if (data->nsd->tcp_query_count > 0 && data->query_count >= data->nsd->tcp_query_count) { - (void) shutdown(handler->fd, SHUT_WR); + (void) shutdown(fd, SHUT_WR); } data->bytes_transmitted = 0; - handler->timeout->tv_sec = data->nsd->tcp_timeout; - handler->timeout->tv_nsec = 0; - timespec_add(handler->timeout, netio_current_time(netio)); - - handler->event_types = NETIO_EVENT_READ | NETIO_EVENT_TIMEOUT; - handler->event_handler = handle_tcp_reading; + timeout.tv_sec = data->nsd->tcp_timeout; + timeout.tv_usec = 0L; + ev_base = data->event.ev_base; + event_del(&data->event); + event_set(&data->event, fd, EV_PERSIST | EV_READ | EV_TIMEOUT, + handle_tcp_reading, data); + if(event_base_set(ev_base, &data->event) != 0) + log_msg(LOG_ERR, "event base set tcpw failed"); + if(event_add(&data->event, &timeout) != 0) + log_msg(LOG_ERR, "event add tcpw failed"); } +static void +handle_slowaccept_timeout(int ATTR_UNUSED(fd), short ATTR_UNUSED(event), + void* ATTR_UNUSED(arg)) +{ + if(slowaccept) { + configure_handler_event_types(EV_PERSIST | EV_READ); + slowaccept = 0; + } +} + /* * Handle an incoming TCP connection. The connection is accepted and - * a new TCP reader event handler is added to NETIO. The TCP handler + * a new TCP reader event handler is added. The TCP handler * is responsible for cleanup when the connection is closed. */ static void -handle_tcp_accept(netio_type *netio, - netio_handler_type *handler, - netio_event_types_type event_types) +handle_tcp_accept(int fd, short event, void* arg) { struct tcp_accept_handler_data *data - = (struct tcp_accept_handler_data *) handler->user_data; + = (struct tcp_accept_handler_data *) arg; int s; struct tcp_handler_data *tcp_data; region_type *tcp_region; - netio_handler_type *tcp_handler; #ifdef INET6 struct sockaddr_storage addr; #else struct sockaddr_in addr; #endif socklen_t addrlen; + struct timeval timeout; - if (!(event_types & NETIO_EVENT_READ)) { + if (!(event & EV_READ)) { return; } @@ -1996,7 +2374,7 @@ handle_tcp_accept(netio_type *netio, /* Accept it... */ addrlen = sizeof(addr); - s = accept(handler->fd, (struct sockaddr *) &addr, &addrlen); + s = accept(fd, (struct sockaddr *) &addr, &addrlen); if (s == -1) { /** * EMFILE and ENFILE is a signal that the limit of open @@ -2006,9 +2384,16 @@ handle_tcp_accept(netio_type *netio, */ if (errno == EMFILE || errno == ENFILE) { if (!slowaccept) { - slowaccept_timeout.tv_sec = NETIO_SLOW_ACCEPT_TIMEOUT; - slowaccept_timeout.tv_nsec = 0L; - timespec_add(&slowaccept_timeout, netio_current_time(netio)); + /* disable accept events */ + struct timeval tv; + configure_handler_event_types(0); + tv.tv_sec = SLOW_ACCEPT_TIMEOUT; + tv.tv_usec = 0L; + event_set(&slowaccept_event, -1, EV_TIMEOUT, + handle_slowaccept_timeout, NULL); + (void)event_base_set(data->event.ev_base, + &slowaccept_event); + (void)event_add(&slowaccept_event, &tv); slowaccept = 1; /* We don't want to spam the logs here */ } @@ -2045,28 +2430,20 @@ handle_tcp_accept(netio_type *netio, tcp_data->nsd = data->nsd; tcp_data->query_count = 0; - tcp_data->tcp_accept_handler_count = data->tcp_accept_handler_count; - tcp_data->tcp_accept_handlers = data->tcp_accept_handlers; - tcp_data->query_state = QUERY_PROCESSED; tcp_data->bytes_transmitted = 0; memcpy(&tcp_data->query->addr, &addr, addrlen); tcp_data->query->addrlen = addrlen; - tcp_handler = (netio_handler_type *) region_alloc( - tcp_region, sizeof(netio_handler_type)); - tcp_handler->fd = s; - tcp_handler->timeout = (struct timespec *) region_alloc( - tcp_region, sizeof(struct timespec)); - tcp_handler->timeout->tv_sec = data->nsd->tcp_timeout; - tcp_handler->timeout->tv_nsec = 0L; - timespec_add(tcp_handler->timeout, netio_current_time(netio)); + timeout.tv_sec = data->nsd->tcp_timeout; + timeout.tv_usec = 0; - tcp_handler->user_data = tcp_data; - tcp_handler->event_types = NETIO_EVENT_READ | NETIO_EVENT_TIMEOUT; - tcp_handler->event_handler = handle_tcp_reading; - - netio_add_handler(netio, tcp_handler); + event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT, + handle_tcp_reading, tcp_data); + if(event_base_set(data->event.ev_base, &tcp_data->event) != 0) + log_msg(LOG_ERR, "cannot set tcp event base"); + if(event_add(&tcp_data->event, &timeout) != 0) + log_msg(LOG_ERR, "cannot set tcp event base"); /* * Keep track of the total number of TCP handlers installed so @@ -2075,9 +2452,7 @@ handle_tcp_accept(netio_type *netio, */ ++data->nsd->current_tcp_count; if (data->nsd->current_tcp_count == data->nsd->maximum_tcp_count) { - configure_handler_event_types(data->tcp_accept_handler_count, - data->tcp_accept_handlers, - NETIO_EVENT_NONE); + configure_handler_event_types(0); } } @@ -2098,7 +2473,6 @@ send_children_command(struct nsd* nsd, sig_atomic_t command, int timeout) (int) nsd->children[i].pid, strerror(errno)); } else if (timeout > 0) { - /* wait for reply */ (void)block_read(NULL, nsd->children[i].child_fd, &command, sizeof(command), timeout); @@ -2113,6 +2487,7 @@ send_children_command(struct nsd* nsd, sig_atomic_t command, int timeout) static void send_children_quit(struct nsd* nsd) { + DEBUG(DEBUG_IPC, 1, (LOG_INFO, "send children quit")); send_children_command(nsd, NSD_QUIT, 0); } @@ -2138,15 +2513,31 @@ set_children_stats(struct nsd* nsd) #endif /* BIND8_STATS */ static void -configure_handler_event_types(size_t count, - netio_handler_type *handlers, - netio_event_types_type event_types) +configure_handler_event_types(short event_types) { size_t i; - assert(handlers); - - for (i = 0; i < count; ++i) { - handlers[i].event_types = event_types; + for (i = 0; i < tcp_accept_handler_count; ++i) { + struct event* handler = &tcp_accept_handlers[i].event; + if(event_types) { + /* reassign */ + int fd = handler->ev_fd; + struct event_base* base = handler->ev_base; + if(tcp_accept_handlers[i].event_added) + event_del(handler); + event_set(handler, fd, event_types, + handle_tcp_accept, &tcp_accept_handlers[i]); + if(event_base_set(base, handler) != 0) + log_msg(LOG_ERR, "conhand: cannot event_base"); + if(event_add(handler, NULL) != 0) + log_msg(LOG_ERR, "conhand: cannot event_add"); + tcp_accept_handlers[i].event_added = 1; + } else { + /* remove */ + if(tcp_accept_handlers[i].event_added) { + event_del(handler); + tcp_accept_handlers[i].event_added = 0; + } + } } } diff --git a/usr.sbin/nsd/util.c b/usr.sbin/nsd/util.c index bfd19272b9f..bb016544b52 100644 --- a/usr.sbin/nsd/util.c +++ b/usr.sbin/nsd/util.c @@ -1,7 +1,7 @@ /* * util.c -- set of various support routines. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -861,6 +861,18 @@ qid_generate(void) #endif } +int +random_generate(int max) +{ +#ifdef HAVE_ARC4RANDOM_UNIFORM + return (int) arc4random_uniform(max); +#elif HAVE_ARC4RANDOM + return (int) (arc4random() % max); +#else + return (int) ((unsigned)random() % max); +#endif +} + void cleanup_region(void *data) { @@ -903,151 +915,102 @@ print_rr(FILE *out, const dname_type *owner = domain_dname(record->owner); const dname_type *owner_origin = dname_origin(region, owner); - int owner_changed - = (!state->previous_owner - || dname_compare(state->previous_owner, owner) != 0); - if (owner_changed) { - int origin_changed = (!state->previous_owner_origin - || dname_compare( - state->previous_owner_origin, - owner_origin) != 0); - if (origin_changed) { - buffer_printf( - output, - "$ORIGIN %s\n", - dname_to_string(owner_origin, NULL)); - } - - set_previous_owner(state, owner); - buffer_printf(output, - "%s", - dname_to_string(owner, - state->previous_owner_origin)); - } + if (state) { + if (!state->previous_owner + || dname_compare(state->previous_owner, owner) != 0) { + int origin_changed = (!state->previous_owner_origin + || dname_compare(state->previous_owner_origin, + owner_origin) != 0); + if (origin_changed) { + buffer_printf(output, "$ORIGIN %s\n", + dname_to_string(owner_origin, NULL)); + } + + set_previous_owner(state, owner); + buffer_printf(output, "%s", + dname_to_string(owner, + state->previous_owner_origin)); + } + } else { + buffer_printf(output, "%s", dname_to_string(owner, NULL)); + } - buffer_printf(output, - "\t%lu\t%s\t%s", - (unsigned long) record->ttl, - rrclass_to_string(record->klass), - rrtype_to_string(record->type)); - - result = print_rdata(output, descriptor, record); - if (!result) { - /* - * Some RDATA failed to print, so print the record's - * RDATA in unknown format. - */ - result = rdata_atoms_to_unknown_string(output, - descriptor, - record->rdata_count, - record->rdatas); - } + buffer_printf(output, "\t%lu\t%s\t%s", + (unsigned long) record->ttl, + rrclass_to_string(record->klass), + rrtype_to_string(record->type)); - if (result) { - buffer_printf(output, "\n"); - buffer_flip(output); - (void)write_data(out, buffer_current(output), buffer_remaining(output)); -/* fflush(out); */ - } + result = print_rdata(output, descriptor, record); + if (!result) { + /* + * Some RDATA failed to print, so print the record's + * RDATA in unknown format. + */ + result = rdata_atoms_to_unknown_string(output, + descriptor, record->rdata_count, record->rdatas); + } + + if (result) { + buffer_printf(output, "\n"); + buffer_flip(output); + result = write_data(out, buffer_current(output), + buffer_remaining(output)); + } region_destroy(region); - return result; + return result; } const char* rcode2str(int rc) { - switch(rc) - { - case RCODE_OK: - return "NO ERROR"; - case RCODE_FORMAT: - return "FORMAT ERROR"; - case RCODE_SERVFAIL: - return "SERV FAIL"; - case RCODE_NXDOMAIN: - return "NAME ERROR"; - case RCODE_IMPL: - return "NOT IMPL"; - case RCODE_REFUSE: - return "REFUSED"; - case RCODE_YXDOMAIN: - return "YXDOMAIN"; - case RCODE_YXRRSET: - return "YXRRSET"; - case RCODE_NXRRSET: - return "NXRRSET"; - case RCODE_NOTAUTH: - return "SERVER NOT AUTHORITATIVE FOR ZONE"; - case RCODE_NOTZONE: - return "NOTZONE"; - default: - return "UNKNOWN ERROR"; - } - return NULL; /* ENOREACH */ -} - -stack_type* -stack_create(struct region* region, size_t size) -{ - stack_type* stack = (stack_type*)region_alloc(region, - sizeof(stack_type)); - stack->capacity = size; - stack->num = 0; - stack->data = (void**) region_alloc(region, sizeof(void*)*size); - memset(stack->data, 0, sizeof(void*)*size); - return stack; -} - -void -stack_push(stack_type* stack, void* elem) -{ - assert(stack); - if(stack->num >= stack->capacity) { - /* stack out of capacity, elem falls off stack */ - return; + switch(rc) { + case RCODE_OK: + return "NO ERROR"; + case RCODE_FORMAT: + return "FORMAT ERROR"; + case RCODE_SERVFAIL: + return "SERV FAIL"; + case RCODE_NXDOMAIN: + return "NAME ERROR"; + case RCODE_IMPL: + return "NOT IMPL"; + case RCODE_REFUSE: + return "REFUSED"; + case RCODE_YXDOMAIN: + return "YXDOMAIN"; + case RCODE_YXRRSET: + return "YXRRSET"; + case RCODE_NXRRSET: + return "NXRRSET"; + case RCODE_NOTAUTH: + return "SERVER NOT AUTHORITATIVE FOR ZONE"; + case RCODE_NOTZONE: + return "NOTZONE"; + default: + return "UNKNOWN ERROR"; } - stack->data[stack->num] = elem; - stack->num ++; -} - -void* -stack_pop(stack_type* stack) -{ - void* elem; - assert(stack); - if(stack->num <= 0) - return NULL; - stack->num --; - elem = stack->data[stack->num]; - stack->data[stack->num] = NULL; - return elem; + return NULL; /* ENOREACH */ } -int -addr2ip( +void +addr2str( #ifdef INET6 - struct sockaddr_storage addr + struct sockaddr_storage *addr #else - struct sockaddr_in addr + struct sockaddr_in *addr #endif -, char *address, socklen_t size) + , char* str, size_t len) { #ifdef INET6 - if (addr.ss_family == AF_INET6) { + if (addr->ss_family == AF_INET6) { if (!inet_ntop(AF_INET6, - &((struct sockaddr_in6 *)&addr)->sin6_addr, - address, size)) - return (1); -#else - if (0) { -#endif - } else { - if (!inet_ntop(AF_INET, - &((struct sockaddr_in *)&addr)->sin_addr, - address, size)) - return (1); + &((struct sockaddr_in6 *)addr)->sin6_addr, str, len)) + strlcpy(str, "[unknown ip6, inet_ntop failed]", len); + return; } - - return (0); +#endif + if (!inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, + str, len)) + strlcpy(str, "[unknown ip4, inet_ntop failed]", len); } diff --git a/usr.sbin/nsd/util.h b/usr.sbin/nsd/util.h index a84f7ac4c72..69dd72f2c35 100644 --- a/usr.sbin/nsd/util.h +++ b/usr.sbin/nsd/util.h @@ -1,7 +1,7 @@ /* * util.h -- set of various support routines. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -10,7 +10,6 @@ #ifndef _UTIL_H_ #define _UTIL_H_ -#include "config.h" #include <sys/time.h> #include <stdarg.h> #include <stdio.h> @@ -31,26 +30,6 @@ struct rr; #define PADDING(n, alignment) \ (ALIGN_UP((n), (alignment)) - (n)) -/* Counter for statistics */ -typedef unsigned long stc_t; - -/** - * Statistics. - * - */ -struct nsdst { - time_t boot; - int period; /* Produce statistics dump every st_period seconds */ - stc_t qtype[257]; /* Counters per qtype */ - stc_t qclass[4]; /* Class IN or Class CH or other */ - stc_t qudp, qudp6; /* Number of queries udp and udp6 */ - stc_t ctcp, ctcp6; /* Number of tcp and tcp6 connections */ - stc_t rcode[17], opcode[6]; /* Rcodes & opcodes */ - /* Dropped, truncated, queries for nonconfigured zone, tx errors */ - stc_t dropped, truncated, wrongzone, txerr, rxerr; - stc_t edns, ednserr, raxfr, nona; -}; - /* * Initialize the logging system. All messages are logged to stderr * until log_open and log_set_log_function are called. @@ -354,6 +333,8 @@ int compare_serial(uint32_t a, uint32_t b); * Generate a random query ID. */ uint16_t qid_generate(void); +/* value between 0 .. (max-1) inclusive */ +int random_generate(int max); /* * call region_destroy on (region*)data, useful for region_add_cleanup(). @@ -379,24 +360,12 @@ int print_rr(FILE *out, struct state_pretty_rr* state, struct rr *record); */ const char* rcode2str(int rc); -/* - * Stack of pointers. - * Stack is fixed size on start. More elems fall off stack. - */ -struct stack { - void** data; - size_t num, capacity; -}; -typedef struct stack stack_type; -stack_type* stack_create(struct region* region, size_t size); -void stack_push(stack_type* stack, void* elem); -void* stack_pop(stack_type* stack); -int addr2ip( +void addr2str( #ifdef INET6 - struct sockaddr_storage addr + struct sockaddr_storage *addr #else - struct sockaddr_in addr + struct sockaddr_in *addr #endif -, char address[], socklen_t size); + , char* str, size_t len); #endif /* _UTIL_H_ */ diff --git a/usr.sbin/nsd/xfrd-disk.c b/usr.sbin/nsd/xfrd-disk.c index aeeda9d4fa4..3b5e0120dd6 100644 --- a/usr.sbin/nsd/xfrd-disk.c +++ b/usr.sbin/nsd/xfrd-disk.c @@ -1,7 +1,7 @@ /* * xfrd-disk.c - XFR (transfer) Daemon TCP system source file. Read/Write state to disk. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -12,6 +12,9 @@ #include <stdlib.h> #include <ctype.h> #include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> #include "xfrd-disk.h" #include "xfrd.h" #include "buffer.h" @@ -238,15 +241,15 @@ xfrd_read_state(struct xfrd_state* xfrd) zone->next_master = nextmas; zone->round_num = round_num; zone->timeout.tv_sec = timeout; - zone->timeout.tv_nsec = 0; + zone->timeout.tv_usec = 0; /* read the zone OK, now set the master properly */ - zone->master = acl_find_num( - zone->zone_options->request_xfr, zone->master_num); + zone->master = acl_find_num(zone->zone_options->pattern-> + request_xfr, zone->master_num); if(!zone->master) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: masters changed for zone %s", zone->apex_str)); - zone->master = zone->zone_options->request_xfr; + zone->master = zone->zone_options->pattern->request_xfr; zone->master_num = 0; zone->round_num = 0; } @@ -440,8 +443,8 @@ xfrd_write_state(struct xfrd_state* xfrd) fprintf(out, "\tnext_master: %d\n", zone->next_master); fprintf(out, "\tround_num: %d\n", zone->round_num); fprintf(out, "\tnext_timeout: %d", - zone->zone_handler.timeout?(int)zone->timeout.tv_sec:0); - if(zone->zone_handler.timeout) { + (zone->zone_handler_flags&EV_TIMEOUT)?(int)zone->timeout.tv_sec:0); + if((zone->zone_handler_flags&EV_TIMEOUT)) { neato_timeout(out, "\t# =", zone->timeout.tv_sec - xfrd_time()); } fprintf(out, "\n"); @@ -459,3 +462,82 @@ xfrd_write_state(struct xfrd_state* xfrd) (int)xfrd->zones->count)); fclose(out); } + +/* return tempdirname */ +static void +tempdirname(char* buf, size_t sz, struct nsd* nsd) +{ + snprintf(buf, sz, "%snsd-xfr-%d", + nsd->options->xfrdir, (int)nsd->pid); +} + +void +xfrd_make_tempdir(struct nsd* nsd) +{ + char tnm[1024]; + tempdirname(tnm, sizeof(tnm), nsd); + /* create parent directories if needed (0750 permissions) */ + if(!create_dirs(tnm)) { + log_msg(LOG_ERR, "parentdirs of %s failed", tnm); + } + /* restrictive permissions here, because this may be in /tmp */ + if(mkdir(tnm, 0700)==-1) { + if(errno != EEXIST) { + log_msg(LOG_ERR, "mkdir %s failed: %s", + tnm, strerror(errno)); + } + } +} + +void +xfrd_del_tempdir(struct nsd* nsd) +{ + char tnm[1024]; + tempdirname(tnm, sizeof(tnm), nsd); + /* ignore parent directories, they are likely /var/tmp, /tmp or + * /var/cache/nsd and do not have to be deleted */ + if(rmdir(tnm)==-1 && errno != ENOENT) { + log_msg(LOG_WARNING, "rmdir %s failed: %s", tnm, + strerror(errno)); + } +} + +/* return name of xfrfile in tempdir */ +static void +tempxfrname(char* buf, size_t sz, struct nsd* nsd, uint64_t number) +{ + char tnm[1024]; + tempdirname(tnm, sizeof(tnm), nsd); + snprintf(buf, sz, "%s/xfr.%lld", tnm, (long long)number); +} + +FILE* +xfrd_open_xfrfile(struct nsd* nsd, uint64_t number, char* mode) +{ + char fname[1024]; + FILE* xfr; + tempxfrname(fname, sizeof(fname), nsd, number); + xfr = fopen(fname, mode); + if(!xfr && errno == ENOENT) { + /* directory may not exist */ + xfrd_make_tempdir(nsd); + xfr = fopen(fname, mode); + } + if(!xfr) { + log_msg(LOG_ERR, "open %s for %s failed: %s", fname, mode, + strerror(errno)); + return NULL; + } + return xfr; +} + +void +xfrd_unlink_xfrfile(struct nsd* nsd, uint64_t number) +{ + char fname[1024]; + tempxfrname(fname, sizeof(fname), nsd, number); + if(unlink(fname) == -1) { + log_msg(LOG_WARNING, "could not unlink %s: %s", fname, + strerror(errno)); + } +} diff --git a/usr.sbin/nsd/xfrd-tcp.c b/usr.sbin/nsd/xfrd-tcp.c index b0fea134aa1..a61ac95fdfa 100644 --- a/usr.sbin/nsd/xfrd-tcp.c +++ b/usr.sbin/nsd/xfrd-tcp.c @@ -1,7 +1,7 @@ /* * xfrd-tcp.c - XFR (transfer) Daemon TCP system source file. Manages tcp conn. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -20,8 +20,29 @@ #include "options.h" #include "namedb.h" #include "xfrd.h" +#include "xfrd-disk.h" #include "util.h" +/* sort tcppipe, first on IP address, for an IPadresss, sort on num_unused */ +static int +xfrd_pipe_cmp(const void* a, const void* b) +{ + const struct xfrd_tcp_pipeline* x = (struct xfrd_tcp_pipeline*)a; + const struct xfrd_tcp_pipeline* y = (struct xfrd_tcp_pipeline*)b; + int r; + if(y->ip_len != x->ip_len) + return (int)y->ip_len - (int)x->ip_len; + r = memcmp(&x->ip, &y->ip, x->ip_len); + if(r != 0) + return r; + /* sort that num_unused is sorted ascending, + * thus, if(x=10, y=1) then result 'bigger', 10-1>0*/ + if(x->num_unused != y->num_unused) + return x->num_unused - y->num_unused; + /* different pipelines are different still, even with same numunused*/ + return (int)(a - b); +} + xfrd_tcp_set_t* xfrd_tcp_set_create(struct region* region) { int i; @@ -31,17 +52,33 @@ xfrd_tcp_set_t* xfrd_tcp_set_create(struct region* region) tcp_set->tcp_waiting_first = 0; tcp_set->tcp_waiting_last = 0; for(i=0; i<XFRD_MAX_TCP; i++) - tcp_set->tcp_state[i] = xfrd_tcp_create(region); + tcp_set->tcp_state[i] = xfrd_tcp_pipeline_create(region); + tcp_set->pipetree = rbtree_create(region, &xfrd_pipe_cmp); return tcp_set; } +struct xfrd_tcp_pipeline* +xfrd_tcp_pipeline_create(region_type* region) +{ + int i; + struct xfrd_tcp_pipeline* tp = (struct xfrd_tcp_pipeline*) + region_alloc_zero(region, sizeof(*tp)); + tp->num_unused = ID_PIPE_NUM; + assert(sizeof(tp->unused)/sizeof(tp->unused[0]) == ID_PIPE_NUM); + for(i=0; i<ID_PIPE_NUM; i++) + tp->unused[i] = (uint16_t)i; + tp->tcp_r = xfrd_tcp_create(region, QIOBUFSZ); + tp->tcp_w = xfrd_tcp_create(region, 512); + return tp; +} + void xfrd_setup_packet(buffer_type* packet, - uint16_t type, uint16_t klass, const dname_type* dname) + uint16_t type, uint16_t klass, const dname_type* dname, uint16_t qid) { /* Set up the header */ buffer_clear(packet); - ID_SET(packet, qid_generate()); + ID_SET(packet, qid); FLAGS_SET(packet, 0); OPCODE_SET(packet, OPCODE_QUERY); QDCOUNT_SET(packet, 1); @@ -153,20 +190,213 @@ xfrd_write_soa_buffer(struct buffer* packet, } xfrd_tcp_t* -xfrd_tcp_create(region_type* region) +xfrd_tcp_create(region_type* region, size_t bufsize) { xfrd_tcp_t* tcp_state = (xfrd_tcp_t*)region_alloc( region, sizeof(xfrd_tcp_t)); memset(tcp_state, 0, sizeof(xfrd_tcp_t)); - tcp_state->packet = buffer_create(region, QIOBUFSZ); + tcp_state->packet = buffer_create(region, bufsize); tcp_state->fd = -1; return tcp_state; } +static struct xfrd_tcp_pipeline* +pipeline_find(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +{ + rbnode_t* sme = NULL; + struct xfrd_tcp_pipeline* r; + /* smaller buf than a full pipeline with 64kb ID array, only need + * the front part with the key info, this front part contains the + * members that the compare function uses. */ + const size_t keysize = sizeof(struct xfrd_tcp_pipeline) - + ID_PIPE_NUM*(sizeof(struct xfrd_zone*) + sizeof(uint16_t)); + /* void* type for alignment of the struct, + * divide the keysize by ptr-size and then add one to round up */ + void* buf[ (keysize / sizeof(void*)) + 1 ]; + struct xfrd_tcp_pipeline* key = (struct xfrd_tcp_pipeline*)buf; + key->node.key = key; + key->ip_len = xfrd_acl_sockaddr_to(zone->master, &key->ip); + key->num_unused = ID_PIPE_NUM; + /* lookup existing tcp transfer to the master with highest unused */ + if(rbtree_find_less_equal(set->pipetree, key, &sme)) { + /* exact match, strange, fully unused tcp cannot be open */ + assert(0); + } + if(!sme) + return NULL; + r = (struct xfrd_tcp_pipeline*)sme->key; + /* <= key pointed at, is the master correct ? */ + if(r->ip_len != key->ip_len) + return NULL; + if(memcmp(&r->ip, &key->ip, key->ip_len) != 0) + return NULL; + /* correct master, is there a slot free for this transfer? */ + if(r->num_unused == 0) + return NULL; + return r; +} + +/* remove zone from tcp waiting list */ +static void +tcp_zone_waiting_list_popfirst(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +{ + assert(zone->tcp_waiting); + set->tcp_waiting_first = zone->tcp_waiting_next; + if(zone->tcp_waiting_next) + zone->tcp_waiting_next->tcp_waiting_prev = NULL; + else set->tcp_waiting_last = 0; + zone->tcp_waiting_next = 0; + zone->tcp_waiting = 0; +} + +/* remove zone from tcp pipe write-wait list */ +static void +tcp_pipe_sendlist_remove(struct xfrd_tcp_pipeline* tp, xfrd_zone_t* zone) +{ + if(zone->in_tcp_send) { + if(zone->tcp_send_prev) + zone->tcp_send_prev->tcp_send_next=zone->tcp_send_next; + else tp->tcp_send_first=zone->tcp_send_next; + if(zone->tcp_send_next) + zone->tcp_send_next->tcp_send_prev=zone->tcp_send_prev; + else tp->tcp_send_last=zone->tcp_send_prev; + zone->in_tcp_send = 0; + } +} + +/* remove first from write-wait list */ +static void +tcp_pipe_sendlist_popfirst(struct xfrd_tcp_pipeline* tp, xfrd_zone_t* zone) +{ + tp->tcp_send_first = zone->tcp_send_next; + if(tp->tcp_send_first) + tp->tcp_send_first->tcp_send_prev = NULL; + else tp->tcp_send_last = NULL; + zone->in_tcp_send = 0; +} + +/* remove zone from tcp pipe ID map */ +static void +tcp_pipe_id_remove(struct xfrd_tcp_pipeline* tp, xfrd_zone_t* zone) +{ + assert(tp->num_unused < ID_PIPE_NUM && tp->num_unused >= 0); + assert(tp->id[zone->query_id] == zone); + tp->id[zone->query_id] = NULL; + tp->unused[tp->num_unused] = zone->query_id; + /* must remove and re-add for sort order in tree */ + (void)rbtree_delete(xfrd->tcp_set->pipetree, &tp->node); + tp->num_unused++; + (void)rbtree_insert(xfrd->tcp_set->pipetree, &tp->node); +} + +/* stop the tcp pipe (and all its zones need to retry) */ +static void +xfrd_tcp_pipe_stop(struct xfrd_tcp_pipeline* tp) +{ + int i, conn = -1; + assert(tp->num_unused < ID_PIPE_NUM); /* at least one 'in-use' */ + assert(ID_PIPE_NUM - tp->num_unused > tp->num_skip); /* at least one 'nonskip' */ + /* need to retry for all the zones connected to it */ + /* these could use different lists and go to a different nextmaster*/ + for(i=0; i<ID_PIPE_NUM; i++) { + if(tp->id[i] && tp->id[i] != TCP_NULL_SKIP) { + xfrd_zone_t* zone = tp->id[i]; + conn = zone->tcp_conn; + zone->tcp_conn = -1; + zone->tcp_waiting = 0; + tcp_pipe_sendlist_remove(tp, zone); + tcp_pipe_id_remove(tp, zone); + xfrd_set_refresh_now(zone); + } + } + assert(conn != -1); + /* now release the entire tcp pipe */ + xfrd_tcp_pipe_release(xfrd->tcp_set, tp, conn); +} + +static void +tcp_pipe_reset_timeout(struct xfrd_tcp_pipeline* tp) +{ + int fd = tp->handler.ev_fd; + struct timeval tv; + tv.tv_sec = xfrd->tcp_set->tcp_timeout; + tv.tv_usec = 0; + if(tp->handler_added) + event_del(&tp->handler); + event_set(&tp->handler, fd, EV_PERSIST|EV_TIMEOUT|EV_READ| + (tp->tcp_send_first?EV_WRITE:0), xfrd_handle_tcp_pipe, tp); + if(event_base_set(xfrd->event_base, &tp->handler) != 0) + log_msg(LOG_ERR, "xfrd tcp: event_base_set failed"); + if(event_add(&tp->handler, &tv) != 0) + log_msg(LOG_ERR, "xfrd tcp: event_add failed"); + tp->handler_added = 1; +} + +/* handle event from fd of tcp pipe */ +void +xfrd_handle_tcp_pipe(int ATTR_UNUSED(fd), short event, void* arg) +{ + struct xfrd_tcp_pipeline* tp = (struct xfrd_tcp_pipeline*)arg; + if((event & EV_WRITE)) { + tcp_pipe_reset_timeout(tp); + if(tp->tcp_send_first) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: event tcp write, zone %s", + tp->tcp_send_first->apex_str)); + xfrd_tcp_write(tp, tp->tcp_send_first); + } + } + if((event & EV_READ) && tp->handler_added) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: event tcp read")); + tcp_pipe_reset_timeout(tp); + xfrd_tcp_read(tp); + } + if((event & EV_TIMEOUT) && tp->handler_added) { + /* tcp connection timed out */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: event tcp timeout")); + xfrd_tcp_pipe_stop(tp); + } +} + +/* add a zone to the pipeline, it starts to want to write its query */ +static void +pipeline_setup_new_zone(xfrd_tcp_set_t* set, struct xfrd_tcp_pipeline* tp, + xfrd_zone_t* zone) +{ + /* assign the ID */ + int idx; + assert(tp->num_unused > 0); + /* we pick a random ID, even though it is TCP anyway */ + idx = random_generate(tp->num_unused); + zone->query_id = tp->unused[idx]; + tp->unused[idx] = tp->unused[tp->num_unused-1]; + tp->id[zone->query_id] = zone; + /* decrement unused counter, and fixup tree */ + (void)rbtree_delete(set->pipetree, &tp->node); + tp->num_unused--; + (void)rbtree_insert(set->pipetree, &tp->node); + + /* add to sendlist, at end */ + zone->tcp_send_next = NULL; + zone->tcp_send_prev = tp->tcp_send_last; + zone->in_tcp_send = 1; + if(tp->tcp_send_last) + tp->tcp_send_last->tcp_send_next = zone; + else tp->tcp_send_first = zone; + tp->tcp_send_last = zone; + + /* is it first in line? */ + if(tp->tcp_send_first == zone) { + xfrd_tcp_setup_write_packet(tp, zone); + /* add write to event handler */ + tcp_pipe_reset_timeout(tp); + } +} + void xfrd_tcp_obtain(xfrd_tcp_set_t* set, xfrd_zone_t* zone) { + struct xfrd_tcp_pipeline* tp; assert(zone->tcp_conn == -1); assert(zone->tcp_waiting == 0); @@ -176,7 +406,7 @@ xfrd_tcp_obtain(xfrd_tcp_set_t* set, xfrd_zone_t* zone) set->tcp_count ++; /* find a free tcp_buffer */ for(i=0; i<XFRD_MAX_TCP; i++) { - if(set->tcp_state[i]->fd == -1) { + if(set->tcp_state[i]->tcp_r->fd == -1) { zone->tcp_conn = i; break; } @@ -186,22 +416,57 @@ xfrd_tcp_obtain(xfrd_tcp_set_t* set, xfrd_zone_t* zone) return; } + tp = set->tcp_state[zone->tcp_conn]; zone->tcp_waiting = 0; /* stop udp use (if any) */ - if(zone->zone_handler.fd != -1) + if(zone->zone_handler.ev_fd != -1) xfrd_udp_release(zone); - if(!xfrd_tcp_open(set, zone)) + if(!xfrd_tcp_open(set, tp, zone)) { + zone->tcp_conn = -1; + set->tcp_count --; + xfrd_set_refresh_now(zone); return; + } + /* ip and ip_len set by tcp_open */ + tp->node.key = tp; + tp->num_unused = ID_PIPE_NUM; + tp->num_skip = 0; + tp->tcp_send_first = NULL; + tp->tcp_send_last = NULL; + memset(tp->id, 0, sizeof(tp->id)); + for(i=0; i<ID_PIPE_NUM; i++) { + tp->unused[i] = i; + } - xfrd_tcp_xfr(set, zone); + /* insert into tree */ + (void)rbtree_insert(set->pipetree, &tp->node); + xfrd_deactivate_zone(zone); + xfrd_unset_timer(zone); + pipeline_setup_new_zone(set, tp, zone); + return; + } + /* check for a pipeline to the same master with unused ID */ + if((tp = pipeline_find(set, zone))!= NULL) { + int i; + if(zone->zone_handler.ev_fd != -1) + xfrd_udp_release(zone); + for(i=0; i<XFRD_MAX_TCP; i++) { + if(set->tcp_state[i] == tp) + zone->tcp_conn = i; + } + xfrd_deactivate_zone(zone); + xfrd_unset_timer(zone); + pipeline_setup_new_zone(set, tp, zone); return; } + /* wait, at end of line */ DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfrd: max number of tcp " "connections (%d) reached.", XFRD_MAX_TCP)); zone->tcp_waiting_next = 0; + zone->tcp_waiting_prev = set->tcp_waiting_last; zone->tcp_waiting = 1; if(!set->tcp_waiting_last) { set->tcp_waiting_first = zone; @@ -210,85 +475,92 @@ xfrd_tcp_obtain(xfrd_tcp_set_t* set, xfrd_zone_t* zone) set->tcp_waiting_last->tcp_waiting_next = zone; set->tcp_waiting_last = zone; } + xfrd_deactivate_zone(zone); xfrd_unset_timer(zone); } int -xfrd_tcp_open(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +xfrd_tcp_open(xfrd_tcp_set_t* set, struct xfrd_tcp_pipeline* tp, + xfrd_zone_t* zone) { int fd, family, conn; - -#ifdef INET6 - struct sockaddr_storage to; -#else - struct sockaddr_in to; -#endif /* INET6 */ - socklen_t to_len; - + struct timeval tv; assert(zone->tcp_conn != -1); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s open tcp conn to %s", zone->apex_str, zone->master->ip_address_spec)); - set->tcp_state[zone->tcp_conn]->is_reading = 0; - set->tcp_state[zone->tcp_conn]->total_bytes = 0; - set->tcp_state[zone->tcp_conn]->msglen = 0; + tp->tcp_r->is_reading = 1; + tp->tcp_r->total_bytes = 0; + tp->tcp_r->msglen = 0; + buffer_clear(tp->tcp_r->packet); + tp->tcp_w->is_reading = 0; + tp->tcp_w->total_bytes = 0; + tp->tcp_w->msglen = 0; + tp->connection_established = 0; if(zone->master->is_ipv6) { #ifdef INET6 family = PF_INET6; #else xfrd_set_refresh_now(zone); - xfrd_tcp_release(set, zone); return 0; #endif } else { family = PF_INET; } fd = socket(family, SOCK_STREAM, IPPROTO_TCP); - set->tcp_state[zone->tcp_conn]->fd = fd; if(fd == -1) { log_msg(LOG_ERR, "xfrd: %s cannot create tcp socket: %s", zone->master->ip_address_spec, strerror(errno)); xfrd_set_refresh_now(zone); - xfrd_tcp_release(set, zone); return 0; } if(fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { log_msg(LOG_ERR, "xfrd: fcntl failed: %s", strerror(errno)); + close(fd); xfrd_set_refresh_now(zone); - xfrd_tcp_release(set, zone); return 0; } - to_len = xfrd_acl_sockaddr_to(zone->master, &to); + tp->ip_len = xfrd_acl_sockaddr_to(zone->master, &tp->ip); /* bind it */ - if (!xfrd_bind_local_interface(fd, - zone->zone_options->outgoing_interface, zone->master, 1)) { - + if (!xfrd_bind_local_interface(fd, zone->zone_options->pattern-> + outgoing_interface, zone->master, 1)) { + close(fd); xfrd_set_refresh_now(zone); - xfrd_tcp_release(set, zone); return 0; } - conn = connect(fd, (struct sockaddr*)&to, to_len); + conn = connect(fd, (struct sockaddr*)&tp->ip, tp->ip_len); if (conn == -1 && errno != EINPROGRESS) { log_msg(LOG_ERR, "xfrd: connect %s failed: %s", zone->master->ip_address_spec, strerror(errno)); + close(fd); xfrd_set_refresh_now(zone); - xfrd_tcp_release(set, zone); return 0; } - - zone->zone_handler.fd = fd; - zone->zone_handler.event_types = NETIO_EVENT_TIMEOUT|NETIO_EVENT_WRITE; - xfrd_set_timer(zone, xfrd_time() + set->tcp_timeout); + tp->tcp_r->fd = fd; + tp->tcp_w->fd = fd; + + /* set the tcp pipe event */ + if(tp->handler_added) + event_del(&tp->handler); + event_set(&tp->handler, fd, EV_PERSIST|EV_TIMEOUT|EV_READ|EV_WRITE, + xfrd_handle_tcp_pipe, tp); + if(event_base_set(xfrd->event_base, &tp->handler) != 0) + log_msg(LOG_ERR, "xfrd tcp: event_base_set failed"); + tv.tv_sec = set->tcp_timeout; + tv.tv_usec = 0; + if(event_add(&tp->handler, &tv) != 0) + log_msg(LOG_ERR, "xfrd tcp: event_add failed"); + tp->handler_added = 1; return 1; } void -xfrd_tcp_xfr(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +xfrd_tcp_setup_write_packet(struct xfrd_tcp_pipeline* tp, xfrd_zone_t* zone) { - xfrd_tcp_t* tcp = set->tcp_state[zone->tcp_conn]; + xfrd_tcp_t* tcp = tp->tcp_w; assert(zone->tcp_conn != -1); assert(zone->tcp_waiting == 0); /* start AXFR or IXFR for the zone */ @@ -298,17 +570,21 @@ xfrd_tcp_xfr(xfrd_tcp_set_t* set, xfrd_zone_t* zone) "(AXFR) for %s to %s", zone->apex_str, zone->master->ip_address_spec)); - xfrd_setup_packet(tcp->packet, TYPE_AXFR, CLASS_IN, zone->apex); + xfrd_setup_packet(tcp->packet, TYPE_AXFR, CLASS_IN, zone->apex, + zone->query_id); } else { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "request incremental zone " "transfer (IXFR) for %s to %s", zone->apex_str, zone->master->ip_address_spec)); - xfrd_setup_packet(tcp->packet, TYPE_IXFR, CLASS_IN, zone->apex); + xfrd_setup_packet(tcp->packet, TYPE_IXFR, CLASS_IN, zone->apex, + zone->query_id); NSCOUNT_SET(tcp->packet, 1); xfrd_write_soa_buffer(tcp->packet, zone->apex, &zone->soa_disk); } - zone->query_id = ID(tcp->packet); + /* old transfer needs to be removed still? */ + if(zone->msg_seq_nr) + xfrd_unlink_xfrfile(xfrd->nsd, zone->xfrfilenumber); zone->msg_seq_nr = 0; zone->msg_rr_count = 0; if(zone->master->key_options && zone->master->key_options->tsig_key) { @@ -317,7 +593,7 @@ xfrd_tcp_xfr(xfrd_tcp_set_t* set, xfrd_zone_t* zone) buffer_flip(tcp->packet); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "sent tcp query with ID %d", zone->query_id)); tcp->msglen = buffer_limit(tcp->packet); - /* wait for select to complete connect before write */ + tcp->total_bytes = 0; } static void @@ -382,12 +658,14 @@ int conn_write(xfrd_tcp_t* tcp) } void -xfrd_tcp_write(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +xfrd_tcp_write(struct xfrd_tcp_pipeline* tp, xfrd_zone_t* zone) { int ret; - xfrd_tcp_t* tcp = set->tcp_state[zone->tcp_conn]; + xfrd_tcp_t* tcp = tp->tcp_w; assert(zone->tcp_conn != -1); - if(tcp->total_bytes == 0) { + assert(zone == tp->tcp_send_first); + /* see if for non-established connection, there is a connect error */ + if(!tp->connection_established) { /* check for pending error from nonblocking connect */ /* from Stevens, unix network programming, vol1, 3rd ed, p450 */ int error = 0; @@ -398,28 +676,51 @@ xfrd_tcp_write(xfrd_tcp_set_t* set, xfrd_zone_t* zone) if(error == EINPROGRESS || error == EWOULDBLOCK) return; /* try again later */ if(error != 0) { - log_msg(LOG_ERR, "Could not tcp connect to %s: %s", - zone->master->ip_address_spec, strerror(error)); - xfrd_set_refresh_now(zone); - xfrd_tcp_release(set, zone); + log_msg(LOG_ERR, "%s: Could not tcp connect to %s: %s", + zone->apex_str, zone->master->ip_address_spec, + strerror(error)); + xfrd_tcp_pipe_stop(tp); return; } } ret = conn_write(tcp); if(ret == -1) { log_msg(LOG_ERR, "xfrd: failed writing tcp %s", strerror(errno)); - xfrd_set_refresh_now(zone); - xfrd_tcp_release(set, zone); + xfrd_tcp_pipe_stop(tp); return; } + if(tcp->total_bytes != 0 && !tp->connection_established) + tp->connection_established = 1; if(ret == 0) { return; /* write again later */ } - /* done writing, get ready for reading */ - tcp->is_reading = 1; - tcp_conn_ready_for_reading(tcp); - zone->zone_handler.event_types = NETIO_EVENT_READ|NETIO_EVENT_TIMEOUT; - xfrd_tcp_read(set, zone); + /* done writing this message */ + + /* remove first zone from sendlist */ + tcp_pipe_sendlist_popfirst(tp, zone); + + /* see if other zone wants to write; init; let it write (now) */ + /* and use a loop, because 64k stack calls is a too much */ + while(tp->tcp_send_first) { + /* setup to write for this zone */ + xfrd_tcp_setup_write_packet(tp, tp->tcp_send_first); + /* attempt to write for this zone (if success, continue loop)*/ + ret = conn_write(tcp); + if(ret == -1) { + log_msg(LOG_ERR, "xfrd: failed writing tcp %s", strerror(errno)); + xfrd_tcp_pipe_stop(tp); + return; + } + if(ret == 0) + return; /* write again later */ + tcp_pipe_sendlist_popfirst(tp, tp->tcp_send_first); + } + + /* if sendlist empty, remove WRITE from event */ + + /* listen to READ, and not WRITE events */ + assert(tp->tcp_send_first == NULL); + tcp_pipe_reset_timeout(tp); } int @@ -496,42 +797,71 @@ conn_read(xfrd_tcp_t* tcp) } void -xfrd_tcp_read(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +xfrd_tcp_read(struct xfrd_tcp_pipeline* tp) { - xfrd_tcp_t* tcp = set->tcp_state[zone->tcp_conn]; + xfrd_zone_t* zone; + xfrd_tcp_t* tcp = tp->tcp_r; int ret; + enum xfrd_packet_result pkt_result; - assert(zone->tcp_conn != -1); ret = conn_read(tcp); if(ret == -1) { - xfrd_set_refresh_now(zone); - xfrd_tcp_release(set, zone); + xfrd_tcp_pipe_stop(tp); return; } if(ret == 0) return; - /* completed msg */ buffer_flip(tcp->packet); - switch(xfrd_handle_received_xfr_packet(zone, tcp->packet)) { + /* see which ID number it is, if skip, handle skip, NULL: warn */ + if(tcp->msglen < QHEADERSZ) { + /* too short for DNS header, skip it */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: tcp skip response that is too short")); + tcp_conn_ready_for_reading(tcp); + return; + } + zone = tp->id[ID(tcp->packet)]; + if(!zone || zone == TCP_NULL_SKIP) { + /* no zone for this id? skip it */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: tcp skip response with %s ID", + zone?"set-to-skip":"unknown")); + tcp_conn_ready_for_reading(tcp); + return; + } + assert(zone->tcp_conn != -1); + + /* handle message for zone */ + pkt_result = xfrd_handle_received_xfr_packet(zone, tcp->packet); + /* setup for reading the next packet on this connection */ + tcp_conn_ready_for_reading(tcp); + switch(pkt_result) { case xfrd_packet_more: - tcp_conn_ready_for_reading(tcp); + /* wait for next packet */ break; - case xfrd_packet_transfer: case xfrd_packet_newlease: - xfrd_tcp_release(set, zone); + /* set to skip if more packets with this ID */ + tp->id[zone->query_id] = TCP_NULL_SKIP; + tp->num_skip++; + /* fall through to remove zone from tp */ + case xfrd_packet_transfer: + xfrd_tcp_release(xfrd->tcp_set, zone); assert(zone->round_num == -1); break; case xfrd_packet_notimpl: zone->master->ixfr_disabled = time(NULL); - xfrd_tcp_release(set, zone); + xfrd_tcp_release(xfrd->tcp_set, zone); /* query next server */ xfrd_make_request(zone); break; case xfrd_packet_bad: case xfrd_packet_tcp: default: - xfrd_tcp_release(set, zone); + /* set to skip if more packets with this ID */ + tp->id[zone->query_id] = TCP_NULL_SKIP; + tp->num_skip++; + xfrd_tcp_release(xfrd->tcp_set, zone); /* query next server */ xfrd_make_request(zone); break; @@ -542,40 +872,103 @@ void xfrd_tcp_release(xfrd_tcp_set_t* set, xfrd_zone_t* zone) { int conn = zone->tcp_conn; + struct xfrd_tcp_pipeline* tp = set->tcp_state[conn]; DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s released tcp conn to %s", zone->apex_str, zone->master->ip_address_spec)); assert(zone->tcp_conn != -1); assert(zone->tcp_waiting == 0); zone->tcp_conn = -1; zone->tcp_waiting = 0; - zone->zone_handler.fd = -1; - zone->zone_handler.event_types = NETIO_EVENT_READ|NETIO_EVENT_TIMEOUT; - if(set->tcp_state[conn]->fd != -1) - close(set->tcp_state[conn]->fd); + /* remove from tcp_send list */ + tcp_pipe_sendlist_remove(tp, zone); + /* remove it from the ID list */ + if(tp->id[zone->query_id] != TCP_NULL_SKIP) + tcp_pipe_id_remove(tp, zone); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: released tcp pipe now %d unused", + tp->num_unused)); + /* if pipe was full, but no more, then see if waiting element is + * for the same master, and can fill the unused ID */ + if(tp->num_unused == 1 && set->tcp_waiting_first) { + struct sockaddr_storage to; + socklen_t to_len = xfrd_acl_sockaddr_to( + set->tcp_waiting_first->master, &to); + if(to_len == tp->ip_len && memcmp(&to, &tp->ip, to_len) == 0) { + /* use this connnection for the waiting zone */ + zone = set->tcp_waiting_first; + assert(zone->tcp_conn == -1); + zone->tcp_conn = conn; + tcp_zone_waiting_list_popfirst(set, zone); + if(zone->zone_handler.ev_fd != -1) + xfrd_udp_release(zone); + xfrd_unset_timer(zone); + pipeline_setup_new_zone(set, tp, zone); + return; + } + /* waiting zone did not go to same server */ + } - set->tcp_state[conn]->fd = -1; + /* if all unused, or only skipped leftover, close the pipeline */ + if(tp->num_unused >= ID_PIPE_NUM || tp->num_skip >= ID_PIPE_NUM - tp->num_unused) + xfrd_tcp_pipe_release(set, tp, conn); +} +void +xfrd_tcp_pipe_release(xfrd_tcp_set_t* set, struct xfrd_tcp_pipeline* tp, + int conn) +{ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: tcp pipe released")); + /* one handler per tcp pipe */ + if(tp->handler_added) + event_del(&tp->handler); + tp->handler_added = 0; + + /* fd in tcp_r and tcp_w is the same, close once */ + if(tp->tcp_r->fd != -1) + close(tp->tcp_r->fd); + tp->tcp_r->fd = -1; + tp->tcp_w->fd = -1; + + /* remove from pipetree */ + (void)rbtree_delete(xfrd->tcp_set->pipetree, &tp->node); + + /* a waiting zone can use the free tcp slot (to another server) */ if(set->tcp_count == XFRD_MAX_TCP && set->tcp_waiting_first) { - /* pop first waiting process */ - zone = set->tcp_waiting_first; - if(set->tcp_waiting_last == zone) - set->tcp_waiting_last = 0; + int i; - set->tcp_waiting_first = zone->tcp_waiting_next; - zone->tcp_waiting_next = 0; + /* pop first waiting process */ + xfrd_zone_t* zone = set->tcp_waiting_first; /* start it */ assert(zone->tcp_conn == -1); zone->tcp_conn = conn; - zone->tcp_waiting = 0; + /* stop udp (if any) */ - if(zone->zone_handler.fd != -1) + if(zone->zone_handler.ev_fd != -1) xfrd_udp_release(zone); - - if(!xfrd_tcp_open(set, zone)) + if(!xfrd_tcp_open(set, tp, zone)) { + zone->tcp_conn = -1; + set->tcp_count --; + xfrd_set_refresh_now(zone); return; + } + /* re-init this tcppipe */ + /* ip and ip_len set by tcp_open */ + tp->node.key = tp; + tp->num_unused = ID_PIPE_NUM; + tp->num_skip = 0; + tp->tcp_send_first = NULL; + tp->tcp_send_last = NULL; + memset(tp->id, 0, sizeof(tp->id)); + for(i=0; i<ID_PIPE_NUM; i++) { + tp->unused[i] = i; + } - xfrd_tcp_xfr(set, zone); + /* insert into tree */ + (void)rbtree_insert(set->pipetree, &tp->node); + /* succeeded? remove zone from lists and setup write */ + xfrd_unset_timer(zone); + tcp_zone_waiting_list_popfirst(set, zone); + pipeline_setup_new_zone(set, tp, zone); } else { assert(!set->tcp_waiting_first); @@ -583,3 +976,4 @@ xfrd_tcp_release(xfrd_tcp_set_t* set, xfrd_zone_t* zone) assert(set->tcp_count >= 0); } } + diff --git a/usr.sbin/nsd/xfrd.c b/usr.sbin/nsd/xfrd.c index 7bba268813e..fc6a4905cbd 100644 --- a/usr.sbin/nsd/xfrd.c +++ b/usr.sbin/nsd/xfrd.c @@ -1,7 +1,7 @@ /* * xfrd.c - XFR (transfer) Daemon source file. Coordinates SOA updates. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -13,6 +13,8 @@ #include <unistd.h> #include <stdlib.h> #include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> #include "xfrd.h" #include "xfrd-tcp.h" #include "xfrd-disk.h" @@ -23,8 +25,10 @@ #include "region-allocator.h" #include "nsd.h" #include "packet.h" +#include "rdata.h" #include "difffile.h" #include "ipc.h" +#include "remote.h" #define XFRD_TRANSFER_TIMEOUT_START 10 /* empty zone timeout is between x and 2*x seconds */ #define XFRD_TRANSFER_TIMEOUT_MAX 14400 /* empty zone timeout max expbackoff */ @@ -43,14 +47,13 @@ xfrd_state_t* xfrd = 0; static void xfrd_main(); /* shut down xfrd, close sockets. */ static void xfrd_shutdown(); +/* delete pending task xfr files in tmp */ +static void xfrd_clean_pending_tasks(struct nsd* nsd, udb_base* u); /* create zone rbtree at start */ static void xfrd_init_zones(); -/* free up memory used by main database */ -static void xfrd_free_namedb(); +/* initial handshake with SOAINFO from main and send expire to main */ +static void xfrd_receive_soa(int socket, int shortsoa); -/* handle zone timeout, event */ -static void xfrd_handle_zone(netio_type *netio, - netio_handler_type *handler, netio_event_types_type event_types); /* handle incoming notification message. soa can be NULL. true if transfer needed. */ static int xfrd_handle_incoming_notify(xfrd_zone_t* zone, xfrd_soa_t* soa); @@ -66,8 +69,7 @@ static void xfrd_set_timer_refresh(xfrd_zone_t* zone); /* set reload timeout */ static void xfrd_set_reload_timeout(); /* handle reload timeout */ -static void xfrd_handle_reload(netio_type *netio, - netio_handler_type *handler, netio_event_types_type event_types); +static void xfrd_handle_reload(int fd, short event, void* arg); /* send expiry notifications to nsd */ static void xfrd_send_expire_notification(xfrd_zone_t* zone); @@ -82,60 +84,99 @@ static void xfrd_udp_read(xfrd_zone_t* zone); /* find master by notify number */ static int find_same_master_notify(xfrd_zone_t* zone, int acl_num_nfy); +static void +xfrd_signal_callback(int sig, short event, void* ATTR_UNUSED(arg)) +{ + if(!(event & EV_SIGNAL)) + return; + sig_handler(sig); +} + +static void +xfrd_sigsetup(int sig) +{ + /* no need to remember the event ; dealloc on process exit */ + struct event *ev = xalloc_zero(sizeof(*ev)); + signal_set(ev, sig, xfrd_signal_callback, NULL); + if(event_base_set(xfrd->event_base, ev) != 0) { + log_msg(LOG_ERR, "xfrd sig handler: event_base_set failed"); + } + if(signal_add(ev, NULL) != 0) { + log_msg(LOG_ERR, "xfrd sig handler: signal_add failed"); + } +} + void -xfrd_init(int socket, struct nsd* nsd) +xfrd_init(int socket, struct nsd* nsd, int shortsoa, int reload_active) { region_type* region; assert(xfrd == 0); /* to setup signalhandling */ - nsd->server_kind = NSD_SERVER_BOTH; + nsd->server_kind = NSD_SERVER_MAIN; - region = region_create(xalloc, free); + region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE, + DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1); xfrd = (xfrd_state_t*)region_alloc(region, sizeof(xfrd_state_t)); memset(xfrd, 0, sizeof(xfrd_state_t)); xfrd->region = region; xfrd->xfrd_start_time = time(0); - xfrd->netio = netio_create(xfrd->region); + xfrd->event_base = nsd_child_event_base(); + if(!xfrd->event_base) { + log_msg(LOG_ERR, "xfrd: cannot create event base"); + exit(1); + } xfrd->nsd = nsd; + xfrd_sigsetup(SIGHUP); + xfrd_sigsetup(SIGTERM); + xfrd_sigsetup(SIGQUIT); + xfrd_sigsetup(SIGCHLD); + xfrd_sigsetup(SIGALRM); + xfrd_sigsetup(SIGILL); + xfrd_sigsetup(SIGUSR1); + xfrd_sigsetup(SIGINT); xfrd->packet = buffer_create(xfrd->region, QIOBUFSZ); xfrd->udp_waiting_first = NULL; xfrd->udp_waiting_last = NULL; xfrd->udp_use_num = 0; + xfrd->got_time = 0; + xfrd->xfrfilenumber = 0; + xfrd->activated_first = NULL; xfrd->ipc_pass = buffer_create(xfrd->region, QIOBUFSZ); - xfrd->parent_soa_info_pass = 0; - - /* add the handlers already, because this involves allocs */ - xfrd->reload_handler.fd = -1; - xfrd->reload_handler.timeout = NULL; - xfrd->reload_handler.user_data = xfrd; - xfrd->reload_handler.event_types = NETIO_EVENT_TIMEOUT; - xfrd->reload_handler.event_handler = xfrd_handle_reload; + xfrd->last_task = region_alloc(xfrd->region, sizeof(*xfrd->last_task)); + udb_ptr_init(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask]); + assert(shortsoa || udb_base_get_userdata(xfrd->nsd->task[xfrd->nsd->mytask])->data == 0); + + xfrd->reload_handler.ev_fd = -1; + xfrd->reload_added = 0; xfrd->reload_timeout.tv_sec = 0; xfrd->reload_cmd_last_sent = xfrd->xfrd_start_time; - xfrd->can_send_reload = 1; + xfrd->can_send_reload = !reload_active; xfrd->ipc_send_blocked = 0; - xfrd->ipc_handler.fd = socket; - xfrd->ipc_handler.timeout = NULL; - xfrd->ipc_handler.user_data = xfrd; - xfrd->ipc_handler.event_types = NETIO_EVENT_READ; - xfrd->ipc_handler.event_handler = xfrd_handle_ipc; - xfrd->ipc_conn = xfrd_tcp_create(xfrd->region); + event_set(&xfrd->ipc_handler, socket, EV_PERSIST|EV_READ, + xfrd_handle_ipc, xfrd); + if(event_base_set(xfrd->event_base, &xfrd->ipc_handler) != 0) + log_msg(LOG_ERR, "xfrd ipc handler: event_base_set failed"); + if(event_add(&xfrd->ipc_handler, NULL) != 0) + log_msg(LOG_ERR, "xfrd ipc handler: event_add failed"); + xfrd->ipc_handler_flags = EV_PERSIST|EV_READ; + xfrd->ipc_conn = xfrd_tcp_create(xfrd->region, QIOBUFSZ); /* not reading using ipc_conn yet */ xfrd->ipc_conn->is_reading = 0; - xfrd->ipc_conn->fd = xfrd->ipc_handler.fd; - xfrd->ipc_conn_write = xfrd_tcp_create(xfrd->region); - xfrd->ipc_conn_write->fd = xfrd->ipc_handler.fd; + xfrd->ipc_conn->fd = socket; xfrd->need_to_send_reload = 0; - xfrd->sending_zone_state = 0; - xfrd->dirty_zones = stack_create(xfrd->region, - nsd_options_num_zones(nsd->options)); + xfrd->need_to_send_shutdown = 0; + xfrd->need_to_send_stats = 0; xfrd->notify_waiting_first = NULL; xfrd->notify_waiting_last = NULL; xfrd->notify_udp_num = 0; +#ifdef HAVE_SSL + daemon_remote_attach(xfrd->nsd->rc, xfrd); +#endif + xfrd->tcp_set = xfrd_tcp_set_create(xfrd->region); xfrd->tcp_set->tcp_timeout = nsd->tcp_timeout; #ifndef HAVE_ARC4RANDOM @@ -143,37 +184,89 @@ xfrd_init(int socket, struct nsd* nsd) #endif DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd pre-startup")); - diff_snip_garbage(nsd->db, nsd->options); xfrd_init_zones(); - xfrd_free_namedb(); + xfrd_receive_soa(socket, shortsoa); xfrd_read_state(xfrd); - xfrd_send_expy_all_zones(); - - /* add handlers after zone handlers so they are before them in list */ - netio_add_handler(xfrd->netio, &xfrd->reload_handler); - netio_add_handler(xfrd->netio, &xfrd->ipc_handler); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd startup")); xfrd_main(); } static void -xfrd_main() +xfrd_process_activated(void) { + xfrd_zone_t* zone; + while((zone = xfrd->activated_first)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s activation", + zone->apex_str)); + /* pop zone from activated list */ + xfrd->activated_first = zone->activated_next; + if(zone->activated_next) + zone->activated_next->activated_prev = NULL; + zone->is_activated = 0; + /* run it : no events, specifically not the TIMEOUT event, + * so that running zone transfers are not interrupted */ + xfrd_handle_zone(zone->zone_handler.ev_fd, 0, zone); + } +} + +static void +xfrd_sig_process(void) +{ + if(xfrd->nsd->signal_hint_quit || xfrd->nsd->signal_hint_shutdown) { + xfrd->nsd->signal_hint_quit = 0; + xfrd->nsd->signal_hint_shutdown = 0; + xfrd->need_to_send_shutdown = 1; + if(!(xfrd->ipc_handler_flags&EV_WRITE)) { + ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE); + } + } else if(xfrd->nsd->signal_hint_reload_hup) { + log_msg(LOG_WARNING, "SIGHUP received, reloading..."); + xfrd->nsd->signal_hint_reload_hup = 0; + if(xfrd->nsd->options->zonefiles_check) { + task_new_check_zonefiles(xfrd->nsd->task[ + xfrd->nsd->mytask], xfrd->last_task, NULL); + } + xfrd_set_reload_now(xfrd); + } else if(xfrd->nsd->signal_hint_statsusr) { + xfrd->nsd->signal_hint_statsusr = 0; + xfrd->need_to_send_stats = 1; + if(!(xfrd->ipc_handler_flags&EV_WRITE)) { + ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE); + } + } else if(xfrd->nsd->signal_hint_child) { + int status; + pid_t child_pid; + xfrd->nsd->signal_hint_child = 0; + while((child_pid = waitpid(0, &status, WNOHANG)) != -1 && child_pid != 0) { + if(status != 0) { + log_msg(LOG_ERR, "process serverparent %d exited with status %d", + (int)child_pid, status); + } + } + } +} + +static void +xfrd_main(void) +{ + /* we may have signals from the startup period, process them */ + xfrd_sig_process(); xfrd->shutdown = 0; while(!xfrd->shutdown) { + /* process activated zones before blocking in select again */ + xfrd_process_activated(); /* dispatch may block for a longer period, so current is gone */ xfrd->got_time = 0; - if(netio_dispatch(xfrd->netio, NULL, 0) == -1) { + if(event_base_loop(xfrd->event_base, EVLOOP_ONCE) == -1) { if (errno != EINTR) { log_msg(LOG_ERR, - "xfrd netio_dispatch failed: %s", + "xfrd dispatch failed: %s", strerror(errno)); } } - if(xfrd->nsd->signal_hint_quit || xfrd->nsd->signal_hint_shutdown) - xfrd->shutdown = 1; + xfrd_sig_process(); } xfrd_shutdown(); } @@ -182,45 +275,117 @@ static void xfrd_shutdown() { xfrd_zone_t* zone; - int i; DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd shutdown")); + event_del(&xfrd->ipc_handler); + close(xfrd->ipc_handler.ev_fd); /* notifies parent we stop */ xfrd_write_state(xfrd); - close(xfrd->ipc_handler.fd); - /* close tcp sockets */ - for(i=0; i<XFRD_MAX_TCP; i++) - { - if(xfrd->tcp_set->tcp_state[i]->fd != -1) { - close(xfrd->tcp_set->tcp_state[i]->fd); - xfrd->tcp_set->tcp_state[i]->fd = -1; - } + if(xfrd->reload_added) { + event_del(&xfrd->reload_handler); + xfrd->reload_added = 0; } - /* close udp sockets */ +#ifdef HAVE_SSL + daemon_remote_close(xfrd->nsd->rc); /* close sockets of rc */ +#endif + /* close sockets */ RBTREE_FOR(zone, xfrd_zone_t*, xfrd->zones) { - if(zone->tcp_conn==-1 && zone->zone_handler.fd != -1) { - close(zone->zone_handler.fd); - zone->zone_handler.fd = -1; + if(zone->event_added) { + event_del(&zone->zone_handler); + if(zone->zone_handler.ev_fd != -1) { + close(zone->zone_handler.ev_fd); + zone->zone_handler.ev_fd = -1; + } + zone->event_added = 0; } } close_notify_fds(xfrd->notify_zones); - /* shouldn't we clean up memory used by xfrd process */ + /* if we are killed past this point this is not a problem, + * some files left in /tmp are cleaned by the OS, but it is neater + * to clean them out */ + + /* unlink xfr files for running transfers */ + RBTREE_FOR(zone, xfrd_zone_t*, xfrd->zones) + { + if(zone->msg_seq_nr) + xfrd_unlink_xfrfile(xfrd->nsd, zone->xfrfilenumber); + } + /* unlink xfr files in not-yet-done task file */ + xfrd_clean_pending_tasks(xfrd->nsd, xfrd->nsd->task[xfrd->nsd->mytask]); + xfrd_del_tempdir(xfrd->nsd); + + /* process-exit cleans up memory used by xfrd process */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd shutdown complete")); exit(0); } static void -xfrd_init_zones() +xfrd_clean_pending_tasks(struct nsd* nsd, udb_base* u) +{ + udb_ptr t; + udb_ptr_new(&t, u, udb_base_get_userdata(u)); + /* no dealloc of entries, we delete the entire file when done */ + while(!udb_ptr_is_null(&t)) { + if(TASKLIST(&t)->task_type == task_apply_xfr) { + xfrd_unlink_xfrfile(nsd, TASKLIST(&t)->yesno); + } + udb_ptr_set_rptr(&t, u, &TASKLIST(&t)->next); + } + udb_ptr_unlink(&t, u); +} + +void +xfrd_init_slave_zone(xfrd_state_t* xfrd, zone_options_t* zone_opt) { - zone_type *dbzone; - zone_options_t *zone_opt; xfrd_zone_t *xzone; - const dname_type* dname; + xzone = (xfrd_zone_t*)region_alloc(xfrd->region, sizeof(xfrd_zone_t)); + memset(xzone, 0, sizeof(xfrd_zone_t)); + xzone->apex = zone_opt->node.key; + xzone->apex_str = zone_opt->name; + xzone->state = xfrd_zone_refreshing; + xzone->zone_options = zone_opt; + /* first retry will use first master */ + xzone->master = 0; + xzone->master_num = 0; + xzone->next_master = 0; + xzone->fresh_xfr_timeout = XFRD_TRANSFER_TIMEOUT_START; + + xzone->soa_nsd_acquired = 0; + xzone->soa_disk_acquired = 0; + xzone->soa_notified_acquired = 0; + /* [0]=1, [1]=0; "." domain name */ + xzone->soa_nsd.prim_ns[0] = 1; + xzone->soa_nsd.email[0] = 1; + xzone->soa_disk.prim_ns[0]=1; + xzone->soa_disk.email[0]=1; + xzone->soa_notified.prim_ns[0]=1; + xzone->soa_notified.email[0]=1; + + xzone->zone_handler.ev_fd = -1; + xzone->zone_handler_flags = 0; + xzone->event_added = 0; + + xzone->tcp_conn = -1; + xzone->tcp_waiting = 0; + xzone->udp_waiting = 0; + xzone->is_activated = 0; + + tsig_create_record_custom(&xzone->tsig, NULL, 0, 0, 4); + + /* set refreshing anyway, if we have data it may be old */ + xfrd_set_refresh_now(xzone); + + xzone->node.key = xzone->apex; + rbtree_insert(xfrd->zones, (rbnode_t*)xzone); +} +static void +xfrd_init_zones() +{ + zone_options_t *zone_opt; assert(xfrd->zones == 0); - assert(xfrd->nsd->db != 0); xfrd->zones = rbtree_create(xfrd->region, (int (*)(const void *, const void *)) dname_compare); @@ -229,101 +394,235 @@ xfrd_init_zones() RBTREE_FOR(zone_opt, zone_options_t*, xfrd->nsd->options->zone_options) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Zone %s\n", zone_opt->name)); - dname = dname_parse(xfrd->region, zone_opt->name); - if(!dname) { - log_msg(LOG_ERR, "xfrd: Could not parse zone name %s.", zone_opt->name); - continue; - } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: adding %s zone", + zone_opt->name)); - dbzone = domain_find_zone(domain_table_find(xfrd->nsd->db->domains, dname)); - if(dbzone && dname_compare(dname, domain_dname(dbzone->apex)) != 0) - dbzone = 0; /* we found a parent zone */ - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: adding %s zone %s\n", - dbzone?"filled":"empty", zone_opt->name)); - - init_notify_send(xfrd->notify_zones, xfrd->netio, - xfrd->region, dname, zone_opt, dbzone); + init_notify_send(xfrd->notify_zones, xfrd->region, zone_opt); if(!zone_is_slave(zone_opt)) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s, master zone has no outgoing xfr requests", zone_opt->name)); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s, " + "master zone has no outgoing xfr requests", + zone_opt->name)); continue; } + xfrd_init_slave_zone(xfrd, zone_opt); + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: started server %d " + "secondary zones", (int)xfrd->zones->count)); +} - xzone = (xfrd_zone_t*)region_alloc(xfrd->region, sizeof(xfrd_zone_t)); - memset(xzone, 0, sizeof(xfrd_zone_t)); - xzone->apex = dname; - xzone->apex_str = zone_opt->name; - xzone->state = xfrd_zone_refreshing; - xzone->dirty = 0; - xzone->zone_options = zone_opt; - /* first retry will use first master */ - xzone->master = 0; - xzone->master_num = 0; - xzone->next_master = 0; - xzone->fresh_xfr_timeout = XFRD_TRANSFER_TIMEOUT_START; - - xzone->soa_nsd_acquired = 0; - xzone->soa_disk_acquired = 0; - xzone->soa_notified_acquired = 0; - /* [0]=1, [1]=0; "." domain name */ - xzone->soa_nsd.prim_ns[0] = 1; - xzone->soa_nsd.email[0] = 1; - xzone->soa_disk.prim_ns[0]=1; - xzone->soa_disk.email[0]=1; - xzone->soa_notified.prim_ns[0]=1; - xzone->soa_notified.email[0]=1; - - xzone->zone_handler.fd = -1; - xzone->zone_handler.timeout = 0; - xzone->zone_handler.user_data = xzone; - xzone->zone_handler.event_types = - NETIO_EVENT_READ|NETIO_EVENT_TIMEOUT; - xzone->zone_handler.event_handler = xfrd_handle_zone; - netio_add_handler(xfrd->netio, &xzone->zone_handler); - xzone->tcp_conn = -1; - xzone->tcp_waiting = 0; - xzone->udp_waiting = 0; - - tsig_create_record_custom(&xzone->tsig, xfrd->region, 0, 0, 4); - - if(dbzone && dbzone->soa_rrset && dbzone->soa_rrset->rrs) { - xzone->soa_nsd_acquired = xfrd_time(); - xzone->soa_disk_acquired = xfrd_time(); - /* we only use the first SOA in the rrset */ - xfrd_copy_soa(&xzone->soa_nsd, dbzone->soa_rrset->rrs); - xfrd_copy_soa(&xzone->soa_disk, dbzone->soa_rrset->rrs); - } - /* set refreshing anyway, we have data but it may be old */ - xfrd_set_refresh_now(xzone); +static void +xfrd_process_soa_info_task(struct task_list_d* task) +{ + xfrd_soa_t soa; + xfrd_soa_t* soa_ptr = &soa; + xfrd_zone_t* zone; + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: process SOAINFO %s", + dname_to_string(task->zname, 0))); + zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, task->zname); + if(task->size <= sizeof(struct task_list_d)+dname_total_size( + task->zname)+sizeof(uint32_t)*6 + sizeof(uint8_t)*2) { + /* NSD has zone without any info */ + DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s lost zone", + dname_to_string(task->zname,0))); + soa_ptr = NULL; + } else { + uint8_t* p = (uint8_t*)task->zname + dname_total_size( + task->zname); + /* read the soa info */ + memset(&soa, 0, sizeof(soa)); + /* left out type, klass, count for speed */ + soa.type = htons(TYPE_SOA); + soa.klass = htons(CLASS_IN); + memmove(&soa.ttl, p, sizeof(uint32_t)); + p += sizeof(uint32_t); + soa.rdata_count = htons(7); + memmove(soa.prim_ns, p, sizeof(uint8_t)); + p += sizeof(uint8_t); + memmove(soa.prim_ns+1, p, soa.prim_ns[0]); + p += soa.prim_ns[0]; + memmove(soa.email, p, sizeof(uint8_t)); + p += sizeof(uint8_t); + memmove(soa.email+1, p, soa.email[0]); + p += soa.email[0]; + memmove(&soa.serial, p, sizeof(uint32_t)); + p += sizeof(uint32_t); + memmove(&soa.refresh, p, sizeof(uint32_t)); + p += sizeof(uint32_t); + memmove(&soa.retry, p, sizeof(uint32_t)); + p += sizeof(uint32_t); + memmove(&soa.expire, p, sizeof(uint32_t)); + p += sizeof(uint32_t); + memmove(&soa.minimum, p, sizeof(uint32_t)); + p += sizeof(uint32_t); + DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s %u", + dname_to_string(task->zname,0), + (unsigned)ntohl(soa.serial))); + } - xzone->node.key = dname; - rbtree_insert(xfrd->zones, (rbnode_t*)xzone); + if(!zone) { + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: zone %s master zone updated", + dname_to_string(task->zname,0))); + notify_handle_master_zone_soainfo(xfrd->notify_zones, + task->zname, soa_ptr); + return; } - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: started server %d secondary zones", (int)xfrd->zones->count)); + xfrd_handle_incoming_soa(zone, soa_ptr, xfrd_time()); } void -xfrd_send_expy_all_zones() +xfrd_receive_soa(int socket, int shortsoa) { + sig_atomic_t cmd; + struct udb_base* xtask = xfrd->nsd->task[xfrd->nsd->mytask]; + udb_ptr last_task, t; xfrd_zone_t* zone; - RBTREE_FOR(zone, xfrd_zone_t*, xfrd->zones) - { - xfrd_send_expire_notification(zone); + + if(!shortsoa) { + /* put all expired zones into mytask */ + udb_ptr_init(&last_task, xtask); + RBTREE_FOR(zone, xfrd_zone_t*, xfrd->zones) { + if(zone->state == xfrd_zone_expired) { + task_new_expire(xtask, &last_task, zone->apex, 1); + } + } + udb_ptr_unlink(&last_task, xtask); + + /* send RELOAD to main to give it this tasklist */ + task_process_sync(xtask); + cmd = NSD_RELOAD; + if(!write_socket(socket, &cmd, sizeof(cmd))) { + log_msg(LOG_ERR, "problems sending reload xfrdtomain: %s", + strerror(errno)); + } + } + + /* receive RELOAD_DONE to get SOAINFO tasklist */ + if(block_read(NULL, socket, &cmd, sizeof(cmd), -1) != sizeof(cmd) || + cmd != NSD_RELOAD_DONE) { + log_msg(LOG_ERR, "did not get start signal from main"); + exit(1); + } +#ifdef BIND8_STATS + if(block_read(NULL, socket, &xfrd->reload_pid, sizeof(pid_t), -1) + != sizeof(pid_t)) { + log_msg(LOG_ERR, "xfrd cannot get reload_pid"); + } +#endif /* BIND8_STATS */ + + /* process tasklist (SOAINFO data) */ + udb_ptr_unlink(xfrd->last_task, xtask); + /* if shortsoa: then use my own taskdb that nsdparent filled */ + if(!shortsoa) + xfrd->nsd->mytask = 1 - xfrd->nsd->mytask; + xtask = xfrd->nsd->task[xfrd->nsd->mytask]; + task_remap(xtask); + udb_ptr_new(&t, xtask, udb_base_get_userdata(xtask)); + while(!udb_ptr_is_null(&t)) { + xfrd_process_soa_info_task(TASKLIST(&t)); + udb_ptr_set_rptr(&t, xtask, &TASKLIST(&t)->next); + } + udb_ptr_unlink(&t, xtask); + task_clear(xtask); + udb_ptr_init(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask]); + + if(!shortsoa) { + /* receive RELOAD_DONE that signals the other tasklist is + * empty, and thus xfrd can operate (can call reload and swap + * to the other, empty, tasklist) */ + if(block_read(NULL, socket, &cmd, sizeof(cmd), -1) != + sizeof(cmd) || + cmd != NSD_RELOAD_DONE) { + log_msg(LOG_ERR, "did not get start signal 2 from " + "main"); + exit(1); + } + } else { + /* for shortsoa version, do expire later */ + /* if expire notifications, put in my task and + * schedule a reload to make sure they are processed */ + RBTREE_FOR(zone, xfrd_zone_t*, xfrd->zones) { + if(zone->state == xfrd_zone_expired) { + xfrd_send_expire_notification(zone); + } + } } } void -xfrd_reopen_logfile() +xfrd_reopen_logfile(void) { if (xfrd->nsd->file_rotation_ok) log_reopen(xfrd->nsd->log_filename, 0); } -static void -xfrd_free_namedb() +void +xfrd_deactivate_zone(xfrd_zone_t* z) { - namedb_close(xfrd->nsd->db); - xfrd->nsd->db = 0; + if(z->is_activated) { + /* delete from activated list */ + if(z->activated_prev) + z->activated_prev->activated_next = z->activated_next; + else xfrd->activated_first = z->activated_next; + if(z->activated_next) + z->activated_next->activated_prev = z->activated_prev; + z->is_activated = 0; + } +} + +void +xfrd_del_slave_zone(xfrd_state_t* xfrd, const dname_type* dname) +{ + xfrd_zone_t* z = (xfrd_zone_t*)rbtree_delete(xfrd->zones, dname); + if(!z) return; + + /* io */ + if(z->tcp_waiting) { + /* delete from tcp waiting list */ + if(z->tcp_waiting_prev) + z->tcp_waiting_prev->tcp_waiting_next = + z->tcp_waiting_next; + else xfrd->tcp_set->tcp_waiting_first = z->tcp_waiting_next; + if(z->tcp_waiting_next) + z->tcp_waiting_next->tcp_waiting_prev = + z->tcp_waiting_prev; + else xfrd->tcp_set->tcp_waiting_last = z->tcp_waiting_prev; + z->tcp_waiting = 0; + } + if(z->udp_waiting) { + /* delete from udp waiting list */ + if(z->udp_waiting_prev) + z->udp_waiting_prev->udp_waiting_next = + z->udp_waiting_next; + else xfrd->udp_waiting_first = z->udp_waiting_next; + if(z->udp_waiting_next) + z->udp_waiting_next->udp_waiting_prev = + z->udp_waiting_prev; + else xfrd->udp_waiting_last = z->udp_waiting_prev; + z->udp_waiting = 0; + } + xfrd_deactivate_zone(z); + if(z->tcp_conn != -1) { + xfrd_tcp_release(xfrd->tcp_set, z); + } else if(z->zone_handler.ev_fd != -1 && z->event_added) { + xfrd_udp_release(z); + } else if(z->event_added) + event_del(&z->zone_handler); + if(z->msg_seq_nr) + xfrd_unlink_xfrfile(xfrd->nsd, z->xfrfilenumber); + + /* tsig */ + tsig_delete_record(&z->tsig, NULL); + + /* z->dname is recycled when the zone_options is removed */ + region_recycle(xfrd->region, z, sizeof(*z)); +} + +void +xfrd_free_namedb(struct nsd* nsd) +{ + namedb_close_udb(nsd->db); + namedb_close(nsd->db); + nsd->db = 0; } static void @@ -346,6 +645,9 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone) set_min = zone->soa_disk_acquired + XFRD_LOWERBOUND_REFRESH; if(set < set_min) set = set_min; + if(set < xfrd_time()) + set = 0; + else set -= xfrd_time(); xfrd_set_timer(zone, set); } @@ -356,10 +658,10 @@ xfrd_set_timer_retry(xfrd_zone_t* zone) if(zone->soa_disk_acquired == 0) { /* if no information, use reasonable timeout */ #ifdef HAVE_ARC4RANDOM - xfrd_set_timer(zone, xfrd_time() + zone->fresh_xfr_timeout + xfrd_set_timer(zone, zone->fresh_xfr_timeout + arc4random()%zone->fresh_xfr_timeout); #else - xfrd_set_timer(zone, xfrd_time() + zone->fresh_xfr_timeout + xfrd_set_timer(zone, zone->fresh_xfr_timeout + random()%zone->fresh_xfr_timeout); #endif /* exponential backoff - some master data in zones is paid-for @@ -372,46 +674,37 @@ xfrd_set_timer_retry(xfrd_zone_t* zone) zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire)) { if(ntohl(zone->soa_disk.retry) < XFRD_LOWERBOUND_RETRY) - xfrd_set_timer(zone, xfrd_time() + XFRD_LOWERBOUND_RETRY); + xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY); else - xfrd_set_timer(zone, xfrd_time() + ntohl(zone->soa_disk.retry)); + xfrd_set_timer(zone, ntohl(zone->soa_disk.retry)); } else { if(ntohl(zone->soa_disk.expire) < XFRD_LOWERBOUND_RETRY) - xfrd_set_timer(zone, xfrd_time() + XFRD_LOWERBOUND_RETRY); - else - xfrd_set_timer(zone, zone->soa_disk_acquired + - ntohl(zone->soa_disk.expire)); + xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY); + else { + if(zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire) < xfrd_time()) + xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY); + else xfrd_set_timer(zone, zone->soa_disk_acquired + + ntohl(zone->soa_disk.expire) - xfrd_time()); + } } } -static void -xfrd_handle_zone(netio_type* ATTR_UNUSED(netio), - netio_handler_type *handler, netio_event_types_type event_types) +void +xfrd_handle_zone(int ATTR_UNUSED(fd), short event, void* arg) { - xfrd_zone_t* zone = (xfrd_zone_t*)handler->user_data; + xfrd_zone_t* zone = (xfrd_zone_t*)arg; if(zone->tcp_conn != -1) { - /* busy in tcp transaction */ - if(xfrd_tcp_is_reading(xfrd->tcp_set, zone->tcp_conn) && event_types & NETIO_EVENT_READ) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event tcp read", zone->apex_str)); - xfrd_set_timer(zone, xfrd_time() + xfrd->tcp_set->tcp_timeout); - xfrd_tcp_read(xfrd->tcp_set, zone); - return; - } else if(!xfrd_tcp_is_reading(xfrd->tcp_set, zone->tcp_conn) && event_types & NETIO_EVENT_WRITE) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event tcp write", zone->apex_str)); - xfrd_set_timer(zone, xfrd_time() + xfrd->tcp_set->tcp_timeout); - xfrd_tcp_write(xfrd->tcp_set, zone); + if(event == 0) /* activated, but already in TCP, nothing to do*/ return; - } else if(event_types & NETIO_EVENT_TIMEOUT) { - /* tcp connection timed out. Stop it. */ - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event tcp timeout", zone->apex_str)); - xfrd_tcp_release(xfrd->tcp_set, zone); - /* continue to retry; as if a timeout happened */ - event_types = NETIO_EVENT_TIMEOUT; - } + /* busy in tcp transaction: an internal error */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event tcp", zone->apex_str)); + xfrd_tcp_release(xfrd->tcp_set, zone); + /* continue to retry; as if a timeout happened */ + event = EV_TIMEOUT; } - if(event_types & NETIO_EVENT_READ) { + if((event & EV_READ)) { /* busy in udp transaction */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event udp read", zone->apex_str)); xfrd_set_refresh_now(zone); @@ -421,7 +714,8 @@ xfrd_handle_zone(netio_type* ATTR_UNUSED(netio), /* timeout */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s timeout", zone->apex_str)); - if(handler->fd != -1) { + if(zone->zone_handler.ev_fd != -1 && zone->event_added && + (event & EV_TIMEOUT)) { assert(zone->tcp_conn == -1); xfrd_udp_release(zone); } @@ -454,8 +748,12 @@ xfrd_handle_zone(netio_type* ATTR_UNUSED(netio), xfrd_set_zone_state(zone, xfrd_zone_refreshing); } } - /* make a new request */ - xfrd_make_request(zone); + + /* only make a new request if no request is running (UDPorTCP) */ + if(zone->zone_handler.ev_fd == -1 && zone->tcp_conn == -1) { + /* make a new request */ + xfrd_make_request(zone); + } } void @@ -467,11 +765,11 @@ xfrd_make_request(xfrd_zone_t* zone) "xfrd zone %s use master %i", zone->apex_str, zone->next_master)); zone->master_num = zone->next_master; - zone->master = acl_find_num( - zone->zone_options->request_xfr, zone->master_num); + zone->master = acl_find_num(zone->zone_options->pattern-> + request_xfr, zone->master_num); /* if there is no next master, fallback to use the first one */ if(!zone->master) { - zone->master = zone->zone_options->request_xfr; + zone->master = zone->zone_options->pattern->request_xfr; zone->master_num = 0; } /* fallback to cycle master */ @@ -487,7 +785,7 @@ xfrd_make_request(xfrd_zone_t* zone) zone->master_num++; } else { /* start a new round */ - zone->master = zone->zone_options->request_xfr; + zone->master = zone->zone_options->pattern->request_xfr; zone->master_num = 0; zone->round_num++; } @@ -519,27 +817,27 @@ xfrd_make_request(xfrd_zone_t* zone) !zone->master->ixfr_disabled) { if (zone->master->allow_udp) { - xfrd_set_timer(zone, xfrd_time() + XFRD_UDP_TIMEOUT); + xfrd_set_timer(zone, XFRD_UDP_TIMEOUT); xfrd_udp_obtain(zone); } else { /* doing 3 rounds of IXFR/TCP might not be useful */ - xfrd_set_timer(zone, xfrd_time() + xfrd->tcp_set->tcp_timeout); + xfrd_set_timer(zone, xfrd->tcp_set->tcp_timeout); xfrd_tcp_obtain(xfrd->tcp_set, zone); } } else if (zone->master->use_axfr_only || zone->soa_disk_acquired <= 0) { - xfrd_set_timer(zone, xfrd_time() + xfrd->tcp_set->tcp_timeout); + xfrd_set_timer(zone, xfrd->tcp_set->tcp_timeout); xfrd_tcp_obtain(xfrd->tcp_set, zone); } else if (zone->master->ixfr_disabled) { - if (zone->zone_options->allow_axfr_fallback) { - xfrd_set_timer(zone, xfrd_time() + xfrd->tcp_set->tcp_timeout); + if (zone->zone_options->pattern->allow_axfr_fallback) { + xfrd_set_timer(zone, xfrd->tcp_set->tcp_timeout); xfrd_tcp_obtain(xfrd->tcp_set, zone); - } - else + } else { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s axfr " "fallback not allowed, skipping master %s.", zone->apex_str, zone->master->ip_address_spec)); + } } } @@ -552,15 +850,30 @@ xfrd_udp_obtain(xfrd_zone_t* zone) xfrd_tcp_release(xfrd->tcp_set, zone); } if(xfrd->udp_use_num < XFRD_MAX_UDP) { + int fd; xfrd->udp_use_num++; - zone->zone_handler.fd = xfrd_send_ixfr_request_udp(zone); - if(zone->zone_handler.fd == -1) + fd = xfrd_send_ixfr_request_udp(zone); + if(fd == -1) xfrd->udp_use_num--; + else { + if(zone->event_added) + event_del(&zone->zone_handler); + event_set(&zone->zone_handler, fd, + EV_PERSIST|EV_READ|EV_TIMEOUT, + xfrd_handle_zone, zone); + if(event_base_set(xfrd->event_base, &zone->zone_handler) != 0) + log_msg(LOG_ERR, "xfrd udp: event_base_set failed"); + if(event_add(&zone->zone_handler, &zone->timeout) != 0) + log_msg(LOG_ERR, "xfrd udp: event_add failed"); + zone->zone_handler_flags=EV_PERSIST|EV_READ|EV_TIMEOUT; + zone->event_added = 1; + } return; } /* queue the zone as last */ zone->udp_waiting = 1; zone->udp_waiting_next = NULL; + zone->udp_waiting_prev = xfrd->udp_waiting_last; if(!xfrd->udp_waiting_first) xfrd->udp_waiting_first = zone; if(xfrd->udp_waiting_last) @@ -593,7 +906,7 @@ xfrd_copy_soa(xfrd_soa_t* soa, rr_type* rr) return; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: copy_soa rr, type %d rrs %u, ttl %u.", - rr->type, rr->rdata_count, rr->ttl)); + (int)rr->type, (unsigned)rr->rdata_count, (unsigned)rr->ttl)); soa->type = htons(rr->type); soa->klass = htons(rr->klass); soa->ttl = htonl(rr->ttl); @@ -613,8 +926,8 @@ xfrd_copy_soa(xfrd_soa_t* soa, rr_type* rr) memcpy(&soa->minimum, rdata_atom_data(rr->rdatas[6]), sizeof(uint32_t)); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: copy_soa rr, serial %u refresh %u retry %u expire %u", - ntohl(soa->serial), ntohl(soa->refresh), ntohl(soa->retry), - ntohl(soa->expire))); + (unsigned)ntohl(soa->serial), (unsigned)ntohl(soa->refresh), + (unsigned)ntohl(soa->retry), (unsigned)ntohl(soa->expire))); } static void @@ -623,7 +936,8 @@ xfrd_set_zone_state(xfrd_zone_t* zone, enum xfrd_zone_state s) if(s != zone->state) { enum xfrd_zone_state old = zone->state; zone->state = s; - if(s == xfrd_zone_expired || old == xfrd_zone_expired) { + if((s == xfrd_zone_expired || old == xfrd_zone_expired) + && s!=old) { xfrd_send_expire_notification(zone); } } @@ -632,36 +946,59 @@ xfrd_set_zone_state(xfrd_zone_t* zone, enum xfrd_zone_state s) void xfrd_set_refresh_now(xfrd_zone_t* zone) { - xfrd_set_timer(zone, xfrd_time()); - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s sets timeout right now, state %d", + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s is activated, state %d", zone->apex_str, zone->state)); + if(!zone->is_activated) { + /* push onto list */ + zone->activated_prev = 0; + zone->activated_next = xfrd->activated_first; + if(xfrd->activated_first) + xfrd->activated_first->activated_prev = zone; + xfrd->activated_first = zone; + zone->is_activated = 1; + } } void xfrd_unset_timer(xfrd_zone_t* zone) { - zone->zone_handler.timeout = NULL; + assert(zone->zone_handler.ev_fd == -1); + if(zone->event_added) + event_del(&zone->zone_handler); + zone->zone_handler_flags = 0; + zone->event_added = 0; } void xfrd_set_timer(xfrd_zone_t* zone, time_t t) { + int fd = zone->zone_handler.ev_fd; + int fl = ((fd == -1)?EV_TIMEOUT:zone->zone_handler_flags); /* randomize the time, within 90%-100% of original */ /* not later so zones cannot expire too late */ /* only for times far in the future */ - if(t > xfrd_time() + 10) { - time_t extra = t - xfrd_time(); - time_t base = extra*9/10; + if(t > 10) { + time_t base = t*9/10; #ifdef HAVE_ARC4RANDOM - t = xfrd_time() + base + arc4random()%(extra-base); + t = base + arc4random()%(t-base); #else - t = xfrd_time() + base + random()%(extra-base); + t = base + random()%(t-base); #endif } - zone->zone_handler.timeout = &zone->timeout; + /* keep existing flags and fd, but re-add with timeout */ + if(zone->event_added) + event_del(&zone->zone_handler); + else fd = -1; zone->timeout.tv_sec = t; - zone->timeout.tv_nsec = 0; + zone->timeout.tv_usec = 0; + event_set(&zone->zone_handler, fd, fl, xfrd_handle_zone, zone); + if(event_base_set(xfrd->event_base, &zone->zone_handler) != 0) + log_msg(LOG_ERR, "xfrd timer: event_base_set failed"); + if(event_add(&zone->zone_handler, &zone->timeout) != 0) + log_msg(LOG_ERR, "xfrd timer: event_add failed"); + zone->zone_handler_flags = fl; + zone->event_added = 1; } void @@ -675,12 +1012,9 @@ xfrd_handle_incoming_soa(xfrd_zone_t* zone, xfrd_set_refresh_now(zone); return; } - if(zone->soa_nsd_acquired && soa->serial == zone->soa_nsd.serial) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s has already been updated " - "to serial %u (at time %u)", zone->apex_str, - ntohl(zone->soa_nsd.serial), (unsigned) zone->soa_nsd_acquired)); + if(zone->soa_nsd_acquired && soa->serial == zone->soa_nsd.serial) return; - } + if(zone->soa_disk_acquired && soa->serial == zone->soa_disk.serial) { /* soa in disk has been loaded in memory */ @@ -750,13 +1084,9 @@ xfrd_handle_incoming_soa(xfrd_zone_t* zone, static void xfrd_send_expire_notification(xfrd_zone_t* zone) { - if(zone->dirty) - return; /* already queued */ - /* enqueue */ - assert(xfrd->dirty_zones->num < xfrd->dirty_zones->capacity); - zone->dirty = 1; - stack_push(xfrd->dirty_zones, (void*)zone); - xfrd->ipc_handler.event_types |= NETIO_EVENT_WRITE; + task_new_expire(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, + zone->apex, zone->state == xfrd_zone_expired); + xfrd_set_reload_timeout(); } int @@ -781,9 +1111,14 @@ void xfrd_udp_release(xfrd_zone_t* zone) { assert(zone->udp_waiting == 0); - if(zone->zone_handler.fd != -1) - close(zone->zone_handler.fd); - zone->zone_handler.fd = -1; + if(zone->event_added) + event_del(&zone->zone_handler); + if(zone->zone_handler.ev_fd != -1) { + close(zone->zone_handler.ev_fd); + } + zone->zone_handler.ev_fd = -1; + zone->zone_handler_flags = 0; + zone->event_added = 0; /* see if there are waiting zones */ if(xfrd->udp_use_num == XFRD_MAX_UDP) { @@ -793,14 +1128,32 @@ xfrd_udp_release(xfrd_zone_t* zone) assert(wz->udp_waiting); wz->udp_waiting = 0; xfrd->udp_waiting_first = wz->udp_waiting_next; + if(wz->udp_waiting_next) + wz->udp_waiting_next->udp_waiting_prev = NULL; if(xfrd->udp_waiting_last == wz) xfrd->udp_waiting_last = NULL; /* see if this zone needs udp connection */ if(wz->tcp_conn == -1) { - wz->zone_handler.fd = - xfrd_send_ixfr_request_udp(wz); - if(wz->zone_handler.fd != -1) + int fd = xfrd_send_ixfr_request_udp(wz); + if(fd != -1) { + if(wz->event_added) + event_del(&wz->zone_handler); + event_set(&wz->zone_handler, fd, + EV_READ|EV_TIMEOUT|EV_PERSIST, + xfrd_handle_zone, wz); + if(event_base_set(xfrd->event_base, + &wz->zone_handler) != 0) + log_msg(LOG_ERR, "cannot set event_base for ixfr"); + if(event_add(&wz->zone_handler, &wz->timeout) != 0) + log_msg(LOG_ERR, "cannot add event for ixfr"); + wz->zone_handler_flags = EV_READ|EV_TIMEOUT|EV_PERSIST; + wz->event_added = 1; return; + } else { + /* make this zone do something with + * this failure to act */ + xfrd_set_refresh_now(wz); + } } } } @@ -813,13 +1166,13 @@ static void xfrd_udp_read(xfrd_zone_t* zone) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s read udp data", zone->apex_str)); - if(!xfrd_udp_read_packet(xfrd->packet, zone->zone_handler.fd)) { + if(!xfrd_udp_read_packet(xfrd->packet, zone->zone_handler.ev_fd)) { xfrd_udp_release(zone); return; } switch(xfrd_handle_received_xfr_packet(zone, xfrd->packet)) { case xfrd_packet_tcp: - xfrd_set_timer(zone, xfrd_time() + xfrd->tcp_set->tcp_timeout); + xfrd_set_timer(zone, xfrd->tcp_set->tcp_timeout); xfrd_udp_release(zone); xfrd_tcp_obtain(xfrd->tcp_set, zone); break; @@ -1028,8 +1381,12 @@ xfrd_send_ixfr_request_udp(xfrd_zone_t* zone) zone->apex_str); return -1; } - xfrd_setup_packet(xfrd->packet, TYPE_IXFR, CLASS_IN, zone->apex); + xfrd_setup_packet(xfrd->packet, TYPE_IXFR, CLASS_IN, zone->apex, + qid_generate()); zone->query_id = ID(xfrd->packet); + /* delete old xfr file? */ + if(zone->msg_seq_nr) + xfrd_unlink_xfrfile(xfrd->nsd, zone->xfrfilenumber); zone->msg_seq_nr = 0; zone->msg_rr_count = 0; DEBUG(DEBUG_XFRD,1, (LOG_INFO, "sent query with ID %d", zone->query_id)); @@ -1040,10 +1397,10 @@ xfrd_send_ixfr_request_udp(xfrd_zone_t* zone) xfrd_tsig_sign_request(xfrd->packet, &zone->tsig, zone->master); } buffer_flip(xfrd->packet); - xfrd_set_timer(zone, xfrd_time() + XFRD_UDP_TIMEOUT); + xfrd_set_timer(zone, XFRD_UDP_TIMEOUT); if((fd = xfrd_send_udp(zone->master, xfrd->packet, - zone->zone_options->outgoing_interface)) == -1) + zone->zone_options->pattern->outgoing_interface)) == -1) return -1; DEBUG(DEBUG_XFRD,1, (LOG_INFO, @@ -1091,27 +1448,30 @@ static int xfrd_parse_soa_info(buffer_type* packet, xfrd_soa_t* soa) */ static int xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, - int *done, xfrd_soa_t* soa) + int *done, xfrd_soa_t* soa, region_type* temp) { /* first RR has already been checked */ uint32_t tmp_serial = 0; uint16_t type, rrlen; - size_t i, soapos; + size_t i, soapos, mempos; + const dname_type* dname; + domain_table_type* owners; + rdata_atom_type* rdatas; for(i=0; i<count; ++i,++zone->msg_rr_count) { if (*done) { - /** - * We are done, but there are more RRs coming. Ignore - * trailing garbage. - */ - DEBUG(DEBUG_XFRD,1, (LOG_WARNING, "xfrd: zone %s xfr is " - "done, ignore trailing garbage", zone->apex_str)); - return 1; + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr has " + "trailing garbage", zone->apex_str)); + return 0; } - if(!packet_skip_dname(packet)) { + region_free_all(temp); + owners = domain_table_create(temp); + /* check the dname for errors */ + dname = dname_make_from_packet(temp, packet, 1, 1); + if(!dname) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr unable " - "to skip owner name", zone->apex_str)); + "to parse owner name", zone->apex_str)); return 0; } if(!buffer_available(packet, 10)) { @@ -1129,9 +1489,15 @@ xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, "too small", zone->apex_str)); return 0; } + mempos = buffer_position(packet); + if(rdata_wireformat_to_rdata_atoms(temp, owners, type, rrlen, + packet, &rdatas) == -1) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr unable " + "to parse rdata", zone->apex_str)); + return 0; + } if(type == TYPE_SOA) { /* check the SOAs */ - size_t mempos = buffer_position(packet); buffer_set_position(packet, soapos); if(!xfrd_parse_soa_info(packet, soa)) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " @@ -1171,17 +1537,16 @@ xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, "bad middle serial", zone->apex_str)); return 0; /* bad middle serial in IXFR */ } - if(ntohl(soa->serial) < tmp_serial) { + if(ntohl(soa->serial) < tmp_serial) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " "serial decreasing not allowed", zone->apex_str)); return 0; /* middle serial decreases in IXFR */ } - /** serial ok, update tmp serial */ + /* serial ok, update tmp serial */ tmp_serial = ntohl(soa->serial); - } - buffer_set_position(packet, mempos); } + buffer_set_position(packet, mempos); buffer_skip(packet, rrlen); } /* packet seems to have a valid DNS RR structure */ @@ -1253,6 +1618,7 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, size_t ancount = ANCOUNT(packet), ancount_todo; size_t nscount = NSCOUNT(packet); int done = 0; + region_type* tempregion = NULL; /* has to be axfr / ixfr reply */ if(!buffer_available(packet, QHEADERSZ)) { @@ -1330,15 +1696,32 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, } ancount_todo = ancount; + tempregion = region_create(xalloc, free); if(zone->msg_rr_count == 0) { + const dname_type* soaname = dname_make_from_packet(tempregion, + packet, 1, 1); + if(!soaname) { /* parse failure */ + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s, from %s: " + "parse error in SOA record", + zone->apex_str, zone->master->ip_address_spec)); + region_destroy(tempregion); + return xfrd_packet_bad; + } + if(dname_compare(soaname, zone->apex) != 0) { /* wrong name */ + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s, from %s: " + "wrong SOA record", + zone->apex_str, zone->master->ip_address_spec)); + region_destroy(tempregion); + return xfrd_packet_bad; + } + /* parse the first RR, see if it is a SOA */ - if(!packet_skip_dname(packet) || - !xfrd_parse_soa_info(packet, soa)) + if(!xfrd_parse_soa_info(packet, soa)) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s, from %s: " - "no SOA begins answer" - " section", + "bad SOA rdata", zone->apex_str, zone->master->ip_address_spec)); + region_destroy(tempregion); return xfrd_packet_bad; } if(zone->soa_disk_acquired != 0 && @@ -1350,6 +1733,7 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, VERBOSITY(1, (LOG_INFO, "xfrd: zone %s ignoring old serial from %s", zone->apex_str, zone->master->ip_address_spec)); + region_destroy(tempregion); return xfrd_packet_bad; } if(zone->soa_disk_acquired != 0 && zone->soa_disk.serial == soa->serial) { @@ -1368,9 +1752,11 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, /* not notified or anything, so stop asking around */ zone->round_num = -1; /* next try start a new round */ xfrd_set_timer_refresh(zone); + region_destroy(tempregion); return xfrd_packet_newlease; } /* try next master */ + region_destroy(tempregion); return xfrd_packet_drop; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "IXFR reply has ok serial (have \ @@ -1398,6 +1784,7 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s received TC from %s. retry tcp.", zone->apex_str, zone->master->ip_address_spec)); + region_destroy(tempregion); return xfrd_packet_tcp; } @@ -1407,15 +1794,19 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, /* The serial is newer, so try tcp to this master. */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: udp reply is short. Try " "tcp anyway.")); + region_destroy(tempregion); return xfrd_packet_tcp; } - if(!xfrd_xfr_check_rrs(zone, packet, ancount_todo, &done, soa)) + if(!xfrd_xfr_check_rrs(zone, packet, ancount_todo, &done, soa, + tempregion)) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s sent bad xfr " "reply.", zone->apex_str)); + region_destroy(tempregion); return xfrd_packet_bad; } + region_destroy(tempregion); if(zone->tcp_conn == -1 && done == 0) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: udp reply incomplete")); return xfrd_packet_bad; @@ -1432,6 +1823,16 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, return xfrd_packet_transfer; } +const char* +xfrd_pretty_time(time_t v) +{ + struct tm* tm = localtime(&v); + static char buf[64]; + if(!strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", tm)) + snprintf(buf, sizeof(buf), "strftime-err-%u", (unsigned)v); + return buf; +} + enum xfrd_packet_result xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) { @@ -1457,30 +1858,20 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) /* rollback */ if(zone->msg_seq_nr > 0) { /* do not process xfr - if only one part simply ignore it. */ - /* rollback previous parts of commit */ - buffer_clear(packet); - buffer_printf(packet, "xfrd: zone %s xfr " - "rollback serial %u at " - "time %lld from %s of %u " - "parts", - zone->apex_str, - (int)zone->msg_new_serial, - (long long)xfrd_time(), - zone->master->ip_address_spec, - zone->msg_seq_nr); - - buffer_flip(packet); - diff_write_commit(zone->apex_str, - zone->msg_old_serial, - zone->msg_new_serial, - zone->query_id, zone->msg_seq_nr, 0, - (char*)buffer_begin(packet), - xfrd->nsd->options); - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s " - "xfr reverted " - "\"%s\"", - zone->apex_str, - (char*)buffer_begin(packet))); + /* delete file with previous parts of commit */ + xfrd_unlink_xfrfile(xfrd->nsd, zone->xfrfilenumber); + VERBOSITY(1, (LOG_INFO, "xfrd: zone %s " + "reverted transfer %u from %s", + zone->apex_str, zone->msg_rr_count? + (int)zone->msg_new_serial:0, + zone->master->ip_address_spec)); + zone->msg_seq_nr = 0; + } else if (res == xfrd_packet_bad) { + VERBOSITY(1, (LOG_INFO, "xfrd: zone %s " + "bad transfer %u from %s", + zone->apex_str, zone->msg_rr_count? + (int)zone->msg_new_serial:0, + zone->master->ip_address_spec)); } if (res == xfrd_packet_notimpl) return res; @@ -1490,11 +1881,17 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) } /* dump reply on disk to diff file */ - diff_write_packet(zone->apex_str, zone->msg_new_serial, zone->query_id, - zone->msg_seq_nr, buffer_begin(packet), buffer_limit(packet), - xfrd->nsd->options); - VERBOSITY(1, (LOG_INFO, - "xfrd: zone %s written received XFR from %s with serial %u to " + /* if first part, get new filenumber. Numbers can wrap around, 64bit + * is enough so we do not collide with older-transfers-in-progress */ + if(zone->msg_seq_nr == 0) + zone->xfrfilenumber = xfrd->xfrfilenumber++; + diff_write_packet(dname_to_string(zone->apex,0), + zone->zone_options->pattern->pname, + zone->msg_old_serial, zone->msg_new_serial, zone->msg_seq_nr, + buffer_begin(packet), buffer_limit(packet), xfrd->nsd, + zone->xfrfilenumber); + VERBOSITY(3, (LOG_INFO, + "xfrd: zone %s written received XFR packet from %s with serial %u to " "disk", zone->apex_str, zone->master->ip_address_spec, (int)zone->msg_new_serial)); zone->msg_seq_nr++; @@ -1505,21 +1902,30 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) /* done. we are completely sure of this */ buffer_clear(packet); - buffer_printf(packet, "xfrd: zone %s received update to serial %u at " - "time %lld from %s in %u parts", - zone->apex_str, (int)zone->msg_new_serial, - (long long)xfrd_time(), - zone->master->ip_address_spec, zone->msg_seq_nr); + buffer_printf(packet, "received update to serial %u at %s from %s", + (unsigned)zone->msg_new_serial, xfrd_pretty_time(xfrd_time()), + zone->master->ip_address_spec); if(zone->master->key_options) { buffer_printf(packet, " TSIG verified with key %s", zone->master->key_options->name); } buffer_flip(packet); diff_write_commit(zone->apex_str, zone->msg_old_serial, - zone->msg_new_serial, zone->query_id, zone->msg_seq_nr, 1, - (char*)buffer_begin(packet), xfrd->nsd->options); + zone->msg_new_serial, zone->msg_seq_nr, 1, + (char*)buffer_begin(packet), xfrd->nsd, zone->xfrfilenumber); VERBOSITY(1, (LOG_INFO, "xfrd: zone %s committed \"%s\"", zone->apex_str, (char*)buffer_begin(packet))); + /* reset msg seq nr, so if that is nonnull we know xfr file exists */ + zone->msg_seq_nr = 0; + /* now put apply_xfr task on the tasklist */ + if(!task_new_apply_xfr(xfrd->nsd->task[xfrd->nsd->mytask], + xfrd->last_task, zone->apex, zone->msg_old_serial, + zone->msg_new_serial, zone->xfrfilenumber)) { + /* delete the file and pretend transfer was bad to continue */ + xfrd_unlink_xfrfile(xfrd->nsd, zone->xfrfilenumber); + xfrd_set_reload_timeout(); + return xfrd_packet_bad; + } /* update the disk serial no. */ zone->soa_disk_acquired = xfrd_time(); zone->soa_disk = soa; @@ -1562,30 +1968,52 @@ xfrd_set_reload_timeout() if(xfrd->reload_timeout.tv_sec == 0 || xfrd_time() >= (time_t)xfrd->reload_timeout.tv_sec ) { /* no reload wait period (or it passed), do it right away */ - xfrd->need_to_send_reload = 1; - xfrd->ipc_handler.event_types |= NETIO_EVENT_WRITE; + xfrd_set_reload_now(xfrd); /* start reload wait period */ xfrd->reload_timeout.tv_sec = xfrd_time() + xfrd->nsd->options->xfrd_reload_timeout; - xfrd->reload_timeout.tv_nsec = 0; + xfrd->reload_timeout.tv_usec = 0; return; } /* cannot reload now, set that after the timeout a reload has to happen */ - xfrd->reload_handler.timeout = &xfrd->reload_timeout; + if(xfrd->reload_added == 0) { + struct timeval tv; + tv.tv_sec = xfrd->reload_timeout.tv_sec - xfrd_time(); + tv.tv_usec = 0; + if(tv.tv_sec > xfrd->nsd->options->xfrd_reload_timeout) + tv.tv_sec = xfrd->nsd->options->xfrd_reload_timeout; + event_set(&xfrd->reload_handler, -1, EV_TIMEOUT, + xfrd_handle_reload, xfrd); + if(event_base_set(xfrd->event_base, &xfrd->reload_handler) != 0) + log_msg(LOG_ERR, "cannot set reload event base"); + if(event_add(&xfrd->reload_handler, &tv) != 0) + log_msg(LOG_ERR, "cannot add reload event"); + xfrd->reload_added = 1; + } } static void -xfrd_handle_reload(netio_type *ATTR_UNUSED(netio), - netio_handler_type *handler, netio_event_types_type event_types) +xfrd_handle_reload(int ATTR_UNUSED(fd), short event, void* ATTR_UNUSED(arg)) { /* reload timeout */ - assert(event_types & NETIO_EVENT_TIMEOUT); + assert(event & EV_TIMEOUT); + (void)event; /* timeout wait period after this request is sent */ - handler->timeout = NULL; + xfrd->reload_added = 0; xfrd->reload_timeout.tv_sec = xfrd_time() + xfrd->nsd->options->xfrd_reload_timeout; - xfrd->need_to_send_reload = 1; - xfrd->ipc_handler.event_types |= NETIO_EVENT_WRITE; + xfrd_set_reload_now(xfrd); +} + +void +xfrd_handle_notify_and_start_xfr(xfrd_zone_t* zone, xfrd_soa_t* soa) +{ + if(xfrd_handle_incoming_notify(zone, soa)) { + if(zone->zone_handler.ev_fd == -1 && zone->tcp_conn == -1 && + !zone->tcp_waiting && !zone->udp_waiting) { + xfrd_set_refresh_now(zone); + } + } } void @@ -1606,13 +2034,14 @@ xfrd_handle_passed_packet(buffer_type* packet, dname = dname_make(tempregion, qnamebuf, 1); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: got passed packet for %s, acl " - "%d", dname_to_string(dname,0), acl_num)); + "%d", dname_to_string(dname,0), acl_num)); /* find the zone */ zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname); if(!zone) { - log_msg(LOG_INFO, "xfrd: incoming packet for unknown zone %s", - dname_to_string(dname,0)); + /* this could be because the zone has been deleted meanwhile */ + DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "xfrd: incoming packet for " + "unknown zone %s", dname_to_string(dname,0))); region_destroy(tempregion); return; /* drop packet for unknown zone */ } @@ -1628,15 +2057,10 @@ xfrd_handle_passed_packet(buffer_type* packet, xfrd_parse_soa_info(packet, &soa)) { have_soa = 1; } - if(xfrd_handle_incoming_notify(zone, have_soa?&soa:NULL)) { - if(zone->zone_handler.fd == -1 - && zone->tcp_conn == -1 && - !zone->tcp_waiting && !zone->udp_waiting) { - xfrd_set_refresh_now(zone); - } - } + xfrd_handle_notify_and_start_xfr(zone, have_soa?&soa:NULL); /* First, see if our notifier has a match in provide-xfr */ - if (acl_find_num(zone->zone_options->request_xfr, acl_num_xfr)) + if (acl_find_num(zone->zone_options->pattern->request_xfr, + acl_num_xfr)) next = acl_num_xfr; else /* If not, find master that matches notifiers ACL entry */ next = find_same_master_notify(zone, acl_num); @@ -1648,7 +2072,7 @@ xfrd_handle_passed_packet(buffer_type* packet, } } else { - /* TODO handle incoming IXFR udp reply via port 53 */ + /* ignore other types of messages */ } } @@ -1680,7 +2104,7 @@ xfrd_handle_incoming_notify(xfrd_zone_t* zone, xfrd_soa_t* soa) xfrd_set_zone_state(zone, xfrd_zone_refreshing); } /* transfer right away */ - VERBOSITY(1, (LOG_INFO, "Handle incoming notify for zone %s", + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Handle incoming notify for zone %s", zone->apex_str)); return 1; } @@ -1688,10 +2112,10 @@ xfrd_handle_incoming_notify(xfrd_zone_t* zone, xfrd_soa_t* soa) static int find_same_master_notify(xfrd_zone_t* zone, int acl_num_nfy) { - acl_options_t* nfy_acl = acl_find_num( - zone->zone_options->allow_notify, acl_num_nfy); + acl_options_t* nfy_acl = acl_find_num(zone->zone_options->pattern-> + allow_notify, acl_num_nfy); int num = 0; - acl_options_t* master = zone->zone_options->request_xfr; + acl_options_t* master = zone->zone_options->pattern->request_xfr; if(!nfy_acl) return -1; while(master) @@ -1723,15 +2147,9 @@ xfrd_check_failed_updates() soa time is before the time of the reload cmd. */ xfrd_soa_t dumped_soa = zone->soa_disk; log_msg(LOG_ERR, "xfrd: zone %s: soa serial %u " - "update failed (acquired: %u), restarting " - "transfer (notified zone)", - zone->apex_str, ntohl(zone->soa_disk.serial), - (unsigned) zone->soa_disk_acquired); - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: nsd has " - "soa serial %u (acquired: %u, reload cmd sent: " - "%u)", zone->apex_str, ntohl(zone->soa_nsd.serial), - (unsigned) zone->soa_nsd_acquired, - (unsigned) xfrd->reload_cmd_last_sent)); + "update failed, restarting " + "transfer (notified zone)", + zone->apex_str, (unsigned)ntohl(zone->soa_disk.serial)); /* revert the soa; it has not been acquired properly */ zone->soa_disk_acquired = zone->soa_nsd_acquired; zone->soa_disk = zone->soa_nsd; @@ -1743,10 +2161,10 @@ xfrd_check_failed_updates() /* this zone still has to be loaded, make sure reload is set to be sent. */ if(xfrd->need_to_send_reload == 0 && - xfrd->reload_handler.timeout == NULL) { + xfrd->reload_added == 0) { log_msg(LOG_ERR, "xfrd: zone %s: needs " - "to be loaded. reload lost? " - "try again", zone->apex_str); + "to be loaded. reload lost? " + "try again", zone->apex_str); xfrd_set_reload_timeout(); } } @@ -1782,3 +2200,65 @@ xfrd_get_temp_buffer() { return xfrd->packet; } + +#ifdef BIND8_STATS +/** process stat info task */ +static void +xfrd_process_stat_info_task(xfrd_state_t* xfrd, struct task_list_d* task) +{ + size_t i; + stc_t* p = (void*)task->zname + sizeof(struct nsdst); + stats_add(&xfrd->nsd->st, (struct nsdst*)task->zname); + for(i=0; i<xfrd->nsd->child_count; i++) { + xfrd->nsd->children[i].query_count += *p++; + } + /* got total, now see if users are interested in these statistics */ +#ifdef HAVE_SSL + daemon_remote_process_stats(xfrd->nsd->rc); +#endif +} +#endif /* BIND8_STATS */ + +static void +xfrd_handle_taskresult(xfrd_state_t* xfrd, struct task_list_d* task) +{ + switch(task->task_type) { + case task_soa_info: + xfrd_process_soa_info_task(task); + break; +#ifdef BIND8_STATS + case task_stat_info: + xfrd_process_stat_info_task(xfrd, task); + break; +#endif /* BIND8_STATS */ + default: + log_msg(LOG_WARNING, "unhandled task result in xfrd from " + "reload type %d", (int)task->task_type); + } +} + +void xfrd_process_task_result(xfrd_state_t* xfrd, struct udb_base* taskudb) +{ + udb_ptr t; + /* remap it for usage */ + task_remap(taskudb); + /* process the task-results in the taskudb */ + udb_ptr_new(&t, taskudb, udb_base_get_userdata(taskudb)); + while(!udb_ptr_is_null(&t)) { + xfrd_handle_taskresult(xfrd, TASKLIST(&t)); + udb_ptr_set_rptr(&t, taskudb, &TASKLIST(&t)->next); + } + udb_ptr_unlink(&t, taskudb); + /* clear the udb so it can be used by xfrd to make new tasks for + * reload, this happens when the reload signal is sent, and thus + * the taskudbs are swapped */ + task_clear(taskudb); +} + +void xfrd_set_reload_now(xfrd_state_t* xfrd) +{ + xfrd->need_to_send_reload = 1; + if(!(xfrd->ipc_handler_flags&EV_WRITE)) { + ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE); + } +} diff --git a/usr.sbin/nsd/zonec.c b/usr.sbin/nsd/zonec.c index 21d437081cc..ca06832e237 100644 --- a/usr.sbin/nsd/zonec.c +++ b/usr.sbin/nsd/zonec.c @@ -1,7 +1,7 @@ /* * zonec.c -- zone compiler. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -22,6 +22,9 @@ #include <unistd.h> #include <stdlib.h> #include <time.h> +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif #include <netinet/in.h> @@ -37,6 +40,7 @@ #include "rdata.h" #include "region-allocator.h" #include "util.h" +#include "zparser.h" #include "options.h" #include "nsec3.h" @@ -46,16 +50,7 @@ const dname_type *error_dname; domain_type *error_domain; -/* The database file... */ -static const char *dbfile = 0; - -/* Some global flags... */ -static int vflag = 0; -/* if -v then print progress each 'progress' RRs */ -static int progress = 10000; - -/* Total errors counter */ -static long int totalerrors = 0; +static time_t startzonec = 0; static long int totalrrs = 0; extern uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE]; @@ -385,6 +380,7 @@ zparser_conv_aaaa(region_type *region, const char *text) return r; } + uint16_t * zparser_conv_ilnp64(region_type *region, const char *text) { @@ -1151,6 +1147,7 @@ zadd_rdata_txt_clean_wireformat() if ((tmp_data = (uint16_t *) region_alloc(parser->region, rd->data[0] + 2)) != NULL) { memcpy(tmp_data, rd->data, rd->data[0] + 2); + /* rd->data of u16+65535 freed when rr_region is freed */ rd->data = tmp_data; } else { @@ -1168,6 +1165,7 @@ zadd_rdata_domain(domain_type *domain) } else { parser->current_rr.rdatas[parser->current_rr.rdata_count].domain = domain; + domain->usage ++; /* new reference to domain */ ++parser->current_rr.rdata_count; } } @@ -1238,6 +1236,14 @@ zrdatacmp(uint16_t type, rr_type *a, rr_type *b) { return 1; } + } else if(rdata_atom_is_literal_domain(type, i)) { + if (rdata_atom_size(a->rdatas[i]) + != rdata_atom_size(b->rdatas[i])) + return 1; + if (!dname_equal_nocase(rdata_atom_data(a->rdatas[i]), + rdata_atom_data(b->rdatas[i]), + rdata_atom_size(a->rdatas[i]))) + return 1; } else { if (rdata_atom_size(a->rdatas[i]) != rdata_atom_size(b->rdatas[i])) @@ -1279,10 +1285,6 @@ zone_open(const char *filename, uint32_t ttl, uint16_t klass, return 0; } - /* Open the network database */ - setprotoent(1); - setservent(1); - zparser_init(filename, ttl, klass, origin); return 1; @@ -1304,13 +1306,15 @@ set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE], } -static void -cleanup_rrset(void *r) +static int +has_soa(domain_type* domain) { - rrset_type *rrset = (rrset_type *) r; - if (rrset) { - free(rrset->rrs); - } + rrset_type* p = NULL; + if(!domain) return 0; + for(p = domain->rrsets; p; p = p->next) + if(rrset_rrtype(p) == TYPE_SOA) + return 1; + return 0; } int @@ -1326,7 +1330,7 @@ process_rr(void) /* We only support IN class */ if (rr->klass != CLASS_IN) { - if(zone->opts && zone->opts->request_xfr) + if(zone_is_slave(zone->opts)) zc_warning_prev_line("only class IN is supported"); else zc_error_prev_line("only class IN is supported"); @@ -1341,48 +1345,27 @@ process_rr(void) zc_error_prev_line("maximum rdata length exceeds %d octets", MAX_RDLENGTH); return 0; } - - /* Do we have the zone already? */ - if (!zone) - { - zone = (zone_type *) region_alloc(parser->region, - sizeof(zone_type)); - zone->apex = parser->default_apex; - zone->soa_rrset = NULL; - zone->soa_nx_rrset = NULL; - zone->ns_rrset = NULL; - zone->opts = NULL; - zone->is_secure = 0; - zone->updated = 1; - - zone->next = parser->db->zones; - parser->db->zones = zone; - parser->current_zone = zone; - } - + /* we have the zone already */ + assert(zone); if (rr->type == TYPE_SOA) { - /* - * This is a SOA record, start a new zone or continue - * an existing one. - */ - if (rr->owner->is_apex) { - if(zone->opts && zone->opts->request_xfr) + if (rr->owner != zone->apex) { + zc_error_prev_line( + "SOA record with invalid domain name"); + return 0; + } + if(has_soa(rr->owner)) { + if(zone_is_slave(zone->opts)) zc_warning_prev_line("this SOA record was already encountered"); else zc_error_prev_line("this SOA record was already encountered"); - } else if (rr->owner == parser->default_apex) { - zone->apex = rr->owner; - rr->owner->is_apex = 1; + return 0; } - - /* parser part */ - parser->current_zone = zone; + rr->owner->is_apex = 1; } - if (!dname_is_subdomain(domain_dname(rr->owner), - domain_dname(zone->apex))) + if (!domain_is_subdomain(rr->owner, zone->apex)) { - if(zone->opts && zone->opts->request_xfr) + if(zone_is_slave(zone->opts)) zc_warning_prev_line("out of zone data"); else zc_error_prev_line("out of zone data"); @@ -1396,14 +1379,14 @@ process_rr(void) sizeof(rrset_type)); rrset->zone = zone; rrset->rr_count = 1; - rrset->rrs = (rr_type *) xalloc(sizeof(rr_type)); + rrset->rrs = (rr_type *) region_alloc(parser->region, + sizeof(rr_type)); rrset->rrs[0] = *rr; - region_add_cleanup(parser->region, cleanup_rrset, rrset); - /* Add it */ domain_add_rrset(rr->owner, rrset); } else { + rr_type* o; if (rr->type != TYPE_RRSIG && rrset->rrs[0].ttl != rr->ttl) { zc_warning_prev_line( "TTL does not match the TTL of the RRset"); @@ -1422,69 +1405,56 @@ process_rr(void) } /* Add it... */ - rrset->rrs = (rr_type *) xrealloc( - rrset->rrs, + o = rrset->rrs; + rrset->rrs = (rr_type *) region_alloc(parser->region, (rrset->rr_count + 1) * sizeof(rr_type)); + memcpy(rrset->rrs, o, (rrset->rr_count) * sizeof(rr_type)); + region_recycle(parser->region, o, + (rrset->rr_count) * sizeof(rr_type)); rrset->rrs[rrset->rr_count] = *rr; ++rrset->rr_count; } if(rr->type == TYPE_DNAME && rrset->rr_count > 1) { - if(zone->opts && zone->opts->request_xfr) + if(zone_is_slave(zone->opts)) zc_warning_prev_line("multiple DNAMEs at the same name"); else zc_error_prev_line("multiple DNAMEs at the same name"); } if(rr->type == TYPE_CNAME && rrset->rr_count > 1) { - if(zone->opts && zone->opts->request_xfr) + if(zone_is_slave(zone->opts)) zc_warning_prev_line("multiple CNAMEs at the same name"); else zc_error_prev_line("multiple CNAMEs at the same name"); } if((rr->type == TYPE_DNAME && domain_find_rrset(rr->owner, zone, TYPE_CNAME)) ||(rr->type == TYPE_CNAME && domain_find_rrset(rr->owner, zone, TYPE_DNAME))) { - if(zone->opts && zone->opts->request_xfr) + if(zone_is_slave(zone->opts)) zc_warning_prev_line("DNAME and CNAME at the same name"); else zc_error_prev_line("DNAME and CNAME at the same name"); } if(domain_find_rrset(rr->owner, zone, TYPE_CNAME) && domain_find_non_cname_rrset(rr->owner, zone)) { - if(zone->opts && zone->opts->request_xfr) + if(zone_is_slave(zone->opts)) zc_warning_prev_line("CNAME and other data at the same name"); else zc_error_prev_line("CNAME and other data at the same name"); } - if (rr->type == TYPE_RRSIG && rr_rrsig_type_covered(rr) == TYPE_DNSKEY) { - rrset->zone->is_secure = 1; - } - /* Check we have SOA */ - if (zone->soa_rrset == NULL) { - if (rr->type == TYPE_SOA) { - if (rr->owner != zone->apex) { - zc_error_prev_line( - "SOA record with invalid domain name"); - } else { - zone->soa_rrset = rrset; - } - } - } - else if (rr->type == TYPE_SOA) { - if(zone->opts && zone->opts->request_xfr) - zc_warning_prev_line("duplicate SOA record discarded"); - else - zc_error_prev_line("duplicate SOA record discarded"); - --rrset->rr_count; - } - - /* Is this a zone NS? */ - if (rr->type == TYPE_NS && rr->owner == zone->apex) { - zone->ns_rrset = rrset; - } - if (vflag > 1 && totalrrs > 0 && (totalrrs % progress == 0)) { - fprintf(stdout, "%ld\n", totalrrs); + if(rr->owner == zone->apex) + apex_rrset_checks(parser->db, rrset, rr->owner); + + if(parser->line % ZONEC_PCT_COUNT == 0 && time(NULL) > startzonec + ZONEC_PCT_TIME) { + struct stat buf; + startzonec = time(NULL); + buf.st_size = 0; + fstat(fileno(yyin), &buf); + if(buf.st_size == 0) buf.st_size = 1; + VERBOSITY(1, (LOG_INFO, "parse %s %d %%", + parser->current_zone->opts->name, + (int)((uint64_t)ftell(yyin)*(uint64_t)100/(uint64_t)buf.st_size))); } ++totalrrs; return 1; @@ -1510,10 +1480,11 @@ domain_find_rrset_any(domain_type *domain, uint16_t type) * Check for DNAME type. Nothing is allowed below it */ static void -check_dname(namedb_type* db) +check_dname(zone_type* zone) { domain_type* domain; - RBTREE_FOR(domain, domain_type*, db->domains->names_to_domains) + for(domain = zone->apex; domain && domain_is_subdomain(domain, + zone->apex); domain=domain_next(domain)) { if(domain->is_existing) { /* there may not be DNAMEs above it */ @@ -1525,11 +1496,11 @@ check_dname(namedb_type* db) while(parent) { if(domain_find_rrset_any(parent, TYPE_DNAME)) { zc_error("While checking node %s,", - dname_to_string(domain_dname(domain), NULL)); + domain_to_string(domain)); zc_error("DNAME at %s has data below it. " "This is not allowed (rfc 2672).", - dname_to_string(domain_dname(parent), NULL)); - exit(1); + domain_to_string(parent)); + return; } parent = parent->parent; } @@ -1540,274 +1511,146 @@ check_dname(namedb_type* db) /* * Reads the specified zone into the memory * nsd_options can be NULL if no config file is passed. - * */ -static void -zone_read(const char *name, const char *zonefile, nsd_options_t* nsd_options) +unsigned int +zonec_read(const char* name, const char* zonefile, zone_type* zone) { const dname_type *dname; + totalrrs = 0; + startzonec = time(NULL); + parser->errors = 0; + dname = dname_parse(parser->region, name); if (!dname) { zc_error("incorrect zone name '%s'", name); - return; + return 0; } #ifndef ROOT_SERVER /* Is it a root zone? Are we a root server then? Idiot proof. */ if (dname->label_count == 1) { zc_error("not configured as a root server"); - return; + return 0; } #endif /* Open the zone file */ if (!zone_open(zonefile, 3600, CLASS_IN, dname)) { - if(nsd_options) { - /* check for secondary zone, they can start with no zone info */ - zone_options_t* zopt = zone_options_find(nsd_options, dname); - if(zopt && zone_is_slave(zopt)) { - zc_warning("slave zone %s with no zonefile '%s'(%s) will " - "force zone transfer.", - name, zonefile, strerror(errno)); - return; - } - } - /* cannot happen with stdin - so no fix needed for zonefile */ zc_error("cannot open '%s': %s", zonefile, strerror(errno)); - return; + return 0; } + parser->current_zone = zone; /* Parse and process all RRs. */ yyparse(); + /* remove origin if it was unused */ + domain_table_deldomain(parser->db, parser->origin); + /* check if zone file contained a correct SOA record */ - if (parser->current_zone && parser->current_zone->soa_rrset - && parser->current_zone->soa_rrset->rr_count!=0) - { - if(dname_compare(domain_dname( - parser->current_zone->soa_rrset->rrs[0].owner), - dname) != 0) { - zc_error("zone configured as '%s', but SOA has owner '%s'.", - name, dname_to_string( - domain_dname(parser->current_zone-> - soa_rrset->rrs[0].owner), NULL)); - } + if (!parser->current_zone) { + zc_error("zone configured as '%s' has no content.", name); + } else if(!parser->current_zone->soa_rrset || + parser->current_zone->soa_rrset->rr_count == 0) { + zc_error("zone configured as '%s' has no SOA record.", name); + } else if(dname_compare(domain_dname( + parser->current_zone->soa_rrset->rrs[0].owner), dname) != 0) { + zc_error("zone configured as '%s', but SOA has owner '%s'.", + name, domain_to_string( + parser->current_zone->soa_rrset->rrs[0].owner)); } fclose(yyin); + if(!zone_is_slave(zone->opts)) + check_dname(zone); - fflush(stdout); - totalerrors += parser->errors; parser->filename = NULL; + return parser->errors; } -static void -usage (void) -{ -#ifndef NDEBUG - fprintf(stderr, "usage: nsd-zonec [-v|-h|-C|-F|-L] [-c configfile] [-o origin] [-d directory] [-f database] [-z zonefile]\n\n"); -#else - fprintf(stderr, "usage: nsd-zonec [-v|-h|-C] [-c configfile] [-o origin] [-d directory] [-f database] [-z zonefile]\n\n"); -#endif - fprintf(stderr, "\tNSD zone compiler, creates database from zone files.\n"); - fprintf(stderr, "\tVersion %s. Report bugs to <%s>.\n\n", - PACKAGE_VERSION, PACKAGE_BUGREPORT); - fprintf(stderr, "\t-v\tBe more verbose.\n"); - fprintf(stderr, "\t-h\tPrint this help information.\n"); - fprintf(stderr, "\t-c\tSpecify config file to read instead of default nsd.conf.\n"); - fprintf(stderr, "\t-C\tNo config file is read.\n"); - fprintf(stderr, "\t-d\tSet working directory to open files from.\n"); - fprintf(stderr, "\t-o\tSpecify a zone's origin (only used with -z).\n"); - fprintf(stderr, "\t-f\tSpecify database file to use.\n"); - fprintf(stderr, "\t-z\tSpecify a zonefile to read (read from stdin with \'-\').\n"); -#ifndef NDEBUG - fprintf(stderr, "\t-F\tSet debug facilities.\n"); - fprintf(stderr, "\t-L\tSet debug level.\n"); -#endif -} - -extern char *optarg; -extern int optind; -int -main (int argc, char **argv) +/* + * setup parse + */ +void +zonec_setup_parser(namedb_type* db) { - struct namedb *db; - char *origin = NULL; - int c; - region_type *global_region; - region_type *rr_region; - const char* configfile= CONFIGFILE; - const char* zonesdir = NULL; - const char* singlefile = NULL; - nsd_options_t* nsd_options = NULL; - - log_init("nsd-zonec"); - - global_region = region_create(xalloc, free); - rr_region = region_create(xalloc, free); - totalerrors = 0; - - /* Parse the command line... */ - while ((c = getopt(argc, argv, "d:f:vhCF:L:o:c:z:")) != -1) { - switch (c) { - case 'c': - configfile = optarg; - break; - case 'v': - ++vflag; - break; - case 'f': - dbfile = optarg; - break; - case 'd': - zonesdir = optarg; - break; - case 'C': - configfile = 0; - break; -#ifndef NDEBUG - case 'F': - sscanf(optarg, "%x", &nsd_debug_facilities); - break; - case 'L': - sscanf(optarg, "%d", &nsd_debug_level); - break; -#endif /* NDEBUG */ - case 'o': - origin = optarg; - break; - case 'z': - singlefile = optarg; - break; - case 'h': - usage(); - exit(0); - case '?': - default: - usage(); - exit(1); - } - } - - argc -= optind; - argv += optind; - - if (argc != 0) { - usage(); - exit(1); - } - - /* Read options */ - if(configfile != 0) { - nsd_options = nsd_options_create(global_region); - if(!parse_options_file(nsd_options, configfile)) - { - fprintf(stderr, "nsd-zonec: could not read config: %s\n", configfile); - exit(1); - } - } - if(nsd_options && zonesdir == 0) zonesdir = nsd_options->zonesdir; - if(zonesdir && zonesdir[0]) { - if (chdir(zonesdir)) { - fprintf(stderr, "nsd-zonec: cannot chdir to %s: %s\n", zonesdir, strerror(errno)); - exit(1); - } - } - if(dbfile == 0) { - if(nsd_options && nsd_options->database) dbfile = nsd_options->database; - else dbfile = DBFILE; - } - - /* Create the database */ - if ((db = namedb_new(dbfile)) == NULL) { - fprintf(stderr, "nsd-zonec: error creating the database (%s): %s\n", - dbfile, strerror(errno)); - exit(1); - } - - parser = zparser_create(global_region, rr_region, db); - if (!parser) { - fprintf(stderr, "nsd-zonec: error creating the parser\n"); - exit(1); - } - + region_type* rr_region = region_create(xalloc, free); + parser = zparser_create(db->region, rr_region, db); + assert(parser); /* Unique pointers used to mark errors. */ - error_dname = (dname_type *) region_alloc(global_region, 0); - error_domain = (domain_type *) region_alloc(global_region, 0); - - if (singlefile || origin) { - /* - * Read a single zone file with the specified origin - */ - if(!singlefile) { - fprintf(stderr, "nsd-zonec: must have -z zonefile when reading single zone.\n"); - exit(1); - } - if(!origin) { - fprintf(stderr, "nsd-zonec: must have -o origin when reading single zone.\n"); - exit(1); - } - if (vflag > 0) - fprintf(stdout, "nsd-zonec: reading zone \"%s\".\n", origin); - zone_read(origin, singlefile, nsd_options); - if (vflag > 0) - fprintf(stdout, "nsd-zonec: processed %ld RRs in \"%s\".\n", totalrrs, origin); - } else { - zone_options_t* zone; - if(!nsd_options) { - fprintf(stderr, "nsd-zonec: no zones specified.\n"); - exit(1); - } - /* read all zones */ - RBTREE_FOR(zone, zone_options_t*, nsd_options->zone_options) - { - if (vflag > 0) - fprintf(stdout, "nsd-zonec: reading zone \"%s\".\n", - zone->name); - zone_read(zone->name, zone->zonefile, nsd_options); - if (vflag > 0) - fprintf(stdout, - "nsd-zonec: processed %ld RRs in \"%s\".\n", - totalrrs, zone->name); - totalrrs = 0; - } - } - check_dname(db); + error_dname = (dname_type *) region_alloc(db->region, 1); + error_domain = (domain_type *) region_alloc(db->region, 1); + /* Open the network database */ + setprotoent(1); + setservent(1); +} -#ifndef NDEBUG - if (vflag > 0) { - fprintf(stdout, "global_region: "); - region_dump_stats(global_region, stdout); - fprintf(stdout, "\n"); - fprintf(stdout, "db->region: "); - region_dump_stats(db->region, stdout); - fprintf(stdout, "\n"); +/** desetup parse */ +void +zonec_desetup_parser(void) +{ + if(parser) { + endservent(); + endprotoent(); + region_destroy(parser->rr_region); + /* removed when parser->region(=db->region) is destroyed: + * region_recycle(parser->region, (void*)error_dname, 1); + * region_recycle(parser->region, (void*)error_domain, 1); */ + /* clear memory for exit, but this is not portable to + * other versions of lex. yylex_destroy(); */ } -#endif /* NDEBUG */ +} - /* Close the database */ - if (namedb_save(db) != 0) { - fprintf(stderr, "nsd-zonec: error writing the database (%s): %s\n", db->filename, strerror(errno)); - namedb_discard(db); - exit(1); - } +static domain_table_type* orig_domains = NULL; +static region_type* orig_region = NULL; +static region_type* orig_dbregion = NULL; - /* Print the total number of errors */ - if (vflag > 0 || totalerrors > 0) { - if (totalerrors > 0) { - fprintf(stderr, "\nnsd-zonec: done with %ld errors.\n", - totalerrors); - } else { - fprintf(stdout, "\nnsd-zonec: done with no errors.\n"); - } - } +/** setup for string parse */ +void +zonec_setup_string_parser(region_type* region, domain_table_type* domains) +{ + assert(parser); /* global parser must be setup */ + orig_domains = parser->db->domains; + orig_region = parser->region; + orig_dbregion = parser->db->region; + parser->region = region; + parser->db->region = region; + parser->db->domains = domains; + zparser_init("string", 3600, CLASS_IN, domain_dname(domains->root)); +} - /* Disable this to save some time. */ -#if 0 - region_destroy(global_region); -#endif +/** desetup string parse */ +void +zonec_desetup_string_parser(void) +{ + parser->region = orig_region; + parser->db->domains = orig_domains; + parser->db->region = orig_dbregion; +} - return totalerrors ? 1 : 0; +/** parse a string into temporary storage */ +int +zonec_parse_string(region_type* region, domain_table_type* domains, + zone_type* zone, char* str, domain_type** parsed, int* num_rrs) +{ + int errors; + zonec_setup_string_parser(region, domains); + parser->current_zone = zone; + parser->errors = 0; + totalrrs = 0; + startzonec = time(NULL)+100000; /* disable */ + parser_push_stringbuf(str); + yyparse(); + parser_pop_stringbuf(); + errors = parser->errors; + *num_rrs = totalrrs; + if(*num_rrs == 0) + *parsed = NULL; + else *parsed = parser->prev_dname; + /* remove origin if it was not used during the parse */ + domain_table_deldomain(parser->db, parser->origin); + zonec_desetup_string_parser(); + return errors; } diff --git a/usr.sbin/nsd/zonec.h b/usr.sbin/nsd/zonec.h index 31eb5504869..ca3ce7fa81e 100644 --- a/usr.sbin/nsd/zonec.h +++ b/usr.sbin/nsd/zonec.h @@ -1,7 +1,7 @@ /* * zonec.h -- zone compiler. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -71,6 +71,7 @@ extern domain_type *error_domain; int yyparse(void); int yylex(void); +int yylex_destroy(void); /*int yyerror(const char *s);*/ void yyrestart(FILE *); @@ -79,6 +80,9 @@ void zc_warning_prev_line(const char *fmt, ...) ATTR_FORMAT(printf, 1, 2); void zc_error(const char *fmt, ...) ATTR_FORMAT(printf, 1, 2); void zc_error_prev_line(const char *fmt, ...) ATTR_FORMAT(printf, 1, 2); +void parser_push_stringbuf(char* str); +void parser_pop_stringbuf(void); + int process_rr(void); uint16_t *zparser_conv_hex(region_type *region, const char *hex, size_t len); uint16_t *zparser_conv_hex_length(region_type *region, const char *hex, size_t len); @@ -126,4 +130,17 @@ zparser_type *zparser_create(region_type *region, region_type *rr_region, void zparser_init(const char *filename, uint32_t ttl, uint16_t klass, const dname_type *origin); +/* parser start and stop to parse a zone */ +void zonec_setup_parser(namedb_type* db); +void zonec_desetup_parser(void); +/* parse a zone into memory. name is origin. zonefile is file to read. + * returns number of errors; failure may have read a partial zone */ +unsigned int zonec_read(const char *name, const char *zonefile, zone_type* zone); +/* parse a string into the region. and with given domaintable. global parser + * is restored afterwards. zone needs apex set. returns last domain name + * parsed and the number rrs parse. return number of errors, 0 is success. + * The string must end with a newline after the RR. */ +int zonec_parse_string(region_type* region, domain_table_type* domains, + zone_type* zone, char* str, domain_type** parsed, int* num_rrs); + #endif /* _ZONEC_H_ */ diff --git a/usr.sbin/nsd/zparser.y b/usr.sbin/nsd/zparser.y index 0379d1812a0..28cd4a676be 100644 --- a/usr.sbin/nsd/zparser.y +++ b/usr.sbin/nsd/zparser.y @@ -2,7 +2,7 @@ /* * zyparser.y -- yacc grammar for (DNS) zone files * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -147,6 +147,8 @@ ttl_directive: DOLLAR_TTL sp STR trail origin_directive: DOLLAR_ORIGIN sp abs_dname trail { + /* if previous origin is unused, remove it, do not leak it */ + domain_table_deldomain(parser->db, parser->origin); parser->origin = $3; } | DOLLAR_ORIGIN sp rel_dname trail @@ -1094,11 +1096,11 @@ static void error_va_list(unsigned line, const char *fmt, va_list args) { if (parser->filename) { - fprintf(stderr, "%s:%u: ", parser->filename, line); + char message[MAXSYSLOGMSGLEN]; + vsnprintf(message, sizeof(message), fmt, args); + log_msg(LOG_ERR, "%s:%u: %s", parser->filename, line, message); } - fprintf(stderr, "error: "); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); + else log_vmsg(LOG_ERR, fmt, args); ++parser->errors; parser->error_occurred = 1; @@ -1130,11 +1132,11 @@ static void warning_va_list(unsigned line, const char *fmt, va_list args) { if (parser->filename) { - fprintf(stderr, "%s:%u: ", parser->filename, line); + char m[MAXSYSLOGMSGLEN]; + vsnprintf(m, sizeof(m), fmt, args); + log_msg(LOG_WARNING, "%s:%u: %s", parser->filename, line, m); } - fprintf(stderr, "warning: "); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); + else log_vmsg(LOG_WARNING, fmt, args); } void |