diff options
98 files changed, 48984 insertions, 0 deletions
diff --git a/usr.sbin/nsd/LICENSE b/usr.sbin/nsd/LICENSE new file mode 100644 index 00000000000..55faacfc49b --- /dev/null +++ b/usr.sbin/nsd/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2001-2006, 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. diff --git a/usr.sbin/nsd/Makefile.in b/usr.sbin/nsd/Makefile.in new file mode 100644 index 00000000000..5f1d2ac5515 --- /dev/null +++ b/usr.sbin/nsd/Makefile.in @@ -0,0 +1,623 @@ +# +# Makefile -- one file to make them all, nsd(8) +# +# Copyright (c) 2001-2006, NLnet Labs. All rights reserved. +# +# See LICENSE for the license. +# + +# Standard installation pathnames +SHELL = @SHELL@ +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +sbindir = @sbindir@ +mandir = @mandir@ +datarootdir = @datarootdir@ + +# NSD specific pathnames +configdir = @configdir@ +piddir = @piddir@ +dbdir = @dbdir@ +pidfile = @pidfile@ +dbfile = @dbfile@ +difffile = @difffile@ +xfrdfile = @xfrdfile@ +nsdconfigfile = @nsd_conf_file@ +zonesdir = @zonesdir@ +user = @user@ +spriority = @start_priority@ +kpriority = @kill_priority@ + +CC = @CC@ +CPPFLAGS = @CPPFLAGS@ -I. -I$(srcdir)# @DEFS@ contains -DHAVE_CONFIG_H +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +LIBOBJS = @LIBOBJS@ +INSTALL = $(srcdir)/install-sh -c +INSTALL_PROGRAM = $(INSTALL) +INSTALL_DATA = $(INSTALL) -m 644 + +YACC = @YACC@ +LEX = @LEX@ + +COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) +LINK = $(CC) $(CFLAGS) $(LDFLAGS) +EDIT = sed \ + -e 's,@prefix\@,$(prefix),g' \ + -e 's,@exec_prefix\@,$(exec_prefix),g' \ + -e 's,@sbindir\@,$(sbindir),g' \ + -e 's,@configdir\@,$(configdir),g' \ + -e 's,@zonesdir\@,$(zonesdir),g' \ + -e 's,@pidfile\@,$(pidfile),g' \ + -e 's,@dbfile\@,$(dbfile),g' \ + -e 's,@difffile\@,$(difffile),g' \ + -e 's,@xfrdfile\@,$(xfrdfile),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 zonec nsd-notify nsd-xfer nsdc.sh nsd-checkconf nsd-patch nsd.conf.sample + +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 \ + 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 \ + 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 \ + namedb.o \ + netio.o \ + nsd.o \ + nsec3.o \ + packet.o \ + query.o \ + rbtree.o \ + rdata.o \ + region-allocator.o \ + server.o \ + tsig.o \ + tsig-openssl.o \ + util.o \ + xfrd-disk.o \ + xfrd-notify.o \ + xfrd-tcp.o \ + xfrd.o + +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 \ + namedb.o \ + nsec3.o \ + options.o \ + packet.o \ + query.o \ + rbtree.o \ + rdata.o \ + region-allocator.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 \ + namedb.o \ + nsd-notify.o \ + nsec3.o \ + options.o \ + packet.o \ + query.o \ + rbtree.o \ + rdata.o \ + region-allocator.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 \ + namedb.o \ + nsd-xfer.o \ + nsec3.o \ + options.o \ + packet.o \ + query.o \ + rbtree.o \ + rdata.o \ + region-allocator.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 \ + namedb.o \ + nsd-checkconf.o \ + nsec3.o \ + options.o \ + packet.o \ + query.o \ + rbtree.o \ + rdata.o \ + region-allocator.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 \ + namedb.o \ + nsd-patch.o \ + nsec3.o \ + options.o \ + packet.o \ + query.o \ + rbtree.o \ + rdata.o \ + region-allocator.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 \ + namedb.o \ + netio.o \ + nsec3.o \ + packet.o \ + query.o \ + rbtree.o \ + rdata.o \ + region-allocator.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_util.o \ + cutest.o + +all: $(TARGETS) + +$(ALL_OBJECTS): + $(COMPILE) -c $< + +nsdc.sh: $(srcdir)/nsdc.sh.in + rm -f nsdc.sh + $(EDIT) $(srcdir)/nsdc.sh.in > nsdc.sh + chmod +x nsdc.sh + +nsd.conf.sample: $(srcdir)/nsd.conf.sample.in + rm -f nsd.conf.sample + $(EDIT) $(srcdir)/nsd.conf.sample.in > nsd.conf.sample + +install: all + $(INSTALL) -d $(DESTDIR)$(sbindir) + $(INSTALL) -d $(DESTDIR)$(configdir) + $(INSTALL) -d $(DESTDIR)$(piddir) + $(INSTALL) -d $(DESTDIR)$(dbdir) + $(INSTALL) -d $(DESTDIR)$(mandir) + $(INSTALL) -d $(DESTDIR)$(mandir)/man8 + $(INSTALL) -d $(DESTDIR)$(mandir)/man5 + $(INSTALL) nsd $(DESTDIR)$(sbindir)/nsd + $(INSTALL) zonec $(DESTDIR)$(sbindir)/zonec + $(INSTALL) nsdc.sh $(DESTDIR)$(sbindir)/nsdc + $(INSTALL) nsd-notify $(DESTDIR)$(sbindir)/nsd-notify + $(INSTALL) nsd-checkconf $(DESTDIR)$(sbindir)/nsd-checkconf + $(INSTALL) nsd-patch $(DESTDIR)$(sbindir)/nsd-patch + $(INSTALL) nsd-xfer $(DESTDIR)$(sbindir)/nsd-xfer + $(INSTALL_DATA) $(srcdir)/nsd.8 $(DESTDIR)$(mandir)/man8 + $(INSTALL_DATA) $(srcdir)/nsdc.8 $(DESTDIR)$(mandir)/man8 + $(INSTALL_DATA) $(srcdir)/zonec.8 $(DESTDIR)$(mandir)/man8 + $(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.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)/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/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) + @echo + @echo "You still need to remove $(DESTDIR)$(configdir), $(DESTDIR)$(piddir), $(DESTDIR)$(dbdir) directory by hand." + +test: + +nsd: $(NSD_OBJECTS) $(LIBOBJS) + $(LINK) -o $@ $(NSD_OBJECTS) $(LIBOBJS) $(LIBS) + +zonec: $(ZONEC_OBJECTS) $(LIBOBJS) + $(LINK) -o $@ $(ZONEC_OBJECTS) $(LIBOBJS) $(LIBS) + +nsd-notify: $(NSD_NOTIFY_OBJECTS) $(LIBOBJS) + $(LINK) -o $@ $(NSD_NOTIFY_OBJECTS) $(LIBOBJS) $(LIBS) + +nsd-checkconf: $(NSD_CHECKCONF_OBJECTS) $(LIBOBJS) + $(LINK) -o $@ $(NSD_CHECKCONF_OBJECTS) $(LIBOBJS) $(LIBS) + +nsd-xfer: $(NSD_XFER_OBJECTS) $(LIBOBJS) + $(LINK) -o $@ $(NSD_XFER_OBJECTS) $(LIBOBJS) $(LIBS) + +nsd-patch: $(NSD_PATCH_OBJECTS) $(LIBOBJS) + $(LINK) -o $@ $(NSD_PATCH_OBJECTS) $(LIBOBJS) $(LIBS) + +cutest: $(CUTEST_OBJECTS) + $(LINK) -o $@ $(CUTEST_OBJECTS) $(LIBOBJS) $(LIBS) + +clean: + rm -f *.o *.so y.* *.core *.gmon tags TAGS + rm -f $(TARGETS) + +realclean: clean + rm -f Makefile config.h config.h.in config.log config.status configure + rm -rf autom4te* + rm -f zlexer.c zparser.h zparser.c zparser.stamp + rm -f configlexer.c configparser.h configparser.c configparser.stamp + +basename.o: $(srcdir)/compat/basename.c + $(COMPILE) -c $(srcdir)/compat/basename.c -o $@ + +inet_pton.o: $(srcdir)/compat/inet_pton.c + $(COMPILE) -c $(srcdir)/compat/inet_pton.c -o $@ + +inet_ntop.o: $(srcdir)/compat/inet_ntop.c + $(COMPILE) -c $(srcdir)/compat/inet_ntop.c -o $@ + +inet_aton.o: $(srcdir)/compat/inet_aton.c + $(COMPILE) -c $(srcdir)/compat/inet_aton.c -o $@ + +b64_pton.o: $(srcdir)/compat/b64_pton.c + $(COMPILE) -c $(srcdir)/compat/b64_pton.c -o $@ + +b64_ntop.o: $(srcdir)/compat/b64_ntop.c + $(COMPILE) -c $(srcdir)/compat/b64_ntop.c -o $@ + +memmove.o: $(srcdir)/compat/memmove.c + $(COMPILE) -c $(srcdir)/compat/memmove.c -o $@ + +snprintf.o: $(srcdir)/compat/snprintf.c + $(COMPILE) -c $(srcdir)/compat/snprintf.c -o $@ + +strlcpy.o: $(srcdir)/compat/strlcpy.c + $(COMPILE) -c $(srcdir)/compat/strlcpy.c -o $@ + +strptime.o: $(srcdir)/compat/strptime.c + $(COMPILE) -c $(srcdir)/compat/strptime.c -o $@ + +vsnprintf.o: $(srcdir)/compat/vsnprintf.c + $(COMPILE) -c $(srcdir)/compat/vsnprintf.c -o $@ + +timegm.o: $(srcdir)/compat/timegm.c + $(COMPILE) -c $(srcdir)/compat/timegm.c -o $@ + +malloc.o: $(srcdir)/compat/malloc.c + $(COMPILE) -c $(srcdir)/compat/malloc.c -o $@ + +pselect.o: $(srcdir)/compat/pselect.c + $(COMPILE) -c $(srcdir)/compat/pselect.c -o $@ + +fake-rfc2553.o: $(srcdir)/compat/fake-rfc2553.c + $(COMPILE) -c $(srcdir)/compat/fake-rfc2553.c -o $@ + +cutest_dname.o: $(srcdir)/tpkg/cutest/cutest_dname.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_dname.c -o $@ + +cutest_dns.o: $(srcdir)/tpkg/cutest/cutest_dns.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_dns.c -o $@ + +cutest_iterated_hash.o: $(srcdir)/tpkg/cutest/cutest_iterated_hash.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_iterated_hash.c -o $@ + +cutest_run.o: $(srcdir)/tpkg/cutest/cutest_run.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_run.c -o $@ + +cutest_rbtree.o: $(srcdir)/tpkg/cutest/cutest_rbtree.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_rbtree.c -o $@ + +cutest_options.o: $(srcdir)/tpkg/cutest/cutest_options.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_options.c -o $@ + +cutest_region.o: $(srcdir)/tpkg/cutest/cutest_region.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_region.c -o $@ + +cutest_util.o: $(srcdir)/tpkg/cutest/cutest_util.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest_util.c -o $@ + +cutest.o: $(srcdir)/tpkg/cutest/cutest.c + $(COMPILE) -c $(srcdir)/tpkg/cutest/cutest.c -o $@ + +zlexer.c: $(srcdir)/zlexer.lex + echo "#include <config.h>" > $@ + $(LEX) -i -t $(srcdir)/zlexer.lex >> $@ + +zparser.c zparser.h: $(srcdir)/zparser.y + $(YACC) -d -o zparser.c $(srcdir)/zparser.y + +configlexer.c: $(srcdir)/configlexer.lex + echo "#include \"configyyrename.h\"" > $@ + $(LEX) -i -t $(srcdir)/configlexer.lex >> $@ + +configparser.c configparser.h: $(srcdir)/configparser.y + $(YACC) -d -o configparser.c $(srcdir)/configparser.y + +# autoconf rules +config.h.in: configure.ac + autoheader + +configure: configure.ac + autoconf + +tags: + ctags *.[ch] + +# dependency generation +DEPEND_TMP=depend1073.tmp +DEPEND_TMP2=depend1074.tmp +DEPEND_TARGET=Makefile +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?' \ + > $(DEPEND_TMP) + cp $(DEPEND_TARGET) $(DEPEND_TMP2) + head -`egrep -n "# Dependencies" $(DEPEND_TARGET) | tail -1 | sed -e 's/:.*$$//'` $(DEPEND_TMP2) > $(DEPEND_TARGET) + cat $(DEPEND_TMP) >> $(DEPEND_TARGET) + @if diff $(DEPEND_TARGET) $(DEPEND_TMP2); then echo " $(DEPEND_TARGET) unchanged"; else echo " Updated $(DEPEND_TARGET))"; fi + @if test -f $(DEPEND_TARGET2); then \ + cp $(DEPEND_TARGET2) $(DEPEND_TMP2); \ + head -`egrep -n "# Dependencies" $(DEPEND_TARGET2) | tail -1 | sed -e 's/:.*$$//'` $(DEPEND_TMP2) > $(DEPEND_TARGET2); \ + cat $(DEPEND_TMP) >> $(DEPEND_TARGET2); \ + if diff $(DEPEND_TARGET2) $(DEPEND_TMP2); then echo " $(DEPEND_TARGET2) unchanged"; else echo " Updated $(DEPEND_TARGET2))"; fi; \ + fi + rm -f $(DEPEND_TMP) $(DEPEND_TMP2) + +# 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 +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 +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)/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 +dbcreate.o: $(srcdir)/dbcreate.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 +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 +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 +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 +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 +iterated_hash.o: $(srcdir)/iterated_hash.c config.h $(srcdir)/iterated_hash.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 +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)/options.h $(srcdir)/rbtree.h $(srcdir)/tsig.h $(srcdir)/dname.h +nsd-checkconf.o: $(srcdir)/nsd-checkconf.c config.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ + $(srcdir)/rbtree.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/buffer.h +nsd-notify.o: $(srcdir)/nsd-notify.c config.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 +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 $(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)/rdata.h $(srcdir)/tsig-openssl.h $(srcdir)/zonec.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)/difffile.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 $(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 +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 +region-allocator.o: $(srcdir)/region-allocator.c config.h $(srcdir)/region-allocator.h $(srcdir)/util.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 $(srcdir)/ipc.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)/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 +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)/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)/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 +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 $(srcdir)/dns.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 +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 +fake-rfc2553.o: $(srcdir)/compat/fake-rfc2553.c $(srcdir)/compat/fake-rfc2553.h config.h +inet_aton.o: $(srcdir)/compat/inet_aton.c config.h +inet_ntop.o: $(srcdir)/compat/inet_ntop.c config.h +inet_pton.o: $(srcdir)/compat/inet_pton.c config.h +malloc.o: $(srcdir)/compat/malloc.c +memmove.o: $(srcdir)/compat/memmove.c config.h +pselect.o: $(srcdir)/compat/pselect.c config.h +snprintf.o: $(srcdir)/compat/snprintf.c config.h +strlcpy.o: $(srcdir)/compat/strlcpy.c config.h +strptime.o: $(srcdir)/compat/strptime.c config.h +cutest.o: $(srcdir)/tpkg/cutest/cutest.c $(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 +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 +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 +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)/region-allocator.h +cutest_run.o: $(srcdir)/tpkg/cutest/cutest_run.c config.h $(srcdir)/tpkg/cutest/cutest.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 diff --git a/usr.sbin/nsd/acx_nlnetlabs.m4 b/usr.sbin/nsd/acx_nlnetlabs.m4 new file mode 100644 index 00000000000..3baa9d3bf13 --- /dev/null +++ b/usr.sbin/nsd/acx_nlnetlabs.m4 @@ -0,0 +1,1152 @@ +# acx_nlnetlabs.m4 - common macros for configure checks +# Copyright 2009, Wouter Wijngaards, NLnet Labs. +# BSD licensed. +# +# Version 2 +# 2009-07-03 +# Changelog +# - fixup LDFLAGS for empty ssl dir. +# +# Automates some of the checking constructs. Aims at portability for POSIX. +# Documentation for functions is below. +# +# the following macro's are provided in this file: +# (see below for details on each macro). +# +# ACX_ESCAPE_BACKSLASH - escape backslashes in var for C-preproc. +# ACX_RSRC_VERSION - create windows resource version number. +# ACX_CHECK_COMPILER_FLAG - see if cc supports a flag. +# ACX_CHECK_ERROR_FLAGS - see which flag is -werror (used below). +# ACX_CHECK_COMPILER_FLAG_NEEDED - see if flags make the code compile cleanly. +# ACX_DEPFLAG - find cc dependency flags. +# ACX_DETERMINE_EXT_FLAGS_UNBOUND - find out which flags enable BSD and POSIX. +# ACX_CHECK_FORMAT_ATTRIBUTE - find cc printf format syntax. +# ACX_CHECK_UNUSED_ATTRIBUTE - find cc variable unused syntax. +# ACX_LIBTOOL_C_ONLY - create libtool for C only, improved. +# ACX_TYPE_U_CHAR - u_char type. +# ACX_TYPE_RLIM_T - rlim_t type. +# ACX_TYPE_SOCKLEN_T - socklen_t type. +# ACX_TYPE_IN_ADDR_T - in_addr_t type. +# ACX_TYPE_IN_PORT_T - in_port_t type. +# ACX_ARG_RPATH - add --disable-rpath option. +# ACX_WITH_SSL - add --with-ssl option, link -lcrypto. +# ACX_LIB_SSL - setup to link -lssl. +# ACX_SYS_LARGEFILE - improved sys_largefile, fseeko, >2G files. +# ACX_CHECK_GETADDRINFO_WITH_INCLUDES - find getaddrinfo, portably. +# ACX_FUNC_DEPRECATED - see if func is deprecated. +# ACX_CHECK_NONBLOCKING_BROKEN - see if nonblocking sockets really work. +# ACX_MKDIR_ONE_ARG - determine mkdir(2) number of arguments. +# ACX_FUNC_IOCTLSOCKET - find ioctlsocket, portably. +# AHX_CONFIG_FORMAT_ATTRIBUTE - config.h text for format. +# AHX_CONFIG_UNUSED_ATTRIBUTE - config.h text for unused. +# AHX_CONFIG_FSEEKO - define fseeko, ftello fallback. +# AHX_CONFIG_RAND_MAX - define RAND_MAX if needed. +# AHX_CONFIG_MAXHOSTNAMELEN - define MAXHOSTNAMELEN if needed. +# AHX_CONFIG_IPV6_MIN_MTU - define IPV6_MIN_MTU if needed. +# AHX_CONFIG_SNPRINTF - snprintf compat prototype +# AHX_CONFIG_INET_PTON - inet_pton compat prototype +# AHX_CONFIG_INET_NTOP - inet_ntop compat prototype +# AHX_CONFIG_INET_ATON - inet_aton compat prototype +# AHX_CONFIG_MEMMOVE - memmove compat prototype +# AHX_CONFIG_STRLCPY - strlcpy compat prototype +# AHX_CONFIG_GMTIME_R - gmtime_r compat prototype +# AHX_CONFIG_W32_SLEEP - w32 compat for sleep +# AHX_CONFIG_W32_USLEEP - w32 compat for usleep +# AHX_CONFIG_W32_RANDOM - w32 compat for random +# AHX_CONFIG_W32_SRANDOM - w32 compat for srandom +# AHX_CONFIG_W32_FD_SET_T - w32 detection of FD_SET_T. +# ACX_CFLAGS_STRIP - strip one flag from CFLAGS +# ACX_STRIP_EXT_FLAGS - strip extension flags from CFLAGS +# AHX_CONFIG_FLAG_OMITTED - define omitted flag +# AHX_CONFIG_FLAG_EXT - define omitted extension flag +# AHX_CONFIG_EXT_FLAGS - define the stripped extension flags +# + +dnl Escape backslashes as \\, for C:\ paths, for the C preprocessor defines. +dnl for example, ACX_ESCAPE_BACKSLASH($from_var, to_var) +dnl $1: the text to change. +dnl $2: the result. +AC_DEFUN([ACX_ESCAPE_BACKSLASH], [$2="`echo $1 | sed -e 's/\\\\/\\\\\\\\/g'`" +]) + +dnl Calculate comma separated windows-resource numbers from package version. +dnl Picks the first three(,0) or four numbers out of the name. +dnl $1: variable for the result +AC_DEFUN([ACX_RSRC_VERSION], +[$1=[`echo $PACKAGE_VERSION | sed -e 's/^[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\).*$/\1,\2,\3,\4/' -e 's/^[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*$/\1,\2,\3,0/' `] +]) + +dnl Routine to help check for compiler flags. +dnl Checks if the compiler will accept the flag. +dnl $1: the flag without a - in front, so g to check -g. +dnl $2: executed if yes +dnl $3: executed if no +AC_DEFUN([ACX_CHECK_COMPILER_FLAG], +[ +AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether $CC supports -$1) +cache=`echo $1 | sed 'y%.=/+-%___p_%'` +AC_CACHE_VAL(cv_prog_cc_flag_$cache, +[ +echo 'void f(){}' >conftest.c +if test -z "`$CC -$1 -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 conftest.o conftest.c +]) +if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then +AC_MSG_RESULT(yes) +: +$2 +else +AC_MSG_RESULT(no) +: +$3 +fi +]) + +dnl setup flags for ACX_CHECK_COMPILER_FLAG_NEEDED +dnl ERRFLAG: result, compiler flag to turn warnings into errors +AC_DEFUN([ACX_CHECK_ERROR_FLAGS], +[ +ACX_CHECK_COMPILER_FLAG(Werror, [ERRFLAG="-Werror"], [ERRFLAG="-errwarn"]) +ACX_CHECK_COMPILER_FLAG(Wall, [ERRFLAG="$ERRFLAG -Wall"], + [ERRFLAG="$ERRFLAG -errfmt"]) +]) + +dnl Routine to help check for needed compiler flags. +dnl $1: flags for CC +dnl $2: the includes and code +dnl $3: if the given code only compiles with the flag, execute argument 3 +dnl $4: if the given code compiles without the flag, execute argument 4 +dnl $5: with and without flag the compile fails, execute argument 5. +AC_DEFUN([ACX_CHECK_COMPILER_FLAG_NEEDED], +[ +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([ACX_CHECK_ERROR_FLAGS]) +AC_MSG_CHECKING(whether we need $1 as a flag for $CC) +cache=AS_TR_SH($1) +dnl cache=`echo $1 | sed 'y%.=/+- %___p__%'` +AC_CACHE_VAL(cv_prog_cc_flag_needed_$cache, +[ +echo '$2' > conftest.c +echo 'void f(){}' >>conftest.c +if test -z "`$CC $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then +eval "cv_prog_cc_flag_needed_$cache=no" +else +[ +if test -z "`$CC $CFLAGS $1 $ERRFLAG -c conftest.c 2>&1`"; then +eval "cv_prog_cc_flag_needed_$cache=yes" +else +eval "cv_prog_cc_flag_needed_$cache=fail" +#echo 'Test with flag fails too!' +#cat conftest.c +#echo "$CC $CFLAGS $1 $ERRFLAG -c conftest.c 2>&1" +#echo `$CC $CFLAGS $1 $ERRFLAG -c conftest.c 2>&1` +#exit 1 +fi +] +fi +rm -f conftest conftest.c conftest.o +]) +if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then +AC_MSG_RESULT(yes) +: +$3 +else +if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then +AC_MSG_RESULT(no) +#echo 'Test with flag is no!' +#cat conftest.c +#echo "$CC $CFLAGS $1 $ERRFLAG -c conftest.c 2>&1" +#echo `$CC $CFLAGS $1 $ERRFLAG -c conftest.c 2>&1` +#exit 1 +: +$4 +else +AC_MSG_RESULT(failed) +: +$5 +fi +fi +]) + +dnl Check for CC dependency flag +dnl DEPFLAG: set to flag that generates dependencies. +AC_DEFUN([ACX_DEPFLAG], +[ +AC_MSG_CHECKING([$CC dependency flag]) +echo 'void f(){}' >conftest.c +if test "`$CC -MM conftest.c 2>&1`" = "conftest.o: conftest.c"; then + DEPFLAG="-MM" +else + if test "`$CC -xM1 conftest.c 2>&1`" = "conftest.o: conftest.c"; then + DEPFLAG="-xM1" + else + DEPFLAG="-MM" # dunno do something + fi +fi +AC_MSG_RESULT($DEPFLAG) +rm -f conftest.c +AC_SUBST(DEPFLAG) +]) + +dnl Determine flags that gives POSIX and BSD functionality. +dnl CFLAGS is modified for the result. +AC_DEFUN([ACX_DETERMINE_EXT_FLAGS_UNBOUND], +[ +ACX_CHECK_COMPILER_FLAG(std=c99, [C99FLAG="-std=c99"]) +ACX_CHECK_COMPILER_FLAG(xc99, [C99FLAG="-xc99"]) + +AC_CHECK_HEADERS([getopt.h time.h],,, [AC_INCLUDES_DEFAULT]) + +ACX_CHECK_COMPILER_FLAG_NEEDED($C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE, +[ +#include "confdefs.h" +#include <stdlib.h> +#include <ctype.h> +#include <sys/time.h> +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#include <unistd.h> +#include <netdb.h> +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +int test() { + int a; + char **opts = NULL; + struct timeval tv; + char *t; + time_t time = 0; + char *buf = NULL; + const char* str = NULL; + struct msghdr msg; + msg.msg_control = 0; + t = ctime_r(&time, buf); + tv.tv_usec = 10; + srandom(32); + a = getopt(2, opts, "a"); + a = isascii(32); + str = gai_strerror(0); + return a; +} +], [CFLAGS="$CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE"]) + +ACX_CHECK_COMPILER_FLAG_NEEDED($C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE, +[ +#include "confdefs.h" +#include <stdlib.h> +#include <ctype.h> +#include <sys/time.h> +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#include <unistd.h> +#include <netdb.h> +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +int test() { + int a; + char **opts = NULL; + struct timeval tv; + char *t; + time_t time = 0; + char *buf = NULL; + const char* str = NULL; + struct msghdr msg; + msg.msg_control = 0; + t = ctime_r(&time, buf); + tv.tv_usec = 10; + srandom(32); + a = getopt(2, opts, "a"); + a = isascii(32); + str = gai_strerror(0); + return a; +} +], [CFLAGS="$CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE"]) + +ACX_CHECK_COMPILER_FLAG_NEEDED($C99FLAG, +[ +#include <stdbool.h> +#include <ctype.h> +int test() { + int a = 0; + return a; +} +], [CFLAGS="$CFLAGS $C99FLAG"]) + +ACX_CHECK_COMPILER_FLAG_NEEDED(-D_BSD_SOURCE, +[ +#include <ctype.h> + +int test() { + int a; + a = isascii(32); + return a; +} +], [CFLAGS="$CFLAGS -D_BSD_SOURCE"]) + +ACX_CHECK_COMPILER_FLAG_NEEDED(-D_GNU_SOURCE, +[ +#include <netinet/in.h> + +int test() { + struct in6_pktinfo inf; + int a = (int)sizeof(inf); + return a; +} +], [CFLAGS="$CFLAGS -D_GNU_SOURCE"]) + +# check again for GNU_SOURCE for setresgid. May fail if setresgid +# is not available at all. -D_FRSRESGID is to make this check unique. +# otherwise we would get the previous cached result. +ACX_CHECK_COMPILER_FLAG_NEEDED(-D_GNU_SOURCE -D_FRSRESGID, +[ +#include <unistd.h> + +int test() { + int a = setresgid(0,0,0); + a = setresuid(0,0,0); + return a; +} +], [CFLAGS="$CFLAGS -D_GNU_SOURCE"]) + +ACX_CHECK_COMPILER_FLAG_NEEDED(-D_POSIX_C_SOURCE=200112, +[ +#include "confdefs.h" +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#include <netdb.h> + +int test() { + int a = 0; + char *t; + time_t time = 0; + char *buf = NULL; + const char* str = NULL; + t = ctime_r(&time, buf); + str = gai_strerror(0); + return a; +} +], [CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=200112"]) + +ACX_CHECK_COMPILER_FLAG_NEEDED(-D__EXTENSIONS__, +[ +#include "confdefs.h" +#include <stdlib.h> +#include <ctype.h> +#include <sys/time.h> +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#include <unistd.h> +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +int test() { + int a; + char **opts = NULL; + struct timeval tv; + tv.tv_usec = 10; + srandom(32); + a = getopt(2, opts, "a"); + a = isascii(32); + return a; +} +], [CFLAGS="$CFLAGS -D__EXTENSIONS__"]) + +])dnl End of ACX_DETERMINE_EXT_FLAGS_UNBOUND + +dnl Check the printf-format attribute (if any) +dnl result in HAVE_ATTR_FORMAT. +dnl Make sure you also include the AHX_CONFIG_FORMAT_ATTRIBUTE. +AC_DEFUN([ACX_CHECK_FORMAT_ATTRIBUTE], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "format" attribute) +AC_CACHE_VAL(ac_cv_c_format_attribute, +[ac_cv_c_format_attribute=no +AC_TRY_COMPILE( +[#include <stdio.h> +void f (char *format, ...) __attribute__ ((format (printf, 1, 2))); +void (*pf) (char *format, ...) __attribute__ ((format (printf, 1, 2))); +], [ + f ("%s", "str"); +], +[ac_cv_c_format_attribute="yes"], +[ac_cv_c_format_attribute="no"]) +]) + +AC_MSG_RESULT($ac_cv_c_format_attribute) +if test $ac_cv_c_format_attribute = yes; then + AC_DEFINE(HAVE_ATTR_FORMAT, 1, [Whether the C compiler accepts the "format" attribute]) +fi +])dnl End of ACX_CHECK_FORMAT_ATTRIBUTE + +dnl Setup ATTR_FORMAT config.h parts. +dnl make sure you call ACX_CHECK_FORMAT_ATTRIBUTE also. +AC_DEFUN([AHX_CONFIG_FORMAT_ATTRIBUTE], +[ +#ifdef HAVE_ATTR_FORMAT +# define ATTR_FORMAT(archetype, string_index, first_to_check) \ + __attribute__ ((format (archetype, string_index, first_to_check))) +#else /* !HAVE_ATTR_FORMAT */ +# define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */ +#endif /* !HAVE_ATTR_FORMAT */ +]) + +dnl Check how to mark function arguments as unused. +dnl result in HAVE_ATTR_UNUSED. +dnl Make sure you include AHX_CONFIG_UNUSED_ATTRIBUTE also. +AC_DEFUN([ACX_CHECK_UNUSED_ATTRIBUTE], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "unused" attribute) +AC_CACHE_VAL(ac_cv_c_unused_attribute, +[ac_cv_c_unused_attribute=no +AC_TRY_COMPILE( +[#include <stdio.h> +void f (char *u __attribute__((unused))); +], [ + f ("x"); +], +[ac_cv_c_unused_attribute="yes"], +[ac_cv_c_unused_attribute="no"]) +]) + +dnl Setup ATTR_UNUSED config.h parts. +dnl make sure you call ACX_CHECK_UNUSED_ATTRIBUTE also. +AC_DEFUN([AHX_CONFIG_UNUSED_ATTRIBUTE], +[ +#if defined(DOXYGEN) +# define ATTR_UNUSED(x) x +#elif defined(__cplusplus) +# define ATTR_UNUSED(x) +#elif defined(HAVE_ATTR_UNUSED) +# define ATTR_UNUSED(x) x __attribute__((unused)) +#else /* !HAVE_ATTR_UNUSED */ +# define ATTR_UNUSED(x) x +#endif /* !HAVE_ATTR_UNUSED */ +]) + +AC_MSG_RESULT($ac_cv_c_unused_attribute) +if test $ac_cv_c_unused_attribute = yes; then + AC_DEFINE(HAVE_ATTR_UNUSED, 1, [Whether the C compiler accepts the "unused" attribute]) +fi +])dnl + +dnl Pre-fun for ACX_LIBTOOL_C_ONLY +AC_DEFUN([ACX_LIBTOOL_C_PRE], [ +# skip these tests, we do not need them. +AC_DEFUN([AC_PROG_F77], [:]) +AC_DEFUN([AC_PROG_FC], [:]) +AC_DEFUN([AC_PROG_CXX], [:]) +AC_DEFUN([AC_PROG_CXXCPP], [:]) +AC_DEFUN([AC_PROG_OBJC], [:]) +AC_DEFUN([AC_PROG_OBJCCPP], [:]) +AC_DEFUN([AC_LIBTOOL_CXX], [:]) +AC_DEFUN([AC_LIBTOOL_F77], [:]) +# always use ./libtool unless override from commandline (libtool=mylibtool) +if test -z "$libtool"; then + libtool="./libtool" +fi +AC_SUBST(libtool) +# avoid libtool max commandline length test on systems that fork slowly. +AC_CANONICAL_HOST +if echo "$host_os" | grep "sunos4" >/dev/null; then + lt_cv_sys_max_cmd_len=32750; +fi +AC_PATH_TOOL(AR, ar, [false]) +if test $AR = false; then + AC_MSG_ERROR([Cannot find 'ar', please extend PATH to include it]) +fi +]) + +dnl Perform libtool check, portably, only for C +AC_DEFUN([ACX_LIBTOOL_C_ONLY], [ +dnl as a requirement so that is gets called before LIBTOOL +dnl because libtools 'AC_REQUIRE' names are right after this one, before +dnl this function contents. +AC_REQUIRE([ACX_LIBTOOL_C_PRE]) +AC_PROG_LIBTOOL +]) + +dnl Detect if u_char type is defined, otherwise define it. +AC_DEFUN([ACX_TYPE_U_CHAR], + [AC_CHECK_TYPE(u_char, unsigned char)]) + +dnl Detect if rlim_t type is defined, otherwise define it. +AC_DEFUN([ACX_TYPE_RLIM_T], +[AC_CHECK_TYPE(rlim_t, , + [AC_DEFINE([rlim_t], [unsigned long], [Define to 'int' if not defined])], [ +AC_INCLUDES_DEFAULT +#if HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif +]) ]) + +dnl Detect if socklen_t type is defined, otherwise define it. +AC_DEFUN([ACX_TYPE_SOCKLEN_T], +[ +AC_CHECK_TYPE(socklen_t, , + [AC_DEFINE([socklen_t], [int], [Define to 'int' if not defined])], [ +AC_INCLUDES_DEFAULT +#if HAVE_SYS_SOCKET_H +# include <sys/socket.h> +#endif +]) ]) + +dnl Detect if socklen_t type is defined, otherwise define it. +AC_DEFUN([ACX_TYPE_IN_ADDR_T], +[ AC_CHECK_TYPE(in_addr_t, [], [AC_DEFINE([in_addr_t], [uint32_t], [in_addr_t])], [ +AC_INCLUDES_DEFAULT +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +]) ]) + +dnl Detect if socklen_t type is defined, otherwise define it. +AC_DEFUN([ACX_TYPE_IN_PORT_T], +[ AC_CHECK_TYPE(in_port_t, [], [AC_DEFINE([in_port_t], [uint16_t], [in_port_t])], [ +AC_INCLUDES_DEFAULT +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +]) ]) + +dnl Add option to disable the evil rpath. Check whether to use rpath or not. +dnl Adds the --disable-rpath option. Uses trick to edit the ./libtool. +AC_DEFUN([ACX_ARG_RPATH], +[ +AC_ARG_ENABLE(rpath, + [ --disable-rpath disable hardcoded rpath (default=enabled)], + enable_rpath=$enableval, enable_rpath=yes) +if test "x$enable_rpath" = xno; then + AC_MSG_RESULT([Fixing libtool for -rpath problems.]) + sed < libtool > libtool-2 \ + 's/^hardcode_libdir_flag_spec.*$'/'hardcode_libdir_flag_spec=" -D__LIBTOOL_RPATH_SED__ "/' + mv libtool-2 libtool + chmod 755 libtool + libtool="./libtool" +fi +]) + +dnl Add a -R to the RUNTIME_PATH. Only if rpath is enabled and it is +dnl an absolute path. +dnl $1: the pathname to add. +AC_DEFUN([ACX_RUNTIME_PATH_ADD], [ + if test "x$enable_rpath" = xyes; then + if echo "$1" | grep "^/" >/dev/null; then + RUNTIME_PATH="$RUNTIME_PATH -R$1" + fi + fi +]) + +dnl Check for SSL. +dnl Adds --with-ssl option, searches for openssl and defines HAVE_SSL if found +dnl Setup of CPPFLAGS, CFLAGS. Adds -lcrypto to LIBS. +dnl Checks main header files of SSL. +dnl +AC_DEFUN([ACX_WITH_SSL], +[ +AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname], + [enable SSL (will check /usr/local/ssl + /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /opt/local /usr/sfw /usr)]),[ + ],[ + withval="yes" + ]) + if test x_$withval = x_no; then + AC_MSG_ERROR([Need SSL library to do digital signature cryptography]) + fi + if test x_$withval != x_no; then + AC_MSG_CHECKING(for SSL) + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /opt/local /usr/sfw /usr" + fi + for dir in $withval; do + ssldir="$dir" + if test -f "$dir/include/openssl/ssl.h"; then + found_ssl="yes" + AC_DEFINE_UNQUOTED([HAVE_SSL], [], [Define if you have the SSL libraries installed.]) + dnl assume /usr/include is already in the include-path. + if test "$ssldir" != "/usr"; then + CPPFLAGS="$CPPFLAGS -I$ssldir/include" + fi + break; + fi + done + if test x_$found_ssl != x_yes; then + AC_MSG_ERROR(Cannot find the SSL libraries in $withval) + else + AC_MSG_RESULT(found in $ssldir) + HAVE_SSL=yes + dnl assume /usr is already in the lib and dynlib paths. + if test "$ssldir" != "/usr" -a "$ssldir" != ""; then + LDFLAGS="$LDFLAGS -L$ssldir/lib" + ACX_RUNTIME_PATH_ADD([$ssldir/lib]) + fi + + AC_MSG_CHECKING([for HMAC_CTX_init in -lcrypto]) + LIBS="$LIBS -lcrypto" + AC_TRY_LINK(, [ + int HMAC_CTX_init(void); + (void)HMAC_CTX_init(); + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_HMAC_CTX_INIT], 1, + [If you have HMAC_CTX_init]) + ], [ + AC_MSG_RESULT(no) + # check if -lwsock32 or -lgdi32 are needed. + BAKLIBS="$LIBS" + LIBS="$LIBS -lgdi32" + AC_MSG_CHECKING([if -lcrypto needs -lgdi32]) + AC_TRY_LINK([], [ + int HMAC_CTX_init(void); + (void)HMAC_CTX_init(); + ],[ + AC_DEFINE([HAVE_HMAC_CTX_INIT], 1, + [If you have HMAC_CTX_init]) + AC_MSG_RESULT(yes) + ],[ + AC_MSG_RESULT(no) + LIBS="$BAKLIBS" + LIBS="$LIBS -ldl" + AC_MSG_CHECKING([if -lcrypto needs -ldl]) + AC_TRY_LINK([], [ + int HMAC_CTX_init(void); + (void)HMAC_CTX_init(); + ],[ + AC_DEFINE([HAVE_HMAC_CTX_INIT], 1, + [If you have HMAC_CTX_init]) + AC_MSG_RESULT(yes) + ],[ + AC_MSG_RESULT(no) + AC_MSG_ERROR([OpenSSL found in $ssldir, but version 0.9.7 or higher is required]) + ]) + ]) + ]) + fi + AC_SUBST(HAVE_SSL) + AC_SUBST(RUNTIME_PATH) + fi +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]) +])dnl End of ACX_WITH_SSL + +dnl Setup to use -lssl +dnl To use -lcrypto, use the ACX_WITH_SSL setup (before this one). +AC_DEFUN([ACX_LIB_SSL], +[ +# check if libssl needs libdl +BAKLIBS="$LIBS" +LIBS="-lssl $LIBS" +AC_MSG_CHECKING([if libssl needs libdl]) +AC_TRY_LINK_FUNC([SSL_CTX_new], [ + AC_MSG_RESULT([no]) + LIBS="$BAKLIBS" +] , [ + AC_MSG_RESULT([yes]) + LIBS="$BAKLIBS" + AC_SEARCH_LIBS([dlopen], [dl]) +]) ])dnl End of ACX_LIB_SSL + +dnl Setup to use very large files (>2Gb). +dnl setups fseeko and its own +AC_DEFUN([ACX_SYS_LARGEFILE], +[ +AC_SYS_LARGEFILE +dnl try to see if an additional _LARGEFILE_SOURCE 1 is needed to get fseeko +ACX_CHECK_COMPILER_FLAG_NEEDED(-D_LARGEFILE_SOURCE=1, +[ +#include <stdio.h> +int test() { + int a = fseeko(stdin, 0, 0); + return a; +} +], [CFLAGS="$CFLAGS -D_LARGEFILE_SOURCE=1"]) +]) + +dnl Check getaddrinfo. +dnl Works on linux, solaris, bsd and windows(links winsock). +dnl defines HAVE_GETADDRINFO, USE_WINSOCK. +AC_DEFUN([ACX_CHECK_GETADDRINFO_WITH_INCLUDES], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(for getaddrinfo) +ac_cv_func_getaddrinfo=no +AC_LINK_IFELSE( +[ +#ifdef __cplusplus +extern "C" +{ +#endif +char* getaddrinfo(); +char* (*f) () = getaddrinfo; +#ifdef __cplusplus +} +#endif +int main() { + ; + return 0; +} +], +dnl this case on linux, solaris, bsd +[ac_cv_func_getaddrinfo="yes"], +dnl no quick getaddrinfo, try mingw32 and winsock2 library. +ORIGLIBS="$LIBS" +LIBS="$LIBS -lws2_32" +AC_LINK_IFELSE( +AC_LANG_PROGRAM( +[ +#ifdef HAVE_WS2TCPIP_H +#include <ws2tcpip.h> +#endif +], +[ + (void)getaddrinfo(NULL, NULL, NULL, NULL); +] +), +[ +ac_cv_func_getaddrinfo="yes" +dnl already: LIBS="$LIBS -lws2_32" +AC_DEFINE(USE_WINSOCK, 1, [Whether the windows socket API is used]) +USE_WINSOCK="1" +], +[ +ac_cv_func_getaddrinfo="no" +LIBS="$ORIGLIBS" +]) +) + +AC_MSG_RESULT($ac_cv_func_getaddrinfo) +if test $ac_cv_func_getaddrinfo = yes; then + AC_DEFINE(HAVE_GETADDRINFO, 1, [Whether getaddrinfo is available]) +fi +])dnl Endof AC_CHECK_GETADDRINFO_WITH_INCLUDES + +dnl check if a function is deprecated. defines DEPRECATED_func in config.h. +dnl $1: function name +dnl $2: C-statement that calls the function. +dnl $3: includes for the program. +dnl $4: executes if yes +dnl $5: executes if no +AC_DEFUN([ACX_FUNC_DEPRECATED], +[ +AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(if $1 is deprecated) +cache=`echo $1 | sed 'y%.=/+-%___p_%'` +AC_CACHE_VAL(cv_cc_deprecated_$cache, +[ +echo '$3' >conftest.c +echo 'void f(){ $2 }' >>conftest.c +if test -z "`$CC -c conftest.c 2>&1 | grep deprecated`"; then +eval "cv_cc_deprecated_$cache=no" +else +eval "cv_cc_deprecated_$cache=yes" +fi +rm -f conftest conftest.o conftest.c +]) +if eval "test \"`echo '$cv_cc_deprecated_'$cache`\" = yes"; then +AC_MSG_RESULT(yes) +AC_DEFINE_UNQUOTED(AS_TR_CPP([DEPRECATED_$1]), 1, [Whether $1 is deprecated]) +: +$4 +else +AC_MSG_RESULT(no) +: +$5 +fi +])dnl end of ACX_FUNC_DEPRECATED + +dnl check if select and nonblocking sockets actually work. +dnl Needs fork(2) and select(2). +dnl defines NONBLOCKING_IS_BROKEN, and if that is true multiple reads from +dnl a nonblocking socket do not work, a new call to select is necessary. +AC_DEFUN([ACX_CHECK_NONBLOCKING_BROKEN], +[ +AC_MSG_CHECKING([if nonblocking sockets work]) +AC_RUN_IFELSE(AC_LANG_PROGRAM([ +#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 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); +]]), [ + AC_MSG_RESULT([yes]) +], [ + AC_MSG_RESULT([no]) + AC_DEFINE([NONBLOCKING_IS_BROKEN], 1, [Define if the network stack does not fully support nonblocking io (causes lower performance).]) +], [ + AC_MSG_RESULT([crosscompile(yes)]) +]) +])dnl End of ACX_CHECK_NONBLOCKING_BROKEN + +dnl Check if mkdir has one or two arguments. +dnl defines MKDIR_HAS_ONE_ARG +AC_DEFUN([ACX_MKDIR_ONE_ARG], +[ +AC_MSG_CHECKING([whether mkdir has one arg]) +AC_TRY_COMPILE([ +#include <stdio.h> +#include <unistd.h> +#ifdef HAVE_WINSOCK2_H +#include <winsock2.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +], [ + (void)mkdir("directory"); +], +AC_MSG_RESULT(yes) +AC_DEFINE(MKDIR_HAS_ONE_ARG, 1, [Define if mkdir has one argument.]) +, +AC_MSG_RESULT(no) +) +])dnl end of ACX_MKDIR_ONE_ARG + +dnl Check for ioctlsocket function. works on mingw32 too. +AC_DEFUN([ACX_FUNC_IOCTLSOCKET], +[ +# check ioctlsocket +AC_MSG_CHECKING(for ioctlsocket) +AC_LINK_IFELSE(AC_LANG_PROGRAM([ +#ifdef HAVE_WINSOCK2_H +#include <winsock2.h> +#endif +], [ + (void)ioctlsocket(0, 0, NULL); +]), [ +AC_MSG_RESULT(yes) +AC_DEFINE(HAVE_IOCTLSOCKET, 1, [if the function 'ioctlsocket' is available]) +],[AC_MSG_RESULT(no)]) +])dnl end of ACX_FUNC_IOCTLSOCKET + +dnl Define fallback for fseeko and ftello if needed. +AC_DEFUN([AHX_CONFIG_FSEEKO], +[ +#ifndef HAVE_FSEEKO +#define fseeko fseek +#define ftello ftell +#endif /* HAVE_FSEEKO */ +]) + +dnl Define RAND_MAX if not defined +AC_DEFUN([AHX_CONFIG_RAND_MAX], +[ +#ifndef RAND_MAX +#define RAND_MAX 2147483647 +#endif +]) + +dnl Define MAXHOSTNAMELEN if not defined +AC_DEFUN([AHX_CONFIG_MAXHOSTNAMELEN], +[ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif +]) + +dnl Define IPV6_MIN_MTU if not defined +AC_DEFUN([AHX_CONFIG_IPV6_MIN_MTU], +[ +#ifndef IPV6_MIN_MTU +#define IPV6_MIN_MTU 1280 +#endif /* IPV6_MIN_MTU */ +]) + +dnl provide snprintf, vsnprintf compat prototype +dnl $1: unique name for compat code +AC_DEFUN([AHX_CONFIG_SNPRINTF], +[ +#ifndef HAVE_SNPRINTF +#define snprintf snprintf_$1 +#define vsnprintf vsnprintf_$1 +#include <stdarg.h> +int snprintf (char *str, size_t count, const char *fmt, ...); +int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); +#endif /* HAVE_SNPRINTF */ +]) + +dnl provide inet_pton compat prototype. +dnl $1: unique name for compat code +AC_DEFUN([AHX_CONFIG_INET_PTON], +[ +#ifndef HAVE_INET_PTON +#define inet_pton inet_pton_$1 +int inet_pton(int af, const char* src, void* dst); +#endif /* HAVE_INET_PTON */ +]) + +dnl provide inet_ntop compat prototype. +dnl $1: unique name for compat code +AC_DEFUN([AHX_CONFIG_INET_NTOP], +[ +#ifndef HAVE_INET_NTOP +#define inet_ntop inet_ntop_$1 +const char *inet_ntop(int af, const void *src, char *dst, size_t size); +#endif +]) + +dnl provide inet_aton compat prototype. +dnl $1: unique name for compat code +AC_DEFUN([AHX_CONFIG_INET_ATON], +[ +#ifndef HAVE_INET_ATON +#define inet_aton inet_aton_$1 +int inet_aton(const char *cp, struct in_addr *addr); +#endif +]) + +dnl provide memmove compat prototype. +dnl $1: unique name for compat code +AC_DEFUN([AHX_CONFIG_MEMMOVE], +[ +#ifndef HAVE_MEMMOVE +#define memmove memmove_$1 +void *memmove(void *dest, const void *src, size_t n); +#endif +]) + +dnl provide strlcpy compat prototype. +dnl $1: unique name for compat code +AC_DEFUN([AHX_CONFIG_STRLCPY], +[ +#ifndef HAVE_STRLCPY +#define strlcpy strlcpy_$1 +size_t strlcpy(char *dst, const char *src, size_t siz); +#endif +]) + +dnl provide gmtime_r compat prototype. +dnl $1: unique name for compat code +AC_DEFUN([AHX_CONFIG_GMTIME_R], +[ +#ifndef HAVE_GMTIME_R +#define gmtime_r gmtime_r_$1 +struct tm *gmtime_r(const time_t *timep, struct tm *result); +#endif +]) + +dnl provide w32 compat definition for sleep +AC_DEFUN([AHX_CONFIG_W32_SLEEP], +[ +#ifndef HAVE_SLEEP +#define sleep(x) Sleep((x)*1000) /* on win32 */ +#endif /* HAVE_SLEEP */ +]) + +dnl provide w32 compat definition for usleep +AC_DEFUN([AHX_CONFIG_W32_USLEEP], +[ +#ifndef HAVE_USLEEP +#define usleep(x) Sleep((x)/1000 + 1) /* on win32 */ +#endif /* HAVE_USLEEP */ +]) + +dnl provide w32 compat definition for random +AC_DEFUN([AHX_CONFIG_W32_RANDOM], +[ +#ifndef HAVE_RANDOM +#define random rand /* on win32, for tests only (bad random) */ +#endif /* HAVE_RANDOM */ +]) + +dnl provide w32 compat definition for srandom +AC_DEFUN([AHX_CONFIG_W32_SRANDOM], +[ +#ifndef HAVE_SRANDOM +#define srandom(x) srand(x) /* on win32, for tests only (bad random) */ +#endif /* HAVE_SRANDOM */ +]) + +dnl provide w32 compat definition for FD_SET_T +AC_DEFUN([AHX_CONFIG_W32_FD_SET_T], +[ +/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */ +#ifdef HAVE_WINSOCK2_H +#define FD_SET_T (u_int) +#else +#define FD_SET_T +#endif +]) + +dnl Remove an extension flag from CFLAGS, define replacement to be made. +dnl Used by ACX_STRIP_EXT_FLAGS. +dnl $1: the name of the flag, for example -D_GNU_SOURCE. +AC_DEFUN([ACX_CFLAGS_STRIP], +[ + if echo $CFLAGS | grep " $1" >/dev/null 2>&1; then + CFLAGS="`echo $CFLAGS | sed -e 's/ $1//g'`" + AC_DEFINE(AS_TR_CPP(OMITTED_$1), 1, Put $1 define in config.h) + fi +]) + +dnl Remove EXT flags from the CFLAGS and set them to be defined in config.h +dnl use with ACX_DETERMINE_EXT_FLAGS. +AC_DEFUN([ACX_STRIP_EXT_FLAGS], +[ + AC_MSG_NOTICE([Stripping extension flags...]) + ACX_CFLAGS_STRIP(-D_GNU_SOURCE) + ACX_CFLAGS_STRIP(-D_BSD_SOURCE) + ACX_CFLAGS_STRIP(-D__EXTENSIONS__) + ACX_CFLAGS_STRIP(-D_POSIX_C_SOURCE=200112) + ACX_CFLAGS_STRIP(-D_XOPEN_SOURCE=600) + ACX_CFLAGS_STRIP(-D_XOPEN_SOURCE_EXTENDED=1) + ACX_CFLAGS_STRIP(-D_ALL_SOURCE) + ACX_CFLAGS_STRIP(-D_LARGEFILE_SOURCE=1) +]) dnl End of ACX_STRIP_EXT_FLAGS + +dnl define one omitted flag for config.h +dnl $1: flag name. -D_GNU_SOURCE +dnl $2: replacement define. _GNU_SOURCE +dnl $3: define value, 1 +AC_DEFUN([AHX_CONFIG_FLAG_OMITTED], +[#if defined($1) && !defined($2) +#define $2 $3 +[#]endif ]) + +dnl Wrapper for AHX_CONFIG_FLAG_OMITTED for -D style flags +dnl $1: the -DNAME or -DNAME=value string. +AC_DEFUN([AHX_CONFIG_FLAG_EXT], +[AHX_CONFIG_FLAG_OMITTED(AS_TR_CPP(OMITTED_$1),m4_bpatsubst(m4_bpatsubst($1,-D,),=.*$,),m4_if(m4_bregexp($1,=),-1,1,m4_bpatsubst($1,^.*=,))) +]) + +dnl config.h part to define omitted cflags, use with ACX_STRIP_EXT_FLAGS. +AC_DEFUN([AHX_CONFIG_EXT_FLAGS], +[AHX_CONFIG_FLAG_EXT(-D_GNU_SOURCE) +AHX_CONFIG_FLAG_EXT(-D_BSD_SOURCE) +AHX_CONFIG_FLAG_EXT(-D__EXTENSIONS__) +AHX_CONFIG_FLAG_EXT(-D_POSIX_C_SOURCE=200112) +AHX_CONFIG_FLAG_EXT(-D_XOPEN_SOURCE=600) +AHX_CONFIG_FLAG_EXT(-D_XOPEN_SOURCE_EXTENDED=1) +AHX_CONFIG_FLAG_EXT(-D_ALL_SOURCE) +AHX_CONFIG_FLAG_EXT(-D_LARGEFILE_SOURCE=1) +]) + +dnl End of file diff --git a/usr.sbin/nsd/answer.c b/usr.sbin/nsd/answer.c new file mode 100644 index 00000000000..198d51f2431 --- /dev/null +++ b/usr.sbin/nsd/answer.c @@ -0,0 +1,91 @@ +/* + * answer.c -- manipulating query answers and encoding them. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <string.h> + +#include "answer.h" +#include "packet.h" +#include "query.h" + +void +answer_init(answer_type *answer) +{ + answer->rrset_count = 0; +} + +int +answer_add_rrset(answer_type *answer, rr_section_type section, + domain_type *domain, rrset_type *rrset) +{ + size_t i; + + assert(section >= ANSWER_SECTION && section < RR_SECTION_COUNT); + assert(domain); + assert(rrset); + + /* Don't add an RRset multiple times. */ + for (i = 0; i < answer->rrset_count; ++i) { + if (answer->rrsets[i] == rrset) { + if (section < answer->section[i]) { + answer->section[i] = section; + return 1; + } else { + return 0; + } + } + } + + if (answer->rrset_count >= MAXRRSPP) { + /* XXX: Generate warning/error? */ + return 0; + } + + answer->section[answer->rrset_count] = section; + answer->domains[answer->rrset_count] = domain; + answer->rrsets[answer->rrset_count] = rrset; + ++answer->rrset_count; + + return 1; +} + +void +encode_answer(query_type *q, const answer_type *answer) +{ + uint16_t counts[RR_SECTION_COUNT]; + rr_section_type section; + size_t i; + + for (section = ANSWER_SECTION; section < RR_SECTION_COUNT; ++section) { + counts[section] = 0; + } + + for (section = ANSWER_SECTION; + !TC(q->packet) && section < RR_SECTION_COUNT; + ++section) + { + for (i = 0; !TC(q->packet) && i < answer->rrset_count; ++i) { + if (answer->section[i] == section) { + counts[section] += packet_encode_rrset( + q, + answer->domains[i], + answer->rrsets[i], + section); + } + } + } + + ANCOUNT_SET(q->packet, counts[ANSWER_SECTION]); + NSCOUNT_SET(q->packet, counts[AUTHORITY_SECTION]); + ARCOUNT_SET(q->packet, + counts[ADDITIONAL_A_SECTION] + + counts[ADDITIONAL_AAAA_SECTION] + + counts[ADDITIONAL_OTHER_SECTION]); +} diff --git a/usr.sbin/nsd/answer.h b/usr.sbin/nsd/answer.h new file mode 100644 index 00000000000..acb3665af11 --- /dev/null +++ b/usr.sbin/nsd/answer.h @@ -0,0 +1,47 @@ +/* + * answer.h -- manipulating query answers and encoding them. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _ANSWER_H_ +#define _ANSWER_H_ + +#include <sys/types.h> + +#include "dns.h" +#include "namedb.h" +#include "packet.h" +#include "query.h" + +/* + * Structure used to keep track of RRsets that need to be stored in + * the answer packet. + */ +typedef struct answer answer_type; +struct answer { + size_t rrset_count; + rrset_type *rrsets[MAXRRSPP]; + domain_type *domains[MAXRRSPP]; + rr_section_type section[MAXRRSPP]; +}; + + +void encode_answer(query_type *q, const answer_type *answer); + + +void answer_init(answer_type *answer); + +/* + * Add the specified RRset to the answer in the specified section. If + * the RRset is already present and in the same (or "higher") section + * return 0, otherwise return 1. + */ +int answer_add_rrset(answer_type *answer, rr_section_type section, + domain_type *domain, rrset_type *rrset); + + +#endif /* _ANSWER_H_ */ diff --git a/usr.sbin/nsd/axfr.c b/usr.sbin/nsd/axfr.c new file mode 100644 index 00000000000..0bec4877f97 --- /dev/null +++ b/usr.sbin/nsd/axfr.c @@ -0,0 +1,201 @@ +/* + * axfr.c -- generating AXFR responses. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include "axfr.h" +#include "dns.h" +#include "packet.h" +#include "options.h" + +#define AXFR_TSIG_SIGN_EVERY_NTH 96 /* tsig sign every N packets. */ + +query_state_type +query_axfr(struct nsd *nsd, struct query *query) +{ + domain_type *closest_match; + domain_type *closest_encloser; + int exact; + int added; + uint16_t total_added = 0; + + if (query->axfr_is_done) + return QUERY_PROCESSED; + + if (query->maxlen > AXFR_MAX_MESSAGE_LEN) + query->maxlen = AXFR_MAX_MESSAGE_LEN; + + assert(!query_overflow(query)); +#ifdef TSIG + /* only keep running values for most packets */ + query->tsig_prepare_it = 0; + query->tsig_update_it = 1; + if(query->tsig_sign_it) { + /* prepare for next updates */ + query->tsig_prepare_it = 1; + query->tsig_sign_it = 0; + } +#endif /* TSIG */ + + if (query->axfr_zone == NULL) { + /* Start AXFR. */ + exact = namedb_lookup(nsd->db, + query->qname, + &closest_match, + &closest_encloser); + + query->domain = closest_encloser; + query->axfr_zone = domain_find_zone(closest_encloser); + + if (!exact + || query->axfr_zone == NULL + || query->axfr_zone->apex != query->domain) + { + /* No SOA no transfer */ + RCODE_SET(query->packet, RCODE_REFUSE); + return QUERY_PROCESSED; + } + + query->axfr_current_domain + = (domain_type *) rbtree_first(nsd->db->domains->names_to_domains); + query->axfr_current_rrset = NULL; + query->axfr_current_rr = 0; +#ifdef TSIG + if(query->tsig.status == TSIG_OK) { + query->tsig_sign_it = 1; /* sign first packet in stream */ + } +#endif /* TSIG */ + + query_add_compression_domain(query, query->domain, QHEADERSZ); + + assert(query->axfr_zone->soa_rrset->rr_count == 1); + added = packet_encode_rr(query, + query->axfr_zone->apex, + &query->axfr_zone->soa_rrset->rrs[0]); + if (!added) { + /* XXX: This should never happen... generate error code? */ + abort(); + } + ++total_added; + } else { + /* + * Query name and EDNS need not be repeated after the + * first response packet. + */ + query->edns.status = EDNS_NOT_PRESENT; + buffer_set_limit(query->packet, QHEADERSZ); + QDCOUNT_SET(query->packet, 0); + query_prepare_response(query); + } + + /* Add zone RRs until answer is full. */ + assert(query->axfr_current_domain); + + while ((rbnode_t *) query->axfr_current_domain != RBTREE_NULL) { + if (!query->axfr_current_rrset) { + query->axfr_current_rrset = domain_find_any_rrset( + query->axfr_current_domain, + query->axfr_zone); + query->axfr_current_rr = 0; + } + while (query->axfr_current_rrset) { + if (query->axfr_current_rrset != query->axfr_zone->soa_rrset + && query->axfr_current_rrset->zone == query->axfr_zone) + { + while (query->axfr_current_rr < query->axfr_current_rrset->rr_count) { + added = packet_encode_rr( + query, + query->axfr_current_domain, + &query->axfr_current_rrset->rrs[query->axfr_current_rr]); + if (!added) + goto return_answer; + ++total_added; + ++query->axfr_current_rr; + } + } + + query->axfr_current_rrset = query->axfr_current_rrset->next; + query->axfr_current_rr = 0; + } + assert(query->axfr_current_domain); + query->axfr_current_domain + = (domain_type *) rbtree_next((rbnode_t *) query->axfr_current_domain); + } + + /* Add terminating SOA RR. */ + assert(query->axfr_zone->soa_rrset->rr_count == 1); + added = packet_encode_rr(query, + query->axfr_zone->apex, + &query->axfr_zone->soa_rrset->rrs[0]); + if (added) { + ++total_added; +#ifdef TSIG + query->tsig_sign_it = 1; /* sign last packet */ +#endif /* TSIG */ + query->axfr_is_done = 1; + } + +return_answer: + ANCOUNT_SET(query->packet, total_added); + NSCOUNT_SET(query->packet, 0); + ARCOUNT_SET(query->packet, 0); + +#ifdef TSIG + /* check if it needs tsig signatures */ + if(query->tsig.status == TSIG_OK) { + if(query->tsig.updates_since_last_prepare >= AXFR_TSIG_SIGN_EVERY_NTH) { + query->tsig_sign_it = 1; + } + } +#endif /* TSIG */ + query_clear_compression_tables(query); + return QUERY_IN_AXFR; +} + +/* + * Answer if this is an AXFR or IXFR query. + */ +query_state_type +answer_axfr_ixfr(struct nsd *nsd, struct query *q) +{ + acl_options_t *acl; + /* Is it AXFR? */ + switch (q->qtype) { + case TYPE_AXFR: + if (q->tcp) { + 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) + { + char address[128]; + + if (addr2ip(q->addr, address, 128)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "addr2ip failed")); + strcpy(address, "[unknown]"); + } + + 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")); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s", + acl?"blocked":"no acl matches")); + RCODE_SET(q->packet, RCODE_REFUSE); + return QUERY_PROCESSED; + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr admitted acl %s %s", + acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY")); + return query_axfr(nsd, q); + } + case TYPE_IXFR: + RCODE_SET(q->packet, RCODE_IMPL); + return QUERY_PROCESSED; + default: + return QUERY_DISCARDED; + } +} diff --git a/usr.sbin/nsd/axfr.h b/usr.sbin/nsd/axfr.h new file mode 100644 index 00000000000..33a68629523 --- /dev/null +++ b/usr.sbin/nsd/axfr.h @@ -0,0 +1,25 @@ +/* + * axfr.h -- generating AXFR responses. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _AXFR_H_ +#define _AXFR_H_ + +#include "nsd.h" +#include "query.h" + +/* + * For optimal compression AXFR response packets are limited in size + * to MAX_COMPRESSION_OFFSET. + */ +#define AXFR_MAX_MESSAGE_LEN MAX_COMPRESSION_OFFSET + +query_state_type answer_axfr_ixfr(struct nsd *nsd, struct query *q); +query_state_type query_axfr(struct nsd *nsd, struct query *query); + +#endif /* _AXFR_H_ */ diff --git a/usr.sbin/nsd/buffer.c b/usr.sbin/nsd/buffer.c new file mode 100644 index 00000000000..67b11183309 --- /dev/null +++ b/usr.sbin/nsd/buffer.c @@ -0,0 +1,131 @@ +/* + * buffer.c -- generic memory buffer . + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> + +#include "buffer.h" + +static void +buffer_cleanup(void *arg) +{ + buffer_type *buffer = (buffer_type *) arg; + assert(!buffer->_fixed); + free(buffer->_data); +} + +buffer_type * +buffer_create(region_type *region, size_t capacity) +{ + buffer_type *buffer + = (buffer_type *) region_alloc(region, sizeof(buffer_type)); + if (!buffer) + return NULL; + + buffer->_data = (uint8_t *) xalloc(capacity); + buffer->_position = 0; + buffer->_limit = buffer->_capacity = capacity; + buffer->_fixed = 0; + buffer_invariant(buffer); + + region_add_cleanup(region, buffer_cleanup, buffer); + + return buffer; +} + +void +buffer_create_from(buffer_type *buffer, void *data, size_t size) +{ + assert(data); + + buffer->_position = 0; + buffer->_limit = buffer->_capacity = size; + buffer->_data = (uint8_t *) data; + buffer->_fixed = 1; + + buffer_invariant(buffer); +} + +void +buffer_clear(buffer_type *buffer) +{ + buffer_invariant(buffer); + + buffer->_position = 0; + buffer->_limit = buffer->_capacity; +} + +void +buffer_flip(buffer_type *buffer) +{ + buffer_invariant(buffer); + + buffer->_limit = buffer->_position; + buffer->_position = 0; +} + +void +buffer_rewind(buffer_type *buffer) +{ + buffer_invariant(buffer); + + buffer->_position = 0; +} + +void +buffer_set_capacity(buffer_type *buffer, size_t capacity) +{ + buffer_invariant(buffer); + assert(buffer->_position <= capacity); + buffer->_data = (uint8_t *) xrealloc(buffer->_data, capacity); + buffer->_limit = buffer->_capacity = capacity; +} + +void +buffer_reserve(buffer_type *buffer, size_t amount) +{ + buffer_invariant(buffer); + assert(!buffer->_fixed); + if (buffer->_capacity < buffer->_position + amount) { + size_t new_capacity = buffer->_capacity * 3 / 2; + if (new_capacity < buffer->_position + amount) { + new_capacity = buffer->_position + amount; + } + buffer_set_capacity(buffer, new_capacity); + } + buffer->_limit = buffer->_capacity; +} + +int +buffer_printf(buffer_type *buffer, const char *format, ...) +{ + va_list args; + int written; + size_t remaining; + + buffer_invariant(buffer); + assert(buffer->_limit == buffer->_capacity); + + remaining = buffer_remaining(buffer); + va_start(args, format); + written = vsnprintf((char *) buffer_current(buffer), remaining, + format, args); + va_end(args); + if (written >= 0 && (size_t) written >= remaining) { + buffer_reserve(buffer, written + 1); + va_start(args, format); + written = vsnprintf((char *) buffer_current(buffer), + buffer_remaining(buffer), + format, args); + va_end(args); + } + buffer->_position += written; + return written; +} diff --git a/usr.sbin/nsd/buffer.h b/usr.sbin/nsd/buffer.h new file mode 100644 index 00000000000..bee7d8b29eb --- /dev/null +++ b/usr.sbin/nsd/buffer.h @@ -0,0 +1,385 @@ +/* + * buffer.h -- generic memory buffer. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + * + * The buffer module implements a generic buffer. The API is based on + * the java.nio.Buffer interface. + */ + +#ifndef _BUFFER_H_ +#define _BUFFER_H_ + +#include <assert.h> +#include <stdarg.h> +#include <string.h> + +#include "region-allocator.h" +#include "util.h" + +typedef struct buffer buffer_type; + +struct buffer +{ + /* + * The current position used for reading/writing. + */ + size_t _position; + + /* + * The read/write limit. + */ + size_t _limit; + + /* + * The amount of data the buffer can contain. + */ + size_t _capacity; + + /* + * The data contained in the buffer. + */ + uint8_t *_data; + + /* + * If the buffer is fixed it cannot be resized. + */ + unsigned _fixed : 1; +}; + +#ifdef NDEBUG +static inline void +buffer_invariant(buffer_type *ATTR_UNUSED(buffer)) +{ +} +#else +static inline void +buffer_invariant(buffer_type *buffer) +{ + assert(buffer); + assert(buffer->_position <= buffer->_limit); + assert(buffer->_limit <= buffer->_capacity); + assert(buffer->_data); +} +#endif + +/* + * Create a new buffer with the specified capacity. + */ +buffer_type *buffer_create(region_type *region, size_t capacity); + +/* + * Create a buffer with the specified data. The data is not copied + * and no memory allocations are done. The buffer is fixed and cannot + * be resized using buffer_reserve(). + */ +void buffer_create_from(buffer_type *buffer, void *data, size_t size); + +/* + * Clear the buffer and make it ready for writing. The buffer's limit + * is set to the capacity and the position is set to 0. + */ +void buffer_clear(buffer_type *buffer); + +/* + * Make the buffer ready for reading the data that has been written to + * the buffer. The buffer's limit is set to the current position and + * the position is set to 0. + */ +void buffer_flip(buffer_type *buffer); + +/* + * Make the buffer ready for re-reading the data. The buffer's + * position is reset to 0. + */ +void buffer_rewind(buffer_type *buffer); + +static inline size_t +buffer_position(buffer_type *buffer) +{ + return buffer->_position; +} + +/* + * Set the buffer's position to MARK. The position must be less than + * or equal to the buffer's limit. + */ +static inline void +buffer_set_position(buffer_type *buffer, size_t mark) +{ + assert(mark <= buffer->_limit); + buffer->_position = mark; +} + +/* + * Change the buffer's position by COUNT bytes. The position must not + * be moved behind the buffer's limit or before the beginning of the + * buffer. + */ +static inline void +buffer_skip(buffer_type *buffer, ssize_t count) +{ + assert(buffer->_position + count <= buffer->_limit); + buffer->_position += count; +} + +static inline size_t +buffer_limit(buffer_type *buffer) +{ + return buffer->_limit; +} + +/* + * Change the buffer's limit. If the buffer's position is greater + * than the new limit the position is set to the limit. + */ +static inline void +buffer_set_limit(buffer_type *buffer, size_t limit) +{ + assert(limit <= buffer->_capacity); + buffer->_limit = limit; + if (buffer->_position > buffer->_limit) + buffer->_position = buffer->_limit; +} + + +static inline size_t +buffer_capacity(buffer_type *buffer) +{ + return buffer->_capacity; +} + +/* + * Change the buffer's capacity. The data is reallocated so any + * pointers to the data may become invalid. The buffer's limit is set + * to the buffer's new capacity. + */ +void buffer_set_capacity(buffer_type *buffer, size_t capacity); + +/* + * Ensure BUFFER can contain at least AMOUNT more bytes. The buffer's + * capacity is increased if necessary using buffer_set_capacity(). + * + * The buffer's limit is always set to the (possibly increased) + * capacity. + */ +void buffer_reserve(buffer_type *buffer, size_t amount); + +/* + * Return a pointer to the data at the indicated position. + */ +static inline uint8_t * +buffer_at(buffer_type *buffer, size_t at) +{ + assert(at <= buffer->_limit); + return buffer->_data + at; +} + +/* + * Return a pointer to the beginning of the buffer (the data at + * position 0). + */ +static inline uint8_t * +buffer_begin(buffer_type *buffer) +{ + return buffer_at(buffer, 0); +} + +/* + * Return a pointer to the end of the buffer (the data at the buffer's + * limit). + */ +static inline uint8_t * +buffer_end(buffer_type *buffer) +{ + return buffer_at(buffer, buffer->_limit); +} + +/* + * Return a pointer to the data at the buffer's current position. + */ +static inline uint8_t * +buffer_current(buffer_type *buffer) +{ + return buffer_at(buffer, buffer->_position); +} + +/* + * The number of bytes remaining between the indicated position and + * the limit. + */ +static inline size_t +buffer_remaining_at(buffer_type *buffer, size_t at) +{ + buffer_invariant(buffer); + assert(at <= buffer->_limit); + return buffer->_limit - at; +} + +/* + * The number of bytes remaining between the buffer's position and + * limit. + */ +static inline size_t +buffer_remaining(buffer_type *buffer) +{ + return buffer_remaining_at(buffer, buffer->_position); +} + +/* + * Check if the buffer has at least COUNT more bytes available. + * Before reading or writing the caller needs to ensure enough space + * is available! + */ +static inline int +buffer_available_at(buffer_type *buffer, size_t at, size_t count) +{ + return count <= buffer_remaining_at(buffer, at); +} + +static inline int +buffer_available(buffer_type *buffer, size_t count) +{ + return buffer_available_at(buffer, buffer->_position, count); +} + +static inline void +buffer_write_at(buffer_type *buffer, size_t at, const void *data, size_t count) +{ + assert(buffer_available_at(buffer, at, count)); + memcpy(buffer->_data + at, data, count); +} + +static inline void +buffer_write(buffer_type *buffer, const void *data, size_t count) +{ + buffer_write_at(buffer, buffer->_position, data, count); + buffer->_position += count; +} + +static inline void +buffer_write_string_at(buffer_type *buffer, size_t at, const char *str) +{ + buffer_write_at(buffer, at, str, strlen(str)); +} + +static inline void +buffer_write_string(buffer_type *buffer, const char *str) +{ + buffer_write(buffer, str, strlen(str)); +} + +static inline void +buffer_write_u8_at(buffer_type *buffer, size_t at, uint8_t data) +{ + assert(buffer_available_at(buffer, at, sizeof(data))); + buffer->_data[at] = data; +} + +static inline void +buffer_write_u8(buffer_type *buffer, uint8_t data) +{ + buffer_write_u8_at(buffer, buffer->_position, data); + buffer->_position += sizeof(data); +} + +static inline void +buffer_write_u16_at(buffer_type *buffer, size_t at, uint16_t data) +{ + assert(buffer_available_at(buffer, at, sizeof(data))); + write_uint16(buffer->_data + at, data); +} + +static inline void +buffer_write_u16(buffer_type *buffer, uint16_t data) +{ + buffer_write_u16_at(buffer, buffer->_position, data); + buffer->_position += sizeof(data); +} + +static inline void +buffer_write_u32_at(buffer_type *buffer, size_t at, uint32_t data) +{ + assert(buffer_available_at(buffer, at, sizeof(data))); + write_uint32(buffer->_data + at, data); +} + +static inline void +buffer_write_u32(buffer_type *buffer, uint32_t data) +{ + buffer_write_u32_at(buffer, buffer->_position, data); + buffer->_position += sizeof(data); +} + +static inline void +buffer_read_at(buffer_type *buffer, size_t at, void *data, size_t count) +{ + assert(buffer_available_at(buffer, at, count)); + memcpy(data, buffer->_data + at, count); +} + +static inline void +buffer_read(buffer_type *buffer, void *data, size_t count) +{ + buffer_read_at(buffer, buffer->_position, data, count); + buffer->_position += count; +} + +static inline uint8_t +buffer_read_u8_at(buffer_type *buffer, size_t at) +{ + assert(buffer_available_at(buffer, at, sizeof(uint8_t))); + return buffer->_data[at]; +} + +static inline uint8_t +buffer_read_u8(buffer_type *buffer) +{ + uint8_t result = buffer_read_u8_at(buffer, buffer->_position); + buffer->_position += sizeof(uint8_t); + return result; +} + +static inline uint16_t +buffer_read_u16_at(buffer_type *buffer, size_t at) +{ + assert(buffer_available_at(buffer, at, sizeof(uint16_t))); + return read_uint16(buffer->_data + at); +} + +static inline uint16_t +buffer_read_u16(buffer_type *buffer) +{ + uint16_t result = buffer_read_u16_at(buffer, buffer->_position); + buffer->_position += sizeof(uint16_t); + return result; +} + +static inline uint32_t +buffer_read_u32_at(buffer_type *buffer, size_t at) +{ + assert(buffer_available_at(buffer, at, sizeof(uint32_t))); + return read_uint32(buffer->_data + at); +} + +static inline uint32_t +buffer_read_u32(buffer_type *buffer) +{ + uint32_t result = buffer_read_u32_at(buffer, buffer->_position); + buffer->_position += sizeof(uint32_t); + return result; +} + +/* + * Print to the buffer, increasing the capacity if required using + * buffer_reserve(). The buffer's position is set to the terminating + * '\0'. Returns the number of characters written (not including the + * terminating '\0'). + */ +int buffer_printf(buffer_type *buffer, const char *format, ...) + ATTR_FORMAT(printf, 2, 3); + +#endif /* _BUFFER_H_ */ diff --git a/usr.sbin/nsd/compat/b64_ntop.c b/usr.sbin/nsd/compat/b64_ntop.c new file mode 100644 index 00000000000..2523537072d --- /dev/null +++ b/usr.sbin/nsd/compat/b64_ntop.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ +#include <config.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define Assert(Cond) if (!(Cond)) abort() + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) { + size_t datalength = 0; + uint8_t input[3]; + uint8_t output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} diff --git a/usr.sbin/nsd/compat/b64_pton.c b/usr.sbin/nsd/compat/b64_pton.c new file mode 100644 index 00000000000..b69bb21bfe3 --- /dev/null +++ b/usr.sbin/nsd/compat/b64_pton.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ +#include <config.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define Assert(Cond) if (!(Cond)) abort() + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(char const *src, uint8_t *target, size_t targsize) +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace((unsigned char)ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) { + /* A non-base64 character. */ + return (-1); + } + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace((unsigned char)ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace((unsigned char)ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/usr.sbin/nsd/compat/basename.c b/usr.sbin/nsd/compat/basename.c new file mode 100644 index 00000000000..cd8600c31ea --- /dev/null +++ b/usr.sbin/nsd/compat/basename.c @@ -0,0 +1,18 @@ +/* Return the basename of a pathname. + This file is in the public domain. */ + +char * +basename (name) + const char *name; +{ + const char *base; + + for (base = name; *name; name++) + { + if (*name == '/') + { + base = name + 1; + } + } + return (char *) base; +} diff --git a/usr.sbin/nsd/compat/fake-rfc2553.c b/usr.sbin/nsd/compat/fake-rfc2553.c new file mode 100644 index 00000000000..91ddf8a809b --- /dev/null +++ b/usr.sbin/nsd/compat/fake-rfc2553.c @@ -0,0 +1,227 @@ +/* From openssh 4.3p2 filename openbsd-compat/fake-rfc2553.h */ +/* + * Copyright (C) 2000-2003 Damien Miller. All rights reserved. + * Copyright (C) 1999 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. + */ + +/* + * Pseudo-implementation of RFC2553 name / address resolution functions + * + * But these functions are not implemented correctly. The minimum subset + * is implemented for ssh use only. For example, this routine assumes + * that ai_family is AF_INET. Don't use it for another purpose. + */ + +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "compat/fake-rfc2553.h" + +#ifndef HAVE_GETNAMEINFO +int getnameinfo(const struct sockaddr *sa, size_t ATTR_UNUSED(salen), char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + struct hostent *hp; + char tmpserv[16]; + + if (serv != NULL) { + snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port)); + if (strlcpy(serv, tmpserv, servlen) >= servlen) + return (EAI_MEMORY); + } + + if (host != NULL) { + if (flags & NI_NUMERICHOST) { + if (strlcpy(host, inet_ntoa(sin->sin_addr), + hostlen) >= hostlen) + return (EAI_MEMORY); + else + return (0); + } else { + hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof(struct in_addr), AF_INET); + if (hp == NULL) + return (EAI_NODATA); + + if (strlcpy(host, hp->h_name, hostlen) >= hostlen) + return (EAI_MEMORY); + else + return (0); + } + } + return (0); +} +#endif /* !HAVE_GETNAMEINFO */ + +#ifndef HAVE_GAI_STRERROR +#ifdef HAVE_CONST_GAI_STRERROR_PROTO +const char * +#else +char * +#endif +gai_strerror(int err) +{ + switch (err) { + case EAI_NODATA: + return ("no address associated with name"); + case EAI_MEMORY: + return ("memory allocation failure."); + case EAI_NONAME: + return ("nodename nor servname provided, or not known"); + default: + return ("unknown/invalid error."); + } +} +#endif /* !HAVE_GAI_STRERROR */ + +#ifndef HAVE_FREEADDRINFO +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + for(; ai != NULL;) { + next = ai->ai_next; + free(ai); + ai = next; + } +} +#endif /* !HAVE_FREEADDRINFO */ + +#ifndef HAVE_GETADDRINFO +static struct +addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints) +{ + struct addrinfo *ai; + + ai = malloc(sizeof(*ai) + sizeof(struct sockaddr_in)); + if (ai == NULL) + return (NULL); + + memset(ai, '\0', sizeof(*ai) + sizeof(struct sockaddr_in)); + + ai->ai_addr = (struct sockaddr *)(ai + 1); + /* XXX -- ssh doesn't use sa_len */ + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr->sa_family = ai->ai_family = AF_INET; + + ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port; + ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr; + + /* XXX: the following is not generally correct, but does what we want */ + if (hints->ai_socktype) + ai->ai_socktype = hints->ai_socktype; + else + ai->ai_socktype = SOCK_STREAM; + + if (hints->ai_protocol) + ai->ai_protocol = hints->ai_protocol; + + return (ai); +} + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct hostent *hp; + struct servent *sp; + struct in_addr in; + int i; + long int port; + u_long addr; + + port = 0; + if (servname != NULL) { + char *cp; + + port = strtol(servname, &cp, 10); + if (port > 0 && port <= 65535 && *cp == '\0') + port = htons(port); + else if ((sp = getservbyname(servname, NULL)) != NULL) + port = sp->s_port; + else + port = 0; + } + + if (hints && hints->ai_flags & AI_PASSIVE) { + addr = htonl(0x00000000); + if (hostname && inet_aton(hostname, &in) != 0) + addr = in.s_addr; + *res = malloc_ai(port, addr, hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + if (!hostname) { + *res = malloc_ai(port, htonl(0x7f000001), hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + if (inet_aton(hostname, &in)) { + *res = malloc_ai(port, in.s_addr, hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + /* Don't try DNS if AI_NUMERICHOST is set */ + if (hints && hints->ai_flags & AI_NUMERICHOST) + return (EAI_NONAME); + + hp = gethostbyname(hostname); + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { + struct addrinfo *cur, *prev; + + cur = prev = *res = NULL; + for (i = 0; hp->h_addr_list[i]; i++) { + struct in_addr *in = (struct in_addr *)hp->h_addr_list[i]; + + cur = malloc_ai(port, in->s_addr, hints); + if (cur == NULL) { + if (*res != NULL) + freeaddrinfo(*res); + return (EAI_MEMORY); + } + if (prev) + prev->ai_next = cur; + else + *res = cur; + + prev = cur; + } + return (0); + } + + return (EAI_NODATA); +} +#endif /* !HAVE_GETADDRINFO */ diff --git a/usr.sbin/nsd/compat/fake-rfc2553.h b/usr.sbin/nsd/compat/fake-rfc2553.h new file mode 100644 index 00000000000..efae6dc1e18 --- /dev/null +++ b/usr.sbin/nsd/compat/fake-rfc2553.h @@ -0,0 +1,181 @@ +/* From openssh 4.3p2 filename openbsd-compat/fake-rfc2553.h */ +/* + * Copyright (C) 2000-2003 Damien Miller. All rights reserved. + * Copyright (C) 1999 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. + */ + +/* + * Pseudo-implementation of RFC2553 name / address resolution functions + * + * But these functions are not implemented correctly. The minimum subset + * is implemented for ssh use only. For example, this routine assumes + * that ai_family is AF_INET. Don't use it for another purpose. + */ + +#ifndef _FAKE_RFC2553_H +#define _FAKE_RFC2553_H + +#include <config.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <limits.h> + +/* + * First, socket and INET6 related definitions + */ +#ifndef HAVE_STRUCT_SOCKADDR_STORAGE +# define _SS_MAXSIZE 128 /* Implementation specific max size */ +# define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr)) +struct sockaddr_storage { + struct sockaddr ss_sa; + char __ss_pad2[_SS_PADSIZE]; +}; +# define ss_family ss_sa.sa_family +#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +#ifndef IN6_IS_ADDR_LOOPBACK +# define IN6_IS_ADDR_LOOPBACK(a) \ + (((uint32_t *)(a))[0] == 0 && ((uint32_t *)(a))[1] == 0 && \ + ((uint32_t *)(a))[2] == 0 && ((uint32_t *)(a))[3] == htonl(1)) +#endif /* !IN6_IS_ADDR_LOOPBACK */ + +#ifndef HAVE_STRUCT_IN6_ADDR +struct in6_addr { + uint8_t s6_addr[16]; +}; +#endif /* !HAVE_STRUCT_IN6_ADDR */ + +#ifndef HAVE_STRUCT_SOCKADDR_IN6 +struct sockaddr_in6 { + unsigned short sin6_family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; +}; +#endif /* !HAVE_STRUCT_SOCKADDR_IN6 */ + +#ifndef AF_INET6 +/* Define it to something that should never appear */ +#define AF_INET6 AF_MAX +#endif + +#ifndef PF_INET +#define PF_INET AF_INET +#endif +#ifndef PF_INET6 +#define PF_INET6 AF_INET6 +#endif + +/* + * Next, RFC2553 name / address resolution API + */ + +#ifndef NI_NUMERICHOST +# define NI_NUMERICHOST (1) +#endif +#ifndef NI_NAMEREQD +# define NI_NAMEREQD (1<<1) +#endif +#ifndef NI_NUMERICSERV +# define NI_NUMERICSERV (1<<2) +#endif + +#ifndef AI_PASSIVE +# define AI_PASSIVE (1) +#endif +#ifndef AI_CANONNAME +# define AI_CANONNAME (1<<1) +#endif +#ifndef AI_NUMERICHOST +# define AI_NUMERICHOST (1<<2) +#endif + +#ifndef NI_MAXSERV +# define NI_MAXSERV 32 +#endif /* !NI_MAXSERV */ +#ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +#endif /* !NI_MAXHOST */ + +#ifndef INT_MAX +#define INT_MAX 0xffffffff +#endif + +#ifndef EAI_NODATA +# define EAI_NODATA (INT_MAX - 1) +#endif +#ifndef EAI_MEMORY +# define EAI_MEMORY (INT_MAX - 2) +#endif +#ifndef EAI_NONAME +# define EAI_NONAME (INT_MAX - 3) +#endif +#ifndef EAI_SYSTEM +# define EAI_SYSTEM (INT_MAX - 4) +#endif + +#ifndef HAVE_STRUCT_ADDRINFO +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif /* !HAVE_STRUCT_ADDRINFO */ + +#ifndef HAVE_GETADDRINFO +#ifdef getaddrinfo +# undef getaddrinfo +#endif +#define getaddrinfo(a,b,c,d) (ssh_getaddrinfo(a,b,c,d)) +int getaddrinfo(const char *, const char *, + const struct addrinfo *, struct addrinfo **); +#endif /* !HAVE_GETADDRINFO */ + +#if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO) +#define gai_strerror(a) (ssh_gai_strerror(a)) +char *gai_strerror(int); +#endif /* !HAVE_GAI_STRERROR */ + +#ifndef HAVE_FREEADDRINFO +#define freeaddrinfo(a) (ssh_freeaddrinfo(a)) +void freeaddrinfo(struct addrinfo *); +#endif /* !HAVE_FREEADDRINFO */ + +#ifndef HAVE_GETNAMEINFO +#define getnameinfo(a,b,c,d,e,f,g) (ssh_getnameinfo(a,b,c,d,e,f,g)) +int getnameinfo(const struct sockaddr *, size_t, char *, size_t, + char *, size_t, int); +#endif /* !HAVE_GETNAMEINFO */ + +#endif /* !_FAKE_RFC2553_H */ + diff --git a/usr.sbin/nsd/compat/inet_aton.c b/usr.sbin/nsd/compat/inet_aton.c new file mode 100644 index 00000000000..7eb8e623d95 --- /dev/null +++ b/usr.sbin/nsd/compat/inet_aton.c @@ -0,0 +1,178 @@ +/* From openssh4.3p2 compat/inet_aton.c */ +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the University 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 REGENTS 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. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/* OPENBSD ORIGINAL: lib/libc/net/inet_addr.c */ + +#include <config.h> + +#if !defined(HAVE_INET_ATON) + +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> + +#if 0 +/* + * Ascii internet address interpretation routine. + * The value returned is in network order. + */ +in_addr_t +inet_addr(const char *cp) +{ + struct in_addr val; + + if (inet_aton(cp, &val)) + return (val.s_addr); + return (INADDR_NONE); +} +#endif + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(const char *cp, struct in_addr *addr) +{ + uint32_t val; + int base, n; + char c; + unsigned int parts[4]; + unsigned int *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!isdigit((int)c)) + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + for (;;) { + if (isascii((int)c) && isdigit((int)c)) { + val = (val * base) + (c - '0'); + c = *++cp; + } else if (base == 16 && isascii((int)c) && isxdigit((int)c)) { + val = (val << 4) | + (c + 10 - (islower((int)c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!isascii((int)c) || !isspace((int)c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if ((val > 0xffffff) || (parts[0] > 0xff)) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if ((val > 0xffff) || (parts[0] > 0xff) || (parts[1] > 0xff)) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if ((val > 0xff) || (parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return (1); +} + +#endif /* !defined(HAVE_INET_ATON) */ diff --git a/usr.sbin/nsd/compat/inet_ntop.c b/usr.sbin/nsd/compat/inet_ntop.c new file mode 100644 index 00000000000..1361733ad08 --- /dev/null +++ b/usr.sbin/nsd/compat/inet_ntop.c @@ -0,0 +1,208 @@ +/* From openssh 4.3p2 compat/inet_ntop.c */ +/* Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/inet_ntop.c */ + +#include <config.h> + +#ifndef HAVE_INET_NTOP + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#ifndef IN6ADDRSZ +#define IN6ADDRSZ 16 /* IPv6 T_AAAA */ +#endif + +#ifndef INT16SZ +#define INT16SZ 2 /* for systems without 16-bit ints */ +#endif + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static const char *inet_ntop4(const u_char *src, char *dst, size_t size); +static const char *inet_ntop6(const u_char *src, char *dst, size_t size); + +/* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ +const char * +inet_ntop(int af, const void *src, char *dst, size_t size) +{ + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); + case AF_INET6: + return (inet_ntop6(src, dst, size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } + /* NOTREACHED */ +} + +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address, more or less like inet_ntoa() + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(const u_char *src, char *dst, size_t size) +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int l; + + l = snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]); + if (l <= 0 || l >= (int)size) { + errno = ENOSPC; + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); +} + +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(const u_char *src, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; + char *tp, *ep; + struct { int base, len; } best, cur; + u_int words[IN6ADDRSZ / INT16SZ]; + int i; + int advance; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + ep = tmp + sizeof(tmp); + for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) + return (NULL); + tp += strlen(tp); + break; + } + advance = snprintf(tp, ep - tp, "%x", words[i]); + if (advance <= 0 || advance >= ep - tp) + return (NULL); + tp += advance; + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + if (tp + 1 >= ep) + return (NULL); + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); +} + +#endif /* !HAVE_INET_NTOP */ diff --git a/usr.sbin/nsd/compat/inet_pton.c b/usr.sbin/nsd/compat/inet_pton.c new file mode 100644 index 00000000000..91987917a49 --- /dev/null +++ b/usr.sbin/nsd/compat/inet_pton.c @@ -0,0 +1,226 @@ +/* $KAME: inet_pton.c,v 1.5 2001/08/20 02:32:40 itojun Exp $ */ + +/* Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include <config.h> + +#include <string.h> +#include <stdio.h> +#include <errno.h> + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static int inet_pton4 (const char *src, uint8_t *dst); +static int inet_pton6 (const char *src, uint8_t *dst); + +/* + * + * The definitions we might miss. + * + */ +#ifndef NS_INT16SZ +#define NS_INT16SZ 2 +#endif + +#ifndef NS_IN6ADDRSZ +#define NS_IN6ADDRSZ 16 +#endif + +#ifndef NS_INADDRSZ +#define NS_INADDRSZ 4 +#endif + +/* int + * inet_pton(af, src, dst) + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * return: + * 1 if the address was valid for the specified address family + * 0 if the address wasn't valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * author: + * Paul Vixie, 1996. + */ +int +inet_pton(af, src, dst) + int af; + const char *src; + void *dst; +{ + switch (af) { + case AF_INET: + return (inet_pton4(src, dst)); + case AF_INET6: + return (inet_pton6(src, dst)); + default: + errno = EAFNOSUPPORT; + return (-1); + } + /* NOTREACHED */ +} + +/* int + * inet_pton4(src, dst) + * like inet_aton() but without all the hexadecimal and shorthand. + * return: + * 1 if `src' is a valid dotted quad, else 0. + * notice: + * does not touch `dst' unless it's returning 1. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton4(src, dst) + const char *src; + uint8_t *dst; +{ + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + uint8_t tmp[NS_INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) { + uint32_t new = *tp * 10 + (pch - digits); + + if (new > 255) + return (0); + *tp = new; + if (! saw_digit) { + if (++octets > 4) + return (0); + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + + memcpy(dst, tmp, NS_INADDRSZ); + return (1); +} + +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it's returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton6(src, dst) + const char *src; + uint8_t *dst; +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + uint8_t tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + uint32_t val; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + return (0); + colonp = tp; + continue; + } + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (uint8_t) (val >> 8) & 0xff; + *tp++ = (uint8_t) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + inet_pton4(curtok, tp) > 0) { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if (saw_xdigit) { + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (uint8_t) (val >> 8) & 0xff; + *tp++ = (uint8_t) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memcpy(dst, tmp, NS_IN6ADDRSZ); + return (1); +} diff --git a/usr.sbin/nsd/compat/malloc.c b/usr.sbin/nsd/compat/malloc.c new file mode 100644 index 00000000000..c32b3f9a359 --- /dev/null +++ b/usr.sbin/nsd/compat/malloc.c @@ -0,0 +1,22 @@ +/* Just a replacement, if the original malloc is not + GNU-compliant. See autoconf documentation. */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#undef malloc + +#include <sys/types.h> + +void *malloc (); + +/* Allocate an N-byte block of memory from the heap. + If N is zero, allocate a 1-byte block. */ + +void * +rpl_malloc (size_t n) +{ + if (n == 0) + n = 1; + return malloc (n); +} diff --git a/usr.sbin/nsd/compat/memmove.c b/usr.sbin/nsd/compat/memmove.c new file mode 100644 index 00000000000..0035bbf7533 --- /dev/null +++ b/usr.sbin/nsd/compat/memmove.c @@ -0,0 +1,43 @@ +/* + * memmove.c: memmove compat implementation. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. +*/ + +#include <config.h> +#include <stdlib.h> + +void *memmove(void *dest, const void *src, size_t n); + +void *memmove(void *dest, const void *src, size_t n) +{ + uint8_t* from = (uint8_t*) src; + uint8_t* to = (uint8_t*) dest; + + if (from == to || n == 0) + return dest; + if (to > from && to-from < (int)n) { + /* to overlaps with from */ + /* <from......> */ + /* <to........> */ + /* copy in reverse, to avoid overwriting from */ + int i; + for(i=n-1; i>=0; i--) + to[i] = from[i]; + return dest; + } + if (from > to && from-to < (int)n) { + /* to overlaps with from */ + /* <from......> */ + /* <to........> */ + /* copy forwards, to avoid overwriting from */ + size_t i; + for(i=0; i<n; i++) + to[i] = from[i]; + return dest; + } + memcpy(dest, src, n); + return dest; +} diff --git a/usr.sbin/nsd/compat/pselect.c b/usr.sbin/nsd/compat/pselect.c new file mode 100644 index 00000000000..524cf2837a0 --- /dev/null +++ b/usr.sbin/nsd/compat/pselect.c @@ -0,0 +1,44 @@ +/* + * Like select(2) but set the signals to block while waiting in + * select. This version is not entirely race condition safe. Only + * operating system support can make it so. + */ + +#include <config.h> + +#include <sys/time.h> +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <unistd.h> +#include <signal.h> + +int +pselect (int n, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + const struct timespec *timeout, + const sigset_t *sigmask) +{ + int result; + sigset_t saved_sigmask; + struct timeval saved_timeout; + + if (sigmask && sigprocmask(SIG_SETMASK, sigmask, &saved_sigmask) == -1) + return -1; + + if (timeout) { + saved_timeout.tv_sec = timeout->tv_sec; + saved_timeout.tv_usec = timeout->tv_nsec / 1000; + result = select(n, readfds, writefds, exceptfds, &saved_timeout); + } else { + result = select(n, readfds, writefds, exceptfds, NULL); + } + + if (sigmask && sigprocmask(SIG_SETMASK, &saved_sigmask, NULL) == -1) + return -1; + + return result; +} diff --git a/usr.sbin/nsd/compat/snprintf.c b/usr.sbin/nsd/compat/snprintf.c new file mode 100644 index 00000000000..674cc09c81d --- /dev/null +++ b/usr.sbin/nsd/compat/snprintf.c @@ -0,0 +1,770 @@ +#include <config.h> + +#ifndef HAVE_SNPRINTF + +#include <ctype.h> +#include <sys/types.h> + +/* Define this as a fall through, HAVE_STDARG_H is probably already set */ + +#define HAVE_VARARGS_H + +/************************************************************** + * 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. + * + * 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. + * + **************************************************************/ + + +/* varargs declarations: */ + +#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 + +int snprintf (char *str, size_t count, const char *fmt, ...); +int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); + +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 ); + +int vsnprintf (char *str, size_t count, const char *fmt, va_list args) +{ + str[0] = 0; + dopr(str, count, fmt, args); + return(strlen(str)); +} + +/* VARARGS3 */ +#ifdef HAVE_STDARGS +int snprintf (char *str,size_t count,const char *fmt,...) +#else +int snprintf (va_alist) va_dcl +#endif +{ +#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 + */ + +/* 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 + + /* 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 + + intpart = ufvalue; + + /* + * 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)); + + if (fracpart >= pow10 (max)) + { + intpart++; + fracpart -= pow10 (max); + } + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); +#endif + + /* 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 (fplace > 0) + dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); + + while (zpadlen > 0) + { + dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + + 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); +} +#endif /* SNPRINTF_TEST */ + +#endif /* !HAVE_SNPRINTF */ diff --git a/usr.sbin/nsd/compat/strlcpy.c b/usr.sbin/nsd/compat/strlcpy.c new file mode 100644 index 00000000000..acd306a151c --- /dev/null +++ b/usr.sbin/nsd/compat/strlcpy.c @@ -0,0 +1,57 @@ +/* from openssh 4.3p2 compat/strlcpy.c */ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ + +#include <config.h> +#ifndef HAVE_STRLCPY + +#include <sys/types.h> +#include <string.h> + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +#endif /* !HAVE_STRLCPY */ diff --git a/usr.sbin/nsd/compat/strptime.c b/usr.sbin/nsd/compat/strptime.c new file mode 100644 index 00000000000..0c61cc9008a --- /dev/null +++ b/usr.sbin/nsd/compat/strptime.c @@ -0,0 +1,349 @@ +/** strptime workaround (for oa macos leopard) + * This strptime follows the man strptime (2001-11-12) + * conforming to SUSv2, POSIX.1-2001 + * + * This very simple version of strptime has no: + * - E alternatives + * - O alternatives + * - Glibc additions + * - Does not process week numbers + * - Does not properly processes year day + * + * LICENSE + * Copyright (c) 2008, NLnet Labs, Matthijs Mekking + * All rights reserved. + * + * 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 NLnetLabs 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 COPYRIGHT OWNER 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. + **/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_CONFIG_H +#include <time.h> +#endif + +#ifndef STRPTIME_WORKS + +#define TM_YEAR_BASE 1900 + +#include <ctype.h> +#include <string.h> + +static const char *abb_weekdays[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL +}; +static const char *full_weekdays[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", NULL +}; +static const char *abb_months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL +}; +static const char *full_months[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", NULL +}; +static const char *ampm[] = { + "am", "pm", NULL +}; + +static int +match_string(const char **buf, const char **strs) +{ + int i = 0; + + for (i = 0; strs[i] != NULL; i++) { + int len = strlen(strs[i]); + if (strncasecmp (*buf, strs[i], len) == 0) { + *buf += len; + return i; + } + } + return -1; +} + +static int +str2int(const char **buf, int max) +{ + int ret=0, count=0; + + while (*buf[0] != '\0' && isdigit(*buf[0]) && count<max) { + ret = ret*10 + (*buf[0] - '0'); + (*buf)++; + count++; + } + + if (!count) + return -1; + return ret; +} + +/** Converts the character string s to values which are stored in tm + * using the format specified by format + **/ +char * +nsd_strptime(const char *s, const char *format, struct tm *tm) +{ + int c, alt_format, ret; + int split_year = 0; + + while ((c = *format) != '\0') { + alt_format = 0; + + /* whitespace, literal or format */ + if (isspace(c)) { /* whitespace */ + /** whitespace matches zero or more whitespace characters in the + * input string. + **/ + while (isspace(*s)) + s++; + } + else if (c == '%') { /* format */ + format++; + c = *format; + switch (c) { + case '%': /* %% is converted to % */ + if (*s != c) { + return NULL; + } + s++; + break; + case 'a': /* weekday name, abbreviated or full */ + case 'A': + ret = match_string(&s, full_weekdays); + if (ret < 0) + ret = match_string(&s, abb_weekdays); + if (ret < 0) { + return NULL; + } + tm->tm_wday = ret; + break; + case 'b': /* month name, abbreviated or full */ + case 'B': + case 'h': + ret = match_string(&s, full_months); + if (ret < 0) + ret = match_string(&s, abb_months); + if (ret < 0) { + return NULL; + } + tm->tm_mon = ret; + break; + case 'c': /* date and time representation */ + if (!(s = nsd_strptime(s, "%x %X", tm))) { + return NULL; + } + break; + case 'C': /* century number */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 99) { /* must be in [00,99] */ + return NULL; + } + + if (split_year) { + tm->tm_year = ret*100 + (tm->tm_year%100); + } + else { + tm->tm_year = ret*100 - TM_YEAR_BASE; + split_year = 1; + } + break; + case 'd': /* day of month */ + case 'e': + ret = str2int(&s, 2); + if (ret < 1 || ret > 31) { /* must be in [01,31] */ + return NULL; + } + tm->tm_mday = ret; + break; + case 'D': /* equivalent to %m/%d/%y */ + if (!(s = nsd_strptime(s, "%m/%d/%y", tm))) { + return NULL; + } + break; + case 'H': /* hour */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 23) { /* must be in [00,23] */ + return NULL; + } + tm->tm_hour = ret; + break; + case 'I': /* 12hr clock hour */ + ret = str2int(&s, 2); + if (ret < 1 || ret > 12) { /* must be in [01,12] */ + return NULL; + } + if (ret == 12) /* actually [0,11] */ + ret = 0; + tm->tm_hour = ret; + break; + case 'j': /* day of year */ + ret = str2int(&s, 2); + if (ret < 1 || ret > 366) { /* must be in [001,366] */ + return NULL; + } + tm->tm_yday = ret; + break; + case 'm': /* month */ + ret = str2int(&s, 2); + if (ret < 1 || ret > 12) { /* must be in [01,12] */ + return NULL; + } + /* months go from 0-11 */ + tm->tm_mon = (ret-1); + break; + case 'M': /* minute */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 59) { /* must be in [00,59] */ + return NULL; + } + tm->tm_min = ret; + break; + case 'n': /* arbitrary whitespace */ + case 't': + while (isspace(*s)) + s++; + break; + case 'p': /* am pm */ + ret = match_string(&s, ampm); + if (ret < 0) { + return NULL; + } + if (tm->tm_hour < 0 || tm->tm_hour > 11) { /* %I */ + return NULL; + } + + if (ret == 1) /* pm */ + tm->tm_hour += 12; + break; + case 'r': /* equivalent of %I:%M:%S %p */ + if (!(s = nsd_strptime(s, "%I:%M:%S %p", tm))) { + return NULL; + } + break; + case 'R': /* equivalent of %H:%M */ + if (!(s = nsd_strptime(s, "%H:%M", tm))) { + return NULL; + } + break; + case 'S': /* seconds */ + ret = str2int(&s, 2); + /* 60 may occur for leap seconds */ + /* earlier 61 was also allowed */ + if (ret < 0 || ret > 60) { /* must be in [00,60] */ + return NULL; + } + tm->tm_sec = ret; + break; + case 'T': /* equivalent of %H:%M:%S */ + if (!(s = nsd_strptime(s, "%H:%M:%S", tm))) { + return NULL; + } + break; + case 'U': /* week number, with the first Sun of Jan being w1 */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 53) { /* must be in [00,53] */ + return NULL; + } + /** it is hard (and not necessary for nsd) to determine time + * data from week number. + **/ + break; + case 'w': /* day of week */ + ret = str2int(&s, 1); + if (ret < 0 || ret > 6) { /* must be in [0,6] */ + return NULL; + } + tm->tm_wday = ret; + break; + case 'W': /* week number, with the first Mon of Jan being w1 */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 53) { /* must be in [00,53] */ + return NULL; + } + /** it is hard (and not necessary for nsd) to determine time + * data from week number. + **/ + break; + case 'x': /* date format */ + if (!(s = nsd_strptime(s, "%m/%d/%y", tm))) { + return NULL; + } + break; + case 'X': /* time format */ + if (!(s = nsd_strptime(s, "%H:%M:%S", tm))) { + return NULL; + } + break; + case 'y': /* last two digits of a year */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 99) { /* must be in [00,99] */ + return NULL; + } + if (split_year) { + tm->tm_year = ((tm->tm_year/100) * 100) + ret; + } + else { + split_year = 1; + + /** currently: + * if in [0,68] we are in 21th century, + * if in [69,99] we are in 20th century. + **/ + if (ret < 69) /* 2000 */ + ret += 100; + tm->tm_year = ret; + } + break; + case 'Y': /* year */ + ret = str2int(&s, 4); + if (ret < 0 || ret > 9999) { + return NULL; + } + tm->tm_year = ret - TM_YEAR_BASE; + break; + case '\0': + default: /* unsupported, cannot match format */ + return NULL; + break; + } + } + else { /* literal */ + /* if input cannot match format, return NULL */ + if (*s != c) + return NULL; + s++; + } + + format++; + } + + /* return pointer to remainder of s */ + return (char*) s; +} + +#endif /* STRPTIME_WORKS */ diff --git a/usr.sbin/nsd/config.h.in b/usr.sbin/nsd/config.h.in new file mode 100644 index 00000000000..da3477a4994 --- /dev/null +++ b/usr.sbin/nsd/config.h.in @@ -0,0 +1,629 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define this to enable BIND8 like NSTATS & XSTATS. */ +#undef BIND8_STATS + +/* Pathname to the NSD configuration file */ +#undef CONFIGFILE + +/* Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work + */ +#undef DARWIN_BROKEN_SETREUID + +/* Pathname to the NSD database */ +#undef DBFILE + +/* Pathname to the NSD diff transfer journal file. */ +#undef DIFFFILE + +/* Define this to enable DNSSEC (RFCs 4033, 4034, and 4035) support. */ +#undef DNSSEC + +/* Define to the default maximum message length with EDNS. */ +#undef EDNS_MAX_MESSAGE_LEN + +/* Define to the default facility for syslog. */ +#undef FACILITY + +/* Define to 1 if you have the `alarm' function. */ +#undef HAVE_ALARM + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +#undef HAVE_ARPA_INET_H + +/* Whether the C compiler accepts the "format" attribute */ +#undef HAVE_ATTR_FORMAT + +/* Whether the C compiler accepts the "unused" attribute */ +#undef HAVE_ATTR_UNUSED + +/* Define to 1 if you have the `b64_ntop' function. */ +#undef HAVE_B64_NTOP + +/* Define to 1 if you have the `b64_pton' function. */ +#undef HAVE_B64_PTON + +/* Define to 1 if you have the `basename' function. */ +#undef HAVE_BASENAME + +/* Define to 1 if your system has a working `chown' function. */ +#undef HAVE_CHOWN + +/* Define to 1 if you have the `chroot' function. */ +#undef HAVE_CHROOT + +/* if time.h provides ctime_r prototype */ +#undef HAVE_CTIME_R_PROTO + +/* Define to 1 if you have the `dup2' function. */ +#undef HAVE_DUP2 + +/* Define to 1 if you have the `endpwent' function. */ +#undef HAVE_ENDPWENT + +/* 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 <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `freeaddrinfo' function. */ +#undef HAVE_FREEADDRINFO + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#undef HAVE_FSEEKO + +/* Define to 1 if you have the `gai_strerror' function. */ +#undef HAVE_GAI_STRERROR + +/* Define to 1 if you have the `getaddrinfo' function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `getnameinfo' function. */ +#undef HAVE_GETNAMEINFO + +/* Define to 1 if you have the `getpwnam' function. */ +#undef HAVE_GETPWNAM + +/* Define to 1 if you have the <grp.h> header file. */ +#undef HAVE_GRP_H + +/* Define to 1 if you have the `inet_aton' function. */ +#undef HAVE_INET_ATON + +/* Define to 1 if you have the `inet_ntop' function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have the `inet_pton' function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the `initgroups' function. */ +#undef HAVE_INITGROUPS + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +#undef HAVE_LIBCRYPTO + +/* Define to 1 if you have the <limits.h> header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the `memcpy' function. */ +#undef HAVE_MEMCPY + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have the <netdb.h> header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#undef HAVE_NETINET_IN_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 `setregid' function. */ +#undef HAVE_SETREGID + +/* Define to 1 if you have the `setresgid' function. */ +#undef HAVE_SETRESGID + +/* Define to 1 if you have the `setresuid' function. */ +#undef HAVE_SETRESUID + +/* Define to 1 if you have the `setreuid' function. */ +#undef HAVE_SETREUID + +/* Define to 1 if you have the `setusercontext' function. */ +#undef HAVE_SETUSERCONTEXT + +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the <signal.h> header file. */ +#undef HAVE_SIGNAL_H + +/* Define to 1 if you have the `sigprocmask' function. */ +#undef HAVE_SIGPROCMASK + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define if you have the SSL libraries installed. */ +#undef HAVE_SSL + +/* Define to 1 if you have the <stdarg.h> header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the <stddef.h> header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* Define to 1 if you have the `strptime' function. */ +#undef HAVE_STRPTIME + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* If time.h has a struct timespec (for pselect). */ +#undef HAVE_STRUCT_TIMESPEC + +/* Define to 1 if you have the <syslog.h> header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the <sys/bitypes.h> header file. */ +#undef HAVE_SYS_BITYPES_H + +/* Define to 1 if you have the <sys/param.h> header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the <sys/select.h> header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the <tcpd.h> header file. */ +#undef HAVE_TCPD_H + +/* Define to 1 if you have the <time.h> header file. */ +#undef HAVE_TIME_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define this if you have double va_list definitions. */ +#undef HAVE_VA_LIST_DOUBLE_DEF + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the <vfork.h> header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to the default nsd identity. */ +#undef IDENTITY + +/* Define this to enable IPv6 support. */ +#undef INET6 + +/* Define to the maximum message length to pass to syslog. */ +#undef MAXSYSLOGMSGLEN + +/* Define to the maximum interfaces to serve. */ +#undef MAX_INTERFACES + +/* Undefine this to enable internal runtime checks. */ +#undef NDEBUG + +/* Define this to enable NSEC3 support. */ +#undef NSEC3 + +/* Define this to enable NSID support. */ +#undef NSID + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Pathname to the NSD pidfile */ +#undef PIDFILE + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define this to configure as a root server. */ +#undef ROOT_SERVER + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* strptime is available from time.h with some defines. */ +#undef STRPTIME_NEEDS_DEFINES + +/* use default strptime. */ +#undef STRPTIME_WORKS + +/* Define to the backlog to be used with listen. */ +#undef TCP_BACKLOG + +/* Define to the default maximum message length. */ +#undef TCP_MAX_MESSAGE_LEN + +/* Define to the default tcp port. */ +#undef TCP_PORT + +/* Define to the default tcp timeout. */ +#undef TCP_TIMEOUT + +/* Define this to enable TSIG support. */ +#undef TSIG + +/* Define to the default maximum udp message length. */ +#undef UDP_MAX_MESSAGE_LEN + +/* Define to the default udp port. */ +#undef UDP_PORT + +/* the user name to drop privileges to */ +#undef USER + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Define to the NSD version to answer version.server query. */ +#undef VERSION + +/* Pathname to the NSD xfrd zone timer state file. */ +#undef XFRDFILE + +/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a + `char[]'. */ +#undef YYTEXT_POINTER + +/* NSD default location for zone files. Empty string or NULL to disable. */ +#undef ZONESDIR + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +#undef _LARGEFILE_SOURCE + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef gid_t + +/* in_addr_t */ +#undef in_addr_t + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define "int16_t" to "short" if "int16_t" is missing */ +#undef int16_t + +/* Define "int32_t" to "int" if "int32_t" is missing */ +#undef int32_t + +/* Define "int64_t" to "long long" if "int64_t" is missing */ +#undef int64_t + +/* Define "int8_t" to "char" if "int8_t" is missing */ +#undef int8_t + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `long int' if <sys/types.h> does not define. */ +#undef off_t + +/* Define to `int' if <sys/types.h> does not define. */ +#undef pid_t + +/* Define "sig_atomic_t" to "int" if "sig_atomic_t" is missing */ +#undef sig_atomic_t + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +#undef size_t + +/* Define "socklen_t" to "int" if "socklen_t" is missing */ +#undef socklen_t + +/* Define "ssize_t" to "int" if "ssize_t" is missing */ +#undef ssize_t + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef uid_t + +/* Define "uint16_t" to "unsigned short" if "uint16_t" is missing */ +#undef uint16_t + +/* Define "uint32_t" to "unsigned int" if "uint32_t" is missing */ +#undef uint32_t + +/* Define "uint64_t" to "unsigned long long" if "uint64_t" is missing */ +#undef uint64_t + +/* Define "uint8_t" to "unsigned char" if "uint8_t" is missing */ +#undef uint8_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork + + +/* define before includes as it specifies what standard to use. */ +#if (defined(HAVE_PSELECT) && !defined (HAVE_PSELECT_PROTO)) \ + || !defined (HAVE_CTIME_R_PROTO) \ + || defined (STRPTIME_NEEDS_DEFINES) +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 600 +# endif +# ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112 +# endif +# ifndef _BSD_SOURCE +# define _BSD_SOURCE 1 +# endif +# ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +# endif +# ifndef _STDC_C99 +# define _STDC_C99 1 +# endif +# ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +# endif +#endif + + + +#ifdef HAVE_VA_LIST_DOUBLE_DEF +/* workaround double va_list definition on some platforms */ +# ifndef _VA_LIST_DEFINED +# define _VA_LIST_DEFINED +# endif +#endif + + + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.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 + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + + + +#ifdef HAVE_ATTR_FORMAT +#define ATTR_FORMAT(archetype, string_index, first_to_check) \ + __attribute__ ((format (archetype, string_index, first_to_check))) +#else /* !HAVE_ATTR_FORMAT */ +#define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */ +#endif /* !HAVE_ATTR_FORMAT */ +#if defined(__cplusplus) +#define ATTR_UNUSED(x) +#elif defined(HAVE_ATTR_UNUSED) +#define ATTR_UNUSED(x) x __attribute__((unused)) +#else /* !HAVE_ATTR_UNUSED */ +#define ATTR_UNUSED(x) x +#endif /* !HAVE_ATTR_UNUSED */ + + + +#ifndef IPV6_MIN_MTU +#define IPV6_MIN_MTU 1280 +#endif /* IPV6_MIN_MTU */ + +#ifndef AF_INET6 +#define AF_INET6 28 +#endif /* AF_INET6 */ + + + +/* maximum nesting of included files */ +#define MAXINCLUDES 10 + + + +#ifndef B64_PTON +int b64_ntop(uint8_t const *src, size_t srclength, + char *target, size_t targsize); +#endif /* !B64_PTON */ +#ifndef B64_NTOP +int b64_pton(char const *src, uint8_t *target, size_t targsize); +#endif /* !B64_NTOP */ +#ifndef HAVE_FSEEKO +#define fseeko fseek +#define ftello ftell +#endif /* HAVE_FSEEKO */ +#ifndef HAVE_SNPRINTF +#include <stdarg.h> +int snprintf (char *str, size_t count, const char *fmt, ...); +int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); +#endif /* HAVE_SNPRINTF */ +#ifndef HAVE_INET_PTON +int inet_pton(int af, const char* src, void* dst); +#endif /* HAVE_INET_PTON */ +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, size_t size); +#endif +#ifndef HAVE_INET_ATON +int inet_aton(const char *cp, struct in_addr *addr); +#endif +#ifndef HAVE_MEMMOVE +void *memmove(void *dest, const void *src, size_t n); +#endif +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t siz); +#endif +#ifndef HAVE_GETADDRINFO +#include "compat/fake-rfc2553.h" +#endif +#ifndef HAVE_STRPTIME +#define HAVE_STRPTIME 1 +char *strptime(const char *s, const char *format, struct tm *tm); +#endif +#ifndef STRPTIME_WORKS +#define STRPTIME_WORKS 1 +#define strptime(a,b,c) nsd_strptime((a),(b),(c)) +#endif + +/* provide timespec def if not available */ +#ifndef CONFIG_DEFINES +#define CONFIG_DEFINES +#ifndef HAVE_STRUCT_TIMESPEC +#ifndef __timespec_defined +#define __timespec_defined 1 + struct timespec { + long tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ + }; +#endif /* !__timespec_defined */ +#endif /* !HAVE_STRUCT_TIMESPEC */ +#endif /* !CONFIG_DEFINES */ + diff --git a/usr.sbin/nsd/configlexer.lex b/usr.sbin/nsd/configlexer.lex new file mode 100644 index 00000000000..7b9a8a508a8 --- /dev/null +++ b/usr.sbin/nsd/configlexer.lex @@ -0,0 +1,196 @@ +%{ +/* + * configlexer.lex - lexical analyzer for NSD config file + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <strings.h> + +#include "options.h" +#include "configyyrename.h" +#include "configparser.h" +void c_error(const char *message); + +#define YY_NO_UNPUT + +#if 0 +#define LEXOUT(s) printf s /* used ONLY when debugging */ +#else +#define LEXOUT(s) +#endif + +struct inc_state { + const char* filename; + int line; +}; +static struct inc_state parse_stack[MAXINCLUDES]; +static YY_BUFFER_STATE include_stack[MAXINCLUDES]; +static int config_include_stack_ptr = 0; + +static void config_start_include(const char* filename) +{ + FILE *input; + if(strlen(filename) == 0) { + c_error_msg("empty include file name"); + return; + } + if(config_include_stack_ptr >= MAXINCLUDES) { + c_error_msg("includes nested too deeply, skipped (>%d)", MAXINCLUDES); + return; + } + input = fopen(filename, "r"); + if(!input) { + c_error_msg("cannot open include file '%s': %s", + filename, strerror(errno)); + return; + } + LEXOUT(("switch_to_include_file(%s) ", filename)); + parse_stack[config_include_stack_ptr].filename = cfg_parser->filename; + parse_stack[config_include_stack_ptr].line = cfg_parser->line; + include_stack[config_include_stack_ptr] = YY_CURRENT_BUFFER; + cfg_parser->filename = region_strdup(cfg_parser->opt->region, filename); + cfg_parser->line = 1; + yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE)); + ++config_include_stack_ptr; +} + +static void config_end_include(void) +{ + --config_include_stack_ptr; + cfg_parser->filename = parse_stack[config_include_stack_ptr].filename; + cfg_parser->line = parse_stack[config_include_stack_ptr].line; + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(include_stack[config_include_stack_ptr]); +} + +#ifndef yy_set_bol /* compat definition, for flex 2.4.6 */ +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \ + } +#endif + +%} + +SPACE [ \t] +LETTER [a-zA-Z] +UNQUOTEDLETTER [^\"\n\r \t\\]|\\. +NEWLINE [\r\n] +COMMENT \# +COLON \: +ANY [^\"\n\r\\]|\\. + +%x quotedstring include include_quoted + +%% +{SPACE}* { LEXOUT(("SP ")); /* ignore */ } +{SPACE}*{COMMENT}.* { LEXOUT(("comment(%s) ", yytext)); /* ignore */ } +server{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER;} +name{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NAME;} +ip-address{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} +debug-mode{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DEBUG_MODE;} +hide-version{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_VERSION;} +ip4-only{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP4_ONLY;} +ip6-only{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP6_ONLY;} +database{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DATABASE;} +identity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IDENTITY;} +logfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;} +server-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_COUNT;} +tcp-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_COUNT;} +tcp-query-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_QUERY_COUNT;} +tcp-timeout{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_TIMEOUT;} +ipv4-edns-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IPV4_EDNS_SIZE;} +ipv6-edns-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IPV6_EDNS_SIZE;} +pidfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PIDFILE;} +port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PORT;} +statistics{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_STATISTICS;} +chroot{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CHROOT;} +username{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_USERNAME;} +zonesdir{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONESDIR;} +difffile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DIFFFILE;} +xfrdfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRDFILE;} +xfrd-reload-timeout{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_RELOAD_TIMEOUT;} +verbosity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERBOSITY;} +zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONE;} +zonefile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILE;} +allow-notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_NOTIFY;} +request-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REQUEST_XFR;} +notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY;} +notify-retry{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY_RETRY;} +provide-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PROVIDE_XFR;} +outgoing-interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_INTERFACE;} +allow-axfr-fallback{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_AXFR_FALLBACK;} +key{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_KEY;} +algorithm{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALGORITHM;} +secret{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SECRET;} +AXFR { LEXOUT(("v(%s) ", yytext)); return VAR_AXFR;} +UDP { LEXOUT(("v(%s) ", yytext)); return VAR_UDP;} +{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} + + /* Quoted strings. Strip leading and ending quotes */ +\" { BEGIN(quotedstring); LEXOUT(("QS ")); } +<quotedstring><<EOF>> { + yyerror("EOF inside quoted string"); + BEGIN(INITIAL); +} +<quotedstring>{ANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } +<quotedstring>\n { cfg_parser->line++; yymore(); } +<quotedstring>\" { + LEXOUT(("QE ")); + BEGIN(INITIAL); + yytext[yyleng - 1] = '\0'; + yylval.str = region_strdup(cfg_parser->opt->region, yytext); + return STRING; +} + + /* include: directive */ +include{COLON} { LEXOUT(("v(%s) ", yytext)); BEGIN(include); } +<include><<EOF>> { + yyerror("EOF inside include directive"); + BEGIN(INITIAL); +} +<include>{SPACE}* { LEXOUT(("ISP ")); /* ignore */ } +<include>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} +<include>\" { LEXOUT(("IQS ")); BEGIN(include_quoted); } +<include>{UNQUOTEDLETTER}* { + LEXOUT(("Iunquotedstr(%s) ", yytext)); + config_start_include(yytext); + BEGIN(INITIAL); +} +<include_quoted><<EOF>> { + yyerror("EOF inside quoted string"); + BEGIN(INITIAL); +} +<include_quoted>{ANY}* { LEXOUT(("ISTR(%s) ", yytext)); yymore(); } +<include_quoted>{NEWLINE} { cfg_parser->line++; yymore(); } +<include_quoted>\" { + LEXOUT(("IQE ")); + yytext[yyleng - 1] = '\0'; + config_start_include(yytext); + BEGIN(INITIAL); +} +<INITIAL><<EOF>> { + yy_set_bol(1); /* Set beginning of line, so "^" rules match. */ + if (config_include_stack_ptr == 0) { + yyterminate(); + } else { + fclose(yyin); + config_end_include(); + } +} + +{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext)); + yylval.str = region_strdup(cfg_parser->opt->region, yytext); return STRING; } + +%% diff --git a/usr.sbin/nsd/configparser.y b/usr.sbin/nsd/configparser.y new file mode 100644 index 00000000000..96969a238df --- /dev/null +++ b/usr.sbin/nsd/configparser.y @@ -0,0 +1,470 @@ +/* + * configparser.y -- yacc grammar for NSD configuration files + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +%{ +#include <config.h> + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include "options.h" +#include "configyyrename.h" +int c_lex(void); +void c_error(const char *message); + +#ifdef __cplusplus +extern "C" +#endif /* __cplusplus */ + +/* 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 */ +#else +#define OUTYY(s) +#endif + +%} +%union { + char* str; +} + +%token SPACE LETTER NEWLINE COMMENT COLON ANY ZONESTR +%token <str> STRING +%token VAR_SERVER VAR_NAME VAR_IP_ADDRESS VAR_DEBUG_MODE +%token VAR_IP4_ONLY VAR_IP6_ONLY VAR_DATABASE VAR_IDENTITY VAR_LOGFILE +%token VAR_SERVER_COUNT VAR_TCP_COUNT VAR_PIDFILE VAR_PORT VAR_STATISTICS +%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_ZONEFILE +%token VAR_ZONE +%token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR +%token VAR_NOTIFY_RETRY VAR_OUTGOING_INTERFACE VAR_ALLOW_AXFR_FALLBACK +%token VAR_KEY +%token VAR_ALGORITHM VAR_SECRET +%token VAR_AXFR VAR_UDP +%token VAR_VERBOSITY VAR_HIDE_VERSION + +%% +toplevelvars: /* empty */ | toplevelvars toplevelvar ; +toplevelvar: serverstart contents_server | zonestart contents_zone | + keystart contents_key; + +/* server: declaration */ +serverstart: VAR_SERVER + { OUTYY(("\nP(server:)\n")); + if(server_settings_seen) { + yyerror("duplicate server: element."); + } + server_settings_seen = 1; + } + ; +contents_server: contents_server content_server | ; +content_server: server_ip_address | server_debug_mode | server_ip4_only | + server_ip6_only | server_database | server_identity | server_logfile | + server_server_count | server_tcp_count | server_pidfile | server_port | + 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_ip_address: VAR_IP_ADDRESS STRING + { + OUTYY(("P(server_ip_address:%s)\n", $2)); + if(cfg_parser->current_ip_address_option) { + cfg_parser->current_ip_address_option->next = + (ip_address_option_t*)region_alloc( + cfg_parser->opt->region, sizeof(ip_address_option_t)); + cfg_parser->current_ip_address_option = + cfg_parser->current_ip_address_option->next; + cfg_parser->current_ip_address_option->next=0; + } else { + cfg_parser->current_ip_address_option = + (ip_address_option_t*)region_alloc( + cfg_parser->opt->region, sizeof(ip_address_option_t)); + cfg_parser->current_ip_address_option->next=0; + cfg_parser->opt->ip_addresses = cfg_parser->current_ip_address_option; + } + + cfg_parser->current_ip_address_option->address = + region_strdup(cfg_parser->opt->region, $2); + } + ; +server_debug_mode: VAR_DEBUG_MODE STRING + { + OUTYY(("P(server_debug_mode:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->opt->debug_mode = (strcmp($2, "yes")==0); + } + ; +server_verbosity: VAR_VERBOSITY STRING + { + OUTYY(("P(server_verbosity:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else cfg_parser->opt->verbosity = atoi($2); + } + ; +server_hide_version: VAR_HIDE_VERSION STRING + { + OUTYY(("P(server_hide_version:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->opt->hide_version = (strcmp($2, "yes")==0); + } + ; +server_ip4_only: VAR_IP4_ONLY STRING + { + 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); + } + ; +server_ip6_only: VAR_IP6_ONLY STRING + { + 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); + } + ; +server_database: VAR_DATABASE STRING + { + OUTYY(("P(server_database:%s)\n", $2)); + cfg_parser->opt->database = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_identity: VAR_IDENTITY STRING + { + OUTYY(("P(server_identity:%s)\n", $2)); + cfg_parser->opt->identity = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_logfile: VAR_LOGFILE STRING + { + OUTYY(("P(server_logfile:%s)\n", $2)); + cfg_parser->opt->logfile = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_server_count: VAR_SERVER_COUNT STRING + { + OUTYY(("P(server_server_count:%s)\n", $2)); + if(atoi($2) <= 0) + yyerror("number greater than zero expected"); + else cfg_parser->opt->server_count = atoi($2); + } + ; +server_tcp_count: VAR_TCP_COUNT STRING + { + OUTYY(("P(server_tcp_count:%s)\n", $2)); + if(atoi($2) <= 0) + yyerror("number greater than zero expected"); + else cfg_parser->opt->tcp_count = atoi($2); + } + ; +server_pidfile: VAR_PIDFILE STRING + { + OUTYY(("P(server_pidfile:%s)\n", $2)); + cfg_parser->opt->pidfile = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_port: VAR_PORT STRING + { + OUTYY(("P(server_port:%s)\n", $2)); + cfg_parser->opt->port = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_statistics: VAR_STATISTICS STRING + { + OUTYY(("P(server_statistics:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else cfg_parser->opt->statistics = atoi($2); + } + ; +server_chroot: VAR_CHROOT STRING + { + OUTYY(("P(server_chroot:%s)\n", $2)); + cfg_parser->opt->chroot = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_username: VAR_USERNAME STRING + { + OUTYY(("P(server_username:%s)\n", $2)); + cfg_parser->opt->username = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_zonesdir: VAR_ZONESDIR STRING + { + OUTYY(("P(server_zonesdir:%s)\n", $2)); + cfg_parser->opt->zonesdir = 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); + } + ; +server_xfrdfile: VAR_XFRDFILE STRING + { + OUTYY(("P(server_xfrdfile:%s)\n", $2)); + cfg_parser->opt->xfrdfile = region_strdup(cfg_parser->opt->region, $2); + } + ; +server_xfrd_reload_timeout: VAR_XFRD_RELOAD_TIMEOUT STRING + { + OUTYY(("P(server_xfrd_reload_timeout:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + cfg_parser->opt->xfrd_reload_timeout = atoi($2); + } + ; +server_tcp_query_count: VAR_TCP_QUERY_COUNT STRING + { + OUTYY(("P(server_tcp_query_count:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + cfg_parser->opt->tcp_query_count = atoi($2); + } + ; +server_tcp_timeout: VAR_TCP_TIMEOUT STRING + { + OUTYY(("P(server_tcp_timeout:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + cfg_parser->opt->tcp_timeout = atoi($2); + } + ; +server_ipv4_edns_size: VAR_IPV4_EDNS_SIZE STRING + { + OUTYY(("P(server_ipv4_edns_size:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + cfg_parser->opt->ipv4_edns_size = atoi($2); + } + ; +server_ipv6_edns_size: VAR_IPV6_EDNS_SIZE STRING + { + OUTYY(("P(server_ipv6_edns_size:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + cfg_parser->opt->ipv6_edns_size = atoi($2); + } + ; + +/* zone: declaration */ +zonestart: VAR_ZONE + { + OUTYY(("\nP(zone:)\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->zonefile) + c_error("previous zone has no zonefile"); + } + cfg_parser->current_zone = zone_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_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_name: VAR_NAME STRING + { + OUTYY(("P(zone_name:%s)\n", $2)); +#ifndef NDEBUG + assert(cfg_parser->current_zone); +#endif + cfg_parser->current_zone->name = region_strdup(cfg_parser->opt->region, $2); + } + ; +zone_zonefile: VAR_ZONEFILE STRING + { + OUTYY(("P(zone_zonefile:%s)\n", $2)); +#ifndef NDEBUG + assert(cfg_parser->current_zone); +#endif + cfg_parser->current_zone->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)); + if(cfg_parser->current_allow_notify) + cfg_parser->current_allow_notify->next = acl; + else + cfg_parser->current_zone->allow_notify = acl; + cfg_parser->current_allow_notify = acl; + } + ; +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)); + 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_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)); + 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_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)); + 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_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)); + 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_notify = acl; + } + ; +zone_notify_retry: VAR_NOTIFY_RETRY STRING + { + OUTYY(("P(zone_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); + } + ; +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)); + if(cfg_parser->current_provide_xfr) + cfg_parser->current_provide_xfr->next = acl; + else + cfg_parser->current_zone->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)); + + if(cfg_parser->current_outgoing_interface) + cfg_parser->current_outgoing_interface->next = acl; + else + cfg_parser->current_zone->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)); + 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); + } + ; + +/* key: declaration */ +keystart: VAR_KEY + { + OUTYY(("\nP(key:)\n")); + if(cfg_parser->current_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; + } + cfg_parser->opt->numkeys++; + } + ; +contents_key: contents_key content_key | content_key; +content_key: key_name | key_algorithm | key_secret; +key_name: VAR_NAME STRING + { + 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); + } + ; +key_algorithm: VAR_ALGORITHM STRING + { + OUTYY(("P(key_algorithm:%s)\n", $2)); +#ifndef NDEBUG + assert(cfg_parser->current_key); +#endif + cfg_parser->current_key->algorithm = region_strdup(cfg_parser->opt->region, $2); + } + ; +key_secret: VAR_SECRET STRING + { + 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); + } + ; + +%% + +/* parse helper routines could be here */ diff --git a/usr.sbin/nsd/configure b/usr.sbin/nsd/configure new file mode 100644 index 00000000000..03131a1e220 --- /dev/null +++ b/usr.sbin/nsd/configure @@ -0,0 +1,12143 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.63 for NSD 3.2.4. +# +# 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 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell bug-autoconf@gnu.org about your system, + echo including any error possibly output before this message. + echo This can help us improve future autoconf versions. + echo Configuration will now proceed without shell functions. +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # 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). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... 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'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + 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 + +# 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'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 </dev/null 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME='NSD' +PACKAGE_TARNAME='nsd' +PACKAGE_VERSION='3.2.4' +PACKAGE_STRING='NSD 3.2.4' +PACKAGE_BUGREPORT='nsd-bugs@nlnetlabs.nl' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='LTLIBOBJS +HAVE_SSL +LIBOBJS +YFLAGS +YACC +LEXLIB +LEX_OUTPUT_ROOT +LEX +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +LN_S +AWK +user +xfrdfile +difffile +zonesdir +piddir +dbdir +dbfile +pidfile +kill_priority +start_priority +nsd_conf_file +configdir +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +with_configdir +with_nsd_conf_file +with_start_priority +with_kill_priority +with_pidfile +with_dbfile +with_zonesdir +with_difffile +with_xfrdfile +with_user +enable_largefile +with_facility +with_max_interfaces +with_tcp_timeout +enable_root_server +enable_ipv6 +enable_dnssec +enable_bind8_stats +enable_checking +enable_tsig +with_ssl +enable_nsec3 +enable_nsid +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +YACC +YFLAGS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { $as_echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { $as_echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 + { (exit 1); exit 1; }; } ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +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 +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { $as_echo "$as_me: error: working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { $as_echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +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.4 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/nsd] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of NSD 3.2.4:";; + esac + cat <<\_ACEOF + +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-largefile omit support for large files + --enable-root-server Configure NSD as a root server + --disable-ipv6 Disables IPv6 support + --disable-dnssec Disable DNSSEC support. + --enable-bind8-stats Enables BIND8 like NSTATS & XSTATS + --enable-checking Enable internal runtime checks + --disable-tsig Disable TSIG support + --disable-nsec3 Disable NSEC3 support + --enable-nsid Enable NSID support + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --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-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-user=username User name or ID to answer the queries with + --with-facility=name Syslog default facility (LOG_DAEMON) + --with-max_interfaces=number + Limit on the number of ip-addresses that may be + specified + --with-tcp-timeout=number + Limit the default tcp timeout + --with-ssl=pathname enable SSL (will check /usr/local/ssl /usr/lib/ssl + /usr/ssl /usr/pkg /usr/sfw /usr/local /usr) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + YACC The `Yet Another C Compiler' implementation to use. Defaults to + the first program found out of: `bison -y', `byacc', `yacc'. + YFLAGS The list of arguments that will be passed by default to $YACC. + This script will default YFLAGS to the empty string to avoid a + default value of `-d' given by some make applications. + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to <nsd-bugs@nlnetlabs.nl>. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +NSD configure 3.2.4 +generated by GNU Autoconf 2.63 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +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.4, which was +generated by GNU Autoconf 2.63. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + ac_site_file1=$CONFIG_SITE +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test -r "$ac_site_file"; then + { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_config_headers="$ac_config_headers config.h" + + +CFLAGS="$CFLAGS" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +if test -z "$ac_file"; then + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + fi + fi +fi +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest$ac_cv_exeext +{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +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 + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* 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); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:$LINENO: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + 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 +# 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 +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:$LINENO: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + 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 +# 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 +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + if test "${ac_cv_header_minix_config_h+set}" = set; then + { $as_echo "$as_me:$LINENO: checking for minix/config.h" >&5 +$as_echo_n "checking for minix/config.h... " >&6; } +if test "${ac_cv_header_minix_config_h+set}" = set; then + $as_echo_n "(cached) " >&6 +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +$as_echo "$ac_cv_header_minix_config_h" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking minix/config.h usability" >&5 +$as_echo_n "checking minix/config.h usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <minix/config.h> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking minix/config.h presence" >&5 +$as_echo_n "checking minix/config.h presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <minix/config.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: minix/config.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: minix/config.h: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: minix/config.h: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: minix/config.h: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------ ## +## Report this to nsd-bugs@nlnetlabs.nl ## +## ------------------------------------ ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for minix/config.h" >&5 +$as_echo_n "checking for minix/config.h... " >&6; } +if test "${ac_cv_header_minix_config_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_header_minix_config_h=$ac_header_preproc +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +$as_echo "$ac_cv_header_minix_config_h" >&6; } + +fi +if test "x$ac_cv_header_minix_config_h" = x""yes; then + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_SOURCE 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_1_SOURCE 2 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _MINIX 1 +_ACEOF + + fi + + + + { $as_echo "$as_me:$LINENO: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if test "${ac_cv_safe_to_define___extensions__+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_safe_to_define___extensions__=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_safe_to_define___extensions__=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + cat >>confdefs.h <<\_ACEOF +#define __EXTENSIONS__ 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _ALL_SOURCE 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _GNU_SOURCE 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _POSIX_PTHREAD_SEMANTICS 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _TANDEM_SOURCE 1 +_ACEOF + + + +case "$prefix" in + NONE) + case "$sysconfdir" in + '${prefix}/etc') + sysconfdir=/etc + ;; + esac + case "$localstatedir" in + '${prefix}/var') + localstatedir=/var + ;; + esac + ;; +esac + +# +# Determine configuration directory +# +configdir=$sysconfdir/nsd + +# Check whether --with-configdir was given. +if test "${with_configdir+set}" = set; then + withval=$with_configdir; configdir=$withval +fi + + + +# +# Determine configuration file +nsd_conf_file=${configdir}/nsd.conf + +# Check whether --with-nsd_conf_file was given. +if test "${with_nsd_conf_file+set}" = set; then + withval=$with_nsd_conf_file; nsd_conf_file=$withval +fi + + +# the eval is to evaluate shell expansion twice, once +# for $nsd_conf_file and once for the ${prefix} within it. + +cat >>confdefs.h <<_ACEOF +#define CONFIGFILE "`eval echo $nsd_conf_file`" +_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 + + + +# +# Database directory +# +dbdir=${localstatedir}/db/nsd + +# +# Determine the pidfile location. Check if /var/run exists, if so set pidfile +# to /var/run/nsd.pid by default +# +if test -d ${localstatedir}/run; then + pidfile=${localstatedir}/run/nsd.pid +else + pidfile=${dbdir}/nsd.pid +fi + +# Check whether --with-pidfile was given. +if test "${with_pidfile+set}" = set; then + withval=$with_pidfile; pidfile=$withval +fi + + + +cat >>confdefs.h <<_ACEOF +#define PIDFILE "`eval echo $pidfile`" +_ACEOF + + +# +# Determine location of nsd.db +# +dbfile=${dbdir}/nsd.db + +# Check whether --with-dbfile was given. +if test "${with_dbfile+set}" = set; then + withval=$with_dbfile; dbfile=$withval +fi + + + +cat >>confdefs.h <<_ACEOF +#define DBFILE "`eval echo $dbfile`" +_ACEOF + + +dbdir=`dirname $dbfile` + + +piddir=`dirname $pidfile` + + +# +# Determine the default directory for the zone files +# +zonesdir=$configdir + +# Check whether --with-zonesdir was given. +if test "${with_zonesdir+set}" = set; then + withval=$with_zonesdir; zonesdir=$withval +fi + + + +cat >>confdefs.h <<_ACEOF +#define ZONESDIR "`eval echo $zonesdir`" +_ACEOF + + +# default diff file location. +difffile=${dbdir}/ixfr.db + +# Check whether --with-difffile was given. +if test "${with_difffile+set}" = set; then + withval=$with_difffile; difffile=$withval +fi + + +cat >>confdefs.h <<_ACEOF +#define DIFFFILE "`eval echo $difffile`" +_ACEOF + + + +# 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 + + + +# +# Determine the user name to drop privileges to +# +user=nsd + +# Check whether --with-user was given. +if test "${with_user+set}" = set; then + withval=$with_user; user=$withval +fi + + + +cat >>confdefs.h <<_ACEOF +#define USER "$user" +_ACEOF + + +# Checks for programs. +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_AWK+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:$LINENO: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +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 + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* 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); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +{ $as_echo "$as_me:$LINENO: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +$as_echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # 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 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. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + +done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +for ac_prog in flex lex +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_LEX+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$LEX"; then + ac_cv_prog_LEX="$LEX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_LEX="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +LEX=$ac_cv_prog_LEX +if test -n "$LEX"; then + { $as_echo "$as_me:$LINENO: result: $LEX" >&5 +$as_echo "$LEX" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$LEX" && break +done +test -n "$LEX" || LEX=":" + +if test "x$LEX" != "x:"; then + cat >conftest.l <<_ACEOF +%% +a { ECHO; } +b { REJECT; } +c { yymore (); } +d { yyless (1); } +e { yyless (input () != 0); } +f { unput (yytext[0]); } +. { BEGIN INITIAL; } +%% +#ifdef YYTEXT_POINTER +extern char *yytext; +#endif +int +main (void) +{ + return ! yylex () + ! yywrap (); +} +_ACEOF +{ (ac_try="$LEX conftest.l" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$LEX conftest.l") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ $as_echo "$as_me:$LINENO: checking lex output file root" >&5 +$as_echo_n "checking lex output file root... " >&6; } +if test "${ac_cv_prog_lex_root+set}" = set; then + $as_echo_n "(cached) " >&6 +else + +if test -f lex.yy.c; then + ac_cv_prog_lex_root=lex.yy +elif test -f lexyy.c; then + ac_cv_prog_lex_root=lexyy +else + { { $as_echo "$as_me:$LINENO: error: cannot find output from $LEX; giving up" >&5 +$as_echo "$as_me: error: cannot find output from $LEX; giving up" >&2;} + { (exit 1); exit 1; }; } +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_lex_root" >&5 +$as_echo "$ac_cv_prog_lex_root" >&6; } +LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root + +if test -z "${LEXLIB+set}"; then + { $as_echo "$as_me:$LINENO: checking lex library" >&5 +$as_echo_n "checking lex library... " >&6; } +if test "${ac_cv_lib_lex+set}" = set; then + $as_echo_n "(cached) " >&6 +else + + ac_save_LIBS=$LIBS + ac_cv_lib_lex='none needed' + for ac_lib in '' -lfl -ll; do + LIBS="$ac_lib $ac_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +`cat $LEX_OUTPUT_ROOT.c` +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_lex=$ac_lib +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + test "$ac_cv_lib_lex" != 'none needed' && break + done + LIBS=$ac_save_LIBS + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_lex" >&5 +$as_echo "$ac_cv_lib_lex" >&6; } + test "$ac_cv_lib_lex" != 'none needed' && LEXLIB=$ac_cv_lib_lex +fi + + +{ $as_echo "$as_me:$LINENO: checking whether yytext is a pointer" >&5 +$as_echo_n "checking whether yytext is a pointer... " >&6; } +if test "${ac_cv_prog_lex_yytext_pointer+set}" = set; then + $as_echo_n "(cached) " >&6 +else + # POSIX says lex can declare yytext either as a pointer or an array; the +# default is implementation-dependent. Figure out which it is, since +# not all implementations provide the %pointer and %array declarations. +ac_cv_prog_lex_yytext_pointer=no +ac_save_LIBS=$LIBS +LIBS="$LEXLIB $ac_save_LIBS" +cat >conftest.$ac_ext <<_ACEOF +#define YYTEXT_POINTER 1 +`cat $LEX_OUTPUT_ROOT.c` +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_prog_lex_yytext_pointer=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_save_LIBS + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_lex_yytext_pointer" >&5 +$as_echo "$ac_cv_prog_lex_yytext_pointer" >&6; } +if test $ac_cv_prog_lex_yytext_pointer = yes; then + +cat >>confdefs.h <<\_ACEOF +#define YYTEXT_POINTER 1 +_ACEOF + +fi +rm -f conftest.l $LEX_OUTPUT_ROOT.c + +fi +for ac_prog in 'bison -y' byacc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_YACC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$YACC"; then + ac_cv_prog_YACC="$YACC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +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 + ac_cv_prog_YACC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +YACC=$ac_cv_prog_YACC +if test -n "$YACC"; then + { $as_echo "$as_me:$LINENO: result: $YACC" >&5 +$as_echo "$YACC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$YACC" && break +done +test -n "$YACC" || YACC="yacc" + + + + + + + +# Checks for typedefs, structures, and compiler characteristics. + + +{ $as_echo "$as_me:$LINENO: checking whether $CC supports -O2" >&5 +$as_echo_n "checking whether $CC supports -O2... " >&6; } +cache=`echo O2 | sed 'y%.=/+-%___p_%'` +if { as_var=cv_prog_cc_flag_$cache; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + +echo 'void f(){}' >conftest.c +if test -z "`$CC -O2 -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:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +: + +else +{ $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +: + CFLAGS="-g" +fi + +{ $as_echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if test "${ac_cv_c_const+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset cs; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this 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; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_const=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_const=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const /**/ +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if test "${ac_cv_c_inline+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_inline=$ac_kw +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +{ $as_echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 +$as_echo_n "checking for uid_t in sys/types.h... " >&6; } +if test "${ac_cv_type_uid_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 +$as_echo "$ac_cv_type_uid_t" >&6; } +if test $ac_cv_type_uid_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uid_t int +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define gid_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for pid_t" >&5 +$as_echo_n "checking for pid_t... " >&6; } +if test "${ac_cv_type_pid_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_pid_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof ((pid_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_pid_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +$as_echo "$ac_cv_type_pid_t" >&6; } +if test "x$ac_cv_type_pid_t" = x""yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for size_t" >&5 +$as_echo_n "checking for size_t... " >&6; } +if test "${ac_cv_type_size_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_size_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof ((size_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_size_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +$as_echo "$ac_cv_type_size_t" >&6; } +if test "x$ac_cv_type_size_t" = x""yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for off_t" >&5 +$as_echo_n "checking for off_t... " >&6; } +if test "${ac_cv_type_off_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_off_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof (off_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof ((off_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_off_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5 +$as_echo "$ac_cv_type_off_t" >&6; } +if test "x$ac_cv_type_off_t" = x""yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + + + +{ $as_echo "$as_me:$LINENO: checking whether the C compiler (${CC-cc}) accepts the \"format\" attribute" >&5 +$as_echo_n "checking whether the C compiler (${CC-cc}) accepts the \"format\" attribute... " >&6; } +if test "${ac_cv_c_format_attribute+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_c_format_attribute=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdio.h> +void f (char *format, ...) __attribute__ ((format (printf, 1, 2))); +void (*pf) (char *format, ...) __attribute__ ((format (printf, 1, 2))); + +int +main () +{ + + f ("%s", "str"); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_format_attribute="yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_format_attribute="no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi + + +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_format_attribute" >&5 +$as_echo "$ac_cv_c_format_attribute" >&6; } +if test $ac_cv_c_format_attribute = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ATTR_FORMAT 1 +_ACEOF + +fi + + +{ $as_echo "$as_me:$LINENO: checking whether the C compiler (${CC-cc}) accepts the \"unused\" attribute" >&5 +$as_echo_n "checking whether the C compiler (${CC-cc}) accepts the \"unused\" attribute... " >&6; } +if test "${ac_cv_c_unused_attribute+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_c_unused_attribute=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdio.h> +void f (char *u __attribute__((unused))); + +int +main () +{ + + f ("x"); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_unused_attribute="yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_unused_attribute="no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi + + +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_unused_attribute" >&5 +$as_echo "$ac_cv_c_unused_attribute" >&6; } +if test $ac_cv_c_unused_attribute = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ATTR_UNUSED 1 +_ACEOF + +fi + + +{ $as_echo "$as_me:$LINENO: checking whether ctime_r works with two arguments" >&5 +$as_echo_n "checking whether ctime_r works with two arguments... " >&6; } +if test "${ac_cv_c_ctime_c+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_c_ctime_c=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <time.h> +void testing (void) { time_t clock; char current_time[40]; ctime_r(&clock, current_time); } +int +main () +{ + + testing(); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_ctime_c="yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_ctime_c="no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi + + +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_ctime_c" >&5 +$as_echo "$ac_cv_c_ctime_c" >&6; } +if test $ac_cv_c_ctime_c = no; then + CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS" +fi + + +# Checks for libraries. + +# Check for SSL, original taken from +# http://www.gnu.org/software/ac-archive/htmldoc/check_ssl.html and +# modified for NSD. + +# Checks for header files. +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if test "${ac_cv_header_sys_wait_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/wait.h> +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_sys_wait_h=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_sys_wait_h=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +$as_echo "$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + + + + + + + + + + + + + + + + + + + + +for ac_header in time.h arpa/inet.h signal.h strings.h fcntl.h limits.h netinet/in.h stddef.h sys/param.h sys/socket.h syslog.h unistd.h sys/select.h stdarg.h stdint.h netdb.h sys/bitypes.h tcpd.h grp.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------ ## +## Report this to nsd-bugs@nlnetlabs.nl ## +## ------------------------------------ ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + +{ $as_echo "$as_me:$LINENO: checking for double definition of struct va_list" >&5 +$as_echo_n "checking for double definition of struct va_list... " >&6; } +if test "${ac_cv_c_va_list_def+set}" = set; then + $as_echo_n "(cached) " >&6 +else + +cat >conftest.c <<EOF +#include <stdio.h> +#include <stdarg.h> +int foo(void); +EOF +if test -z "`$CC -Werror -D_XOPEN_SOURCE=600 -c conftest.c 2>&1`"; then +eval "ac_cv_c_va_list_def=no" +else +eval "ac_cv_c_va_list_def=yes" +fi +rm -f conftest* + +fi + +if test $ac_cv_c_va_list_def = yes; then +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +: + +cat >>confdefs.h <<_ACEOF +#define HAVE_VA_LIST_DOUBLE_DEF /**/ +_ACEOF + +else +{ $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +: + +fi + + + + +{ $as_echo "$as_me:$LINENO: checking whether strptime needs defines" >&5 +$as_echo_n "checking whether strptime needs defines... " >&6; } +if test "${ac_cv_c_strptime_needs_defs+set}" = set; then + $as_echo_n "(cached) " >&6 +else + +cat >conftest.c <<EOF +#include <time.h> +void testing (void) { struct tm t; char *timestr; strptime(timestr, "%Y%m", &t); } +EOF +if test -z "`$CC -Wall -Werror -c conftest.c 2>&1`"; then +eval "ac_cv_c_strptime_needs_defs=no" +else +eval "ac_cv_c_strptime_needs_defs=yes" +fi +rm -f conftest* + +fi + + +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_strptime_needs_defs" >&5 +$as_echo "$ac_cv_c_strptime_needs_defs" >&6; } +if test $ac_cv_c_strptime_needs_defs = yes; then + +cat >>confdefs.h <<_ACEOF +#define STRPTIME_NEEDS_DEFINES 1 +_ACEOF + +fi + + +# check wether strptime also works + + +{ $as_echo "$as_me:$LINENO: checking whether strptime works" >&5 +$as_echo_n "checking whether strptime works... " >&6; } +if test c${cross_compiling} = cno; then +if test "$cross_compiling" = yes; then + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#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; } + +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "ac_cv_c_strptime_works=yes" +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +eval "ac_cv_c_strptime_works=no" +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +else +eval "ac_cv_c_strptime_works=maybe" +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_strptime_works" >&5 +$as_echo "$ac_cv_c_strptime_works" >&6; } +if test $ac_cv_c_strptime_works = no; then +case " $LIBOBJS " in + *" strptime.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS strptime.$ac_objext" + ;; +esac + +else + +cat >>confdefs.h <<_ACEOF +#define STRPTIME_WORKS 1 +_ACEOF + +fi + + +{ $as_echo "$as_me:$LINENO: checking for library containing inet_pton" >&5 +$as_echo_n "checking for library containing inet_pton... " >&6; } +if test "${ac_cv_search_inet_pton+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 inet_pton (); +int +main () +{ +return inet_pton (); + ; + return 0; +} +_ACEOF +for ac_lib in '' nsl; 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 + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_search_inet_pton=$ac_res +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_inet_pton+set}" = set; then + break +fi +done +if test "${ac_cv_search_inet_pton+set}" = set; then + : +else + ac_cv_search_inet_pton=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_inet_pton" >&5 +$as_echo "$ac_cv_search_inet_pton" >&6; } +ac_res=$ac_cv_search_inet_pton +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +{ $as_echo "$as_me:$LINENO: checking for library containing socket" >&5 +$as_echo_n "checking for library containing socket... " >&6; } +if test "${ac_cv_search_socket+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +for ac_lib in '' socket; 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 + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_search_socket=$ac_res +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_socket+set}" = set; then + break +fi +done +if test "${ac_cv_search_socket+set}" = set; then + : +else + ac_cv_search_socket=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_socket" >&5 +$as_echo "$ac_cv_search_socket" >&6; } +ac_res=$ac_cv_search_socket +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + + + + +{ $as_echo "$as_me:$LINENO: checking for int8_t" >&5 +$as_echo_n "checking for int8_t... " >&6; } +if test "${ac_cv_type_int8_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])int8_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_int8_t=yes +else + ac_cv_type_int8_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_int8_t" >&5 +$as_echo "$ac_cv_type_int8_t" >&6; } +if test $ac_cv_type_int8_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define int8_t char +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for int16_t" >&5 +$as_echo_n "checking for int16_t... " >&6; } +if test "${ac_cv_type_int16_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_int16_t=yes +else + ac_cv_type_int16_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_int16_t" >&5 +$as_echo "$ac_cv_type_int16_t" >&6; } +if test $ac_cv_type_int16_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define int16_t short +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for int32_t" >&5 +$as_echo_n "checking for int32_t... " >&6; } +if test "${ac_cv_type_int32_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_int32_t=yes +else + ac_cv_type_int32_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_int32_t" >&5 +$as_echo "$ac_cv_type_int32_t" >&6; } +if test $ac_cv_type_int32_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define int32_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for int64_t" >&5 +$as_echo_n "checking for int64_t... " >&6; } +if test "${ac_cv_type_int64_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])int64_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_int64_t=yes +else + ac_cv_type_int64_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_int64_t" >&5 +$as_echo "$ac_cv_type_int64_t" >&6; } +if test $ac_cv_type_int64_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define int64_t long long +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for uint8_t" >&5 +$as_echo_n "checking for uint8_t... " >&6; } +if test "${ac_cv_type_uint8_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])uint8_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_uint8_t=yes +else + ac_cv_type_uint8_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_uint8_t" >&5 +$as_echo "$ac_cv_type_uint8_t" >&6; } +if test $ac_cv_type_uint8_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uint8_t unsigned char +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for uint16_t" >&5 +$as_echo_n "checking for uint16_t... " >&6; } +if test "${ac_cv_type_uint16_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])uint16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_uint16_t=yes +else + ac_cv_type_uint16_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_uint16_t" >&5 +$as_echo "$ac_cv_type_uint16_t" >&6; } +if test $ac_cv_type_uint16_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uint16_t unsigned short +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for uint32_t" >&5 +$as_echo_n "checking for uint32_t... " >&6; } +if test "${ac_cv_type_uint32_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])uint32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_uint32_t=yes +else + ac_cv_type_uint32_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_uint32_t" >&5 +$as_echo "$ac_cv_type_uint32_t" >&6; } +if test $ac_cv_type_uint32_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uint32_t unsigned int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for uint64_t" >&5 +$as_echo_n "checking for uint64_t... " >&6; } +if test "${ac_cv_type_uint64_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])uint64_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_uint64_t=yes +else + ac_cv_type_uint64_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_uint64_t" >&5 +$as_echo "$ac_cv_type_uint64_t" >&6; } +if test $ac_cv_type_uint64_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uint64_t unsigned long long +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for socklen_t" >&5 +$as_echo_n "checking for socklen_t... " >&6; } +if test "${ac_cv_type_socklen_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])socklen_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_socklen_t=yes +else + ac_cv_type_socklen_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_socklen_t" >&5 +$as_echo "$ac_cv_type_socklen_t" >&6; } +if test $ac_cv_type_socklen_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define socklen_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for sig_atomic_t" >&5 +$as_echo_n "checking for sig_atomic_t... " >&6; } +if test "${ac_cv_type_sig_atomic_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])sig_atomic_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_sig_atomic_t=yes +else + ac_cv_type_sig_atomic_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_sig_atomic_t" >&5 +$as_echo "$ac_cv_type_sig_atomic_t" >&6; } +if test $ac_cv_type_sig_atomic_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define sig_atomic_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for ssize_t" >&5 +$as_echo_n "checking for ssize_t... " >&6; } +if test "${ac_cv_type_ssize_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "(^|[^a-zA-Z_0-9])ssize_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + ac_cv_type_ssize_t=yes +else + ac_cv_type_ssize_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_ssize_t" >&5 +$as_echo "$ac_cv_type_ssize_t" >&6; } +if test $ac_cv_type_ssize_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define ssize_t int +_ACEOF + +fi + + +{ $as_echo "$as_me:$LINENO: checking for in_addr_t" >&5 +$as_echo_n "checking for in_addr_t... " >&6; } +if test "${ac_cv_type_in_addr_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_in_addr_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +int +main () +{ +if (sizeof (in_addr_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +int +main () +{ +if (sizeof ((in_addr_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_in_addr_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_in_addr_t" >&5 +$as_echo "$ac_cv_type_in_addr_t" >&6; } +if test "x$ac_cv_type_in_addr_t" = x""yes; then + : +else + +cat >>confdefs.h <<\_ACEOF +#define in_addr_t uint32_t +_ACEOF + +fi + + +# Checks for library functions. + +for ac_header in unistd.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------ ## +## Report this to nsd-bugs@nlnetlabs.nl ## +## ------------------------------------ ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:$LINENO: checking for working chown" >&5 +$as_echo_n "checking for working chown... " >&6; } +if test "${ac_cv_func_chown_works+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_chown_works=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <fcntl.h> + +int +main () +{ + char *f = "conftest.chown"; + struct stat before, after; + + if (creat (f, 0600) < 0) + return 1; + if (stat (f, &before) < 0) + return 1; + if (chown (f, (uid_t) -1, (gid_t) -1) == -1) + return 1; + if (stat (f, &after) < 0) + return 1; + return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_chown_works=yes +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_chown_works=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +rm -f conftest.chown + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_chown_works" >&5 +$as_echo "$ac_cv_func_chown_works" >&6; } +if test $ac_cv_func_chown_works = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CHOWN 1 +_ACEOF + +fi + + +for ac_header in vfork.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------ ## +## Report this to nsd-bugs@nlnetlabs.nl ## +## ------------------------------------ ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in fork vfork +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + { $as_echo "$as_me:$LINENO: checking for working fork" >&5 +$as_echo_n "checking for working fork... " >&6; } +if test "${ac_cv_func_fork_works+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* By Ruediger Kuhlmann. */ + return fork () < 0; + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fork_works=yes +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fork_works=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +$as_echo "$ac_cv_func_fork_works" >&6; } + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { $as_echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + { $as_echo "$as_me:$LINENO: checking for working vfork" >&5 +$as_echo_n "checking for working vfork... " >&6; } +if test "${ac_cv_func_vfork_works+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +$ac_includes_default +#include <sys/wait.h> +#ifdef HAVE_VFORK_H +# include <vfork.h> +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include <vfork.h>, but some compilers + (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + return ( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +$as_echo "$ac_cv_func_vfork_works" >&6; } + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { $as_echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + + +for ac_header in stdlib.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------ ## +## Report this to nsd-bugs@nlnetlabs.nl ## +## ------------------------------------ ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 +$as_echo_n "checking for GNU libc compatible malloc... " >&6; } +if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_malloc_0_nonnull=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if defined STDC_HEADERS || defined HAVE_STDLIB_H +# include <stdlib.h> +#else +char *malloc (); +#endif + +int +main () +{ +return ! malloc (0); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_malloc_0_nonnull=yes +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_malloc_0_nonnull=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 +$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; } +if test $ac_cv_func_malloc_0_nonnull = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 1 +_ACEOF + +else + cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 0 +_ACEOF + + case " $LIBOBJS " in + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" + ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define malloc rpl_malloc +_ACEOF + +fi + + + +{ $as_echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if test "${ac_cv_type_signal+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <signal.h> + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_signal=int +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_signal=void +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + +{ $as_echo "$as_me:$LINENO: checking for _LARGEFILE_SOURCE value needed for large files" >&5 +$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; } +if test "${ac_cv_sys_largefile_source+set}" = set; then + $as_echo_n "(cached) " >&6 +else + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> /* for off_t */ + #include <stdio.h> +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_sys_largefile_source=no; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _LARGEFILE_SOURCE 1 +#include <sys/types.h> /* for off_t */ + #include <stdio.h> +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_sys_largefile_source=1; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + ac_cv_sys_largefile_source=unknown + break +done +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_source" >&5 +$as_echo "$ac_cv_sys_largefile_source" >&6; } +case $ac_cv_sys_largefile_source in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source +_ACEOF +;; +esac +rm -rf conftest* + +# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug +# in glibc 2.1.3, but that breaks too many other things. +# If you want fseeko and ftello with glibc, upgrade to a fixed glibc. +if test $ac_cv_sys_largefile_source != unknown; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_FSEEKO 1 +_ACEOF + +fi + +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if test "${ac_cv_sys_largefile_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_sys_largefile_CC=' -n32'; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if test "${ac_cv_sys_file_offset_bits+set}" = set; then + $as_echo_n "(cached) " >&6 +else + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_sys_file_offset_bits=no; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_sys_file_offset_bits=64; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if test "${ac_cv_sys_large_files+set}" = set; then + $as_echo_n "(cached) " >&6 +else + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_sys_large_files=no; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_sys_large_files=1; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +for ac_func in alarm chroot dup2 endpwent gethostname memset memcpy socket strcasecmp strchr strdup strerror strncasecmp strtol getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask strptime setusercontext initgroups setresuid setreuid setresgid setregid getpwnam +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# check if setreuid en setregid fail, on MacOSX10.4(darwin8). +if echo $build_os | grep darwin8 > /dev/null; then + +cat >>confdefs.h <<\_ACEOF +#define DARWIN_BROKEN_SETREUID 1 +_ACEOF + +fi + +# +# Checking for missing functions we can replace +# + +for ac_func in basename +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in inet_aton +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in inet_pton +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in inet_ntop +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in snprintf +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in strlcpy +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in strptime +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in b64_pton +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in b64_ntop +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in pselect +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +for ac_func in memmove +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case " $LIBOBJS " in + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + ;; +esac + +fi +done + + + +{ $as_echo "$as_me:$LINENO: checking for pselect prototype in sys/select.h" >&5 +$as_echo_n "checking for pselect prototype in sys/select.h... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/select.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "[^a-zA-Z_]*pselect[^a-zA-Z_]" >/dev/null 2>&1; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PSELECT_PROTO 1 +_ACEOF + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f conftest* + + +{ $as_echo "$as_me:$LINENO: checking for ctime_r prototype in time.h" >&5 +$as_echo_n "checking for ctime_r prototype in time.h... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <time.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "[^a-zA-Z_]*ctime_r[^a-zA-Z_]" >/dev/null 2>&1; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CTIME_R_PROTO 1 +_ACEOF + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f conftest* + + +{ $as_echo "$as_me:$LINENO: checking for struct timespec" >&5 +$as_echo_n "checking for struct timespec... " >&6; } +if test "${ac_cv_type_struct_timespec+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_struct_timespec=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +$ac_includes_default +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif + + +int +main () +{ +if (sizeof (struct timespec)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +$ac_includes_default +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif + + +int +main () +{ +if (sizeof ((struct timespec))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_struct_timespec=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_struct_timespec" >&5 +$as_echo "$ac_cv_type_struct_timespec" >&6; } +if test "x$ac_cv_type_struct_timespec" = x""yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STRUCT_TIMESPEC 1 +_ACEOF + +fi + + + +cat >>confdefs.h <<_ACEOF +#define IDENTITY "unidentified server" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION PACKAGE_STRING +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define TCP_BACKLOG 5 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define TCP_PORT "53" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define TCP_MAX_MESSAGE_LEN 65535 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define UDP_PORT "53" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define UDP_MAX_MESSAGE_LEN 512 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define EDNS_MAX_MESSAGE_LEN 4096 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define MAXSYSLOGMSGLEN 512 +_ACEOF + + +facility=LOG_DAEMON + +# Check whether --with-facility was given. +if test "${with_facility+set}" = set; then + withval=$with_facility; facility=$withval +fi + + +cat >>confdefs.h <<_ACEOF +#define FACILITY $facility +_ACEOF + + +max_interfaces=8 + +# Check whether --with-max_interfaces was given. +if test "${with_max_interfaces+set}" = set; then + withval=$with_max_interfaces; max_interfaces=$withval +fi + + +cat >>confdefs.h <<_ACEOF +#define MAX_INTERFACES $max_interfaces +_ACEOF + + +tcp_timeout=120 + +# Check whether --with-tcp_timeout was given. +if test "${with_tcp_timeout+set}" = set; then + withval=$with_tcp_timeout; tcp_timeout=$withval +fi + + +cat >>confdefs.h <<_ACEOF +#define TCP_TIMEOUT $tcp_timeout +_ACEOF + + +# Check whether --enable-root-server was given. +if test "${enable_root_server+set}" = set; then + enableval=$enable_root_server; +fi + +case "$enable_root_server" in + yes) + +cat >>confdefs.h <<_ACEOF +#define ROOT_SERVER /**/ +_ACEOF + + ;; + no|*) + ;; +esac + +# Check whether --enable-ipv6 was given. +if test "${enable_ipv6+set}" = set; then + enableval=$enable_ipv6; +fi + +case "$enable_ipv6" in + no) + ;; + yes|*) + +cat >>confdefs.h <<_ACEOF +#define INET6 /**/ +_ACEOF + + ;; +esac + +# Check whether --enable-dnssec was given. +if test "${enable_dnssec+set}" = set; then + enableval=$enable_dnssec; +fi + +case "$enable_dnssec" in + no) + ;; + yes|*) + +cat >>confdefs.h <<_ACEOF +#define DNSSEC /**/ +_ACEOF + + ;; +esac + +# Check whether --enable-bind8-stats was given. +if test "${enable_bind8_stats+set}" = set; then + enableval=$enable_bind8_stats; +fi + + +case "$enable_bind8_stats" in + yes|'') + +cat >>confdefs.h <<_ACEOF +#define BIND8_STATS /**/ +_ACEOF + + ;; + no|*) + ;; +esac + +# Check whether --enable-checking was given. +if test "${enable_checking+set}" = set; then + enableval=$enable_checking; +fi + +case "$enable_checking" in + yes) + + +{ $as_echo "$as_me:$LINENO: checking whether $CC supports -W" >&5 +$as_echo_n "checking whether $CC supports -W... " >&6; } +cache=`echo W | sed 'y%.=/+-%___p_%'` +if { as_var=cv_prog_cc_flag_$cache; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + +echo 'void f(){}' >conftest.c +if test -z "`$CC -W -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:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +: + CFLAGS="$CFLAGS -W" +else +{ $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +: + +fi + + + +{ $as_echo "$as_me:$LINENO: checking whether $CC supports -Wall" >&5 +$as_echo_n "checking whether $CC supports -Wall... " >&6; } +cache=`echo Wall | sed 'y%.=/+-%___p_%'` +if { as_var=cv_prog_cc_flag_$cache; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + +echo 'void f(){}' >conftest.c +if test -z "`$CC -Wall -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:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +: + CFLAGS="$CFLAGS -Wall" +else +{ $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +: + +fi + + + +{ $as_echo "$as_me:$LINENO: checking whether $CC supports -Wextra" >&5 +$as_echo_n "checking whether $CC supports -Wextra... " >&6; } +cache=`echo Wextra | sed 'y%.=/+-%___p_%'` +if { as_var=cv_prog_cc_flag_$cache; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + +echo 'void f(){}' >conftest.c +if test -z "`$CC -Wextra -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:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +: + CFLAGS="$CFLAGS -Wextra" +else +{ $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +: + +fi + + ;; + no|*) + +cat >>confdefs.h <<\_ACEOF +#define NDEBUG /**/ +_ACEOF + + ;; +esac + +# Check whether --enable-tsig was given. +if test "${enable_tsig+set}" = set; then + enableval=$enable_tsig; +fi + +case "$enable_tsig" in + no) + ;; + yes|*) + + +# Check whether --with-ssl was given. +if test "${with_ssl+set}" = set; then + withval=$with_ssl; + +else + + withval="yes" + +fi + + if test x_$withval != x_no; then + { $as_echo "$as_me:$LINENO: checking for SSL" >&5 +$as_echo_n "checking for SSL... " >&6; } + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/sfw /usr/local /usr" + fi + for dir in $withval; do + ssldir="$dir" + if test -f "$dir/include/openssl/ssl.h"; then + found_ssl="yes"; + +cat >>confdefs.h <<_ACEOF +#define HAVE_SSL /**/ +_ACEOF + + CPPFLAGS="$CPPFLAGS -I$ssldir/include"; + break; + fi + done + if test x_$found_ssl != x_yes; then + { { $as_echo "$as_me:$LINENO: error: Cannot find the SSL libraries in $withval" >&5 +$as_echo "$as_me: error: Cannot find the SSL libraries in $withval" >&2;} + { (exit 1); exit 1; }; } + else + { $as_echo "$as_me:$LINENO: result: found in $ssldir" >&5 +$as_echo "found in $ssldir" >&6; } + HAVE_SSL=yes + LDFLAGS="$LDFLAGS -L$ssldir/lib"; + if test x_$ssldir = x_/usr/sfw; then + LDFLAGS="$LDFLAGS -R$ssldir/lib"; + fi + +{ $as_echo "$as_me:$LINENO: checking for HMAC_CTX_init in -lcrypto" >&5 +$as_echo_n "checking for HMAC_CTX_init in -lcrypto... " >&6; } +if test "${ac_cv_lib_crypto_HMAC_CTX_init+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrypto $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 HMAC_CTX_init (); +int +main () +{ +return HMAC_CTX_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_crypto_HMAC_CTX_init=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_crypto_HMAC_CTX_init=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_HMAC_CTX_init" >&5 +$as_echo "$ac_cv_lib_crypto_HMAC_CTX_init" >&6; } +if test "x$ac_cv_lib_crypto_HMAC_CTX_init" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCRYPTO 1 +_ACEOF + + LIBS="-lcrypto $LIBS" + +else + + { { $as_echo "$as_me:$LINENO: error: OpenSSL found in $ssldir, but version 0.9.7 or higher is required" >&5 +$as_echo "$as_me: error: OpenSSL found in $ssldir, but version 0.9.7 or higher is required" >&2;} + { (exit 1); exit 1; }; } + +fi + + + +for ac_func in EVP_sha1 EVP_sha256 +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* 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 $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + fi + + fi + + if test x_$HAVE_SSL != x_yes; then + { { $as_echo "$as_me:$LINENO: error: SSL is required to enable TSIG support. Use --with-ssl to specify the location of the SSL libraries or --disable-tsig to disable TSIG support." >&5 +$as_echo "$as_me: error: SSL is required to enable TSIG support. Use --with-ssl to specify the location of the SSL libraries or --disable-tsig to disable TSIG support." >&2;} + { (exit 1); exit 1; }; } + fi + +cat >>confdefs.h <<\_ACEOF +#define TSIG /**/ +_ACEOF + + ;; +esac + +# Check whether --enable-nsec3 was given. +if test "${enable_nsec3+set}" = set; then + enableval=$enable_nsec3; +fi + +case "$enable_nsec3" in + no) + ;; + yes|*) + +cat >>confdefs.h <<_ACEOF +#define NSEC3 /**/ +_ACEOF + + ;; +esac + +# Check whether --enable-nsid was given. +if test "${enable_nsid+set}" = set; then + enableval=$enable_nsid; +fi + +case "$enable_nsid" in + yes) + +cat >>confdefs.h <<\_ACEOF +#define NSID /**/ +_ACEOF + + ;; + no|*) + ;; +esac + + + + + + + + + + + + + + + + +if test $ac_cv_func_getaddrinfo = no; then +case " $LIBOBJS " in + *" fake-rfc2553.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS fake-rfc2553.$ac_objext" + ;; +esac + +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 + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # 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). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... 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'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + 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 + +# 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'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# 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.4, which was +generated by GNU Autoconf 2.63. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTION]... [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to <bug-autoconf@gnu.org>." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +NSD config.status 3.2.4 +configured by $0, generated by GNU Autoconf 2.63, + with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_FILES="$CONFIG_FILES '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { $as_echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { $as_echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +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 + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + $as_echo "$as_me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr='
' +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\).*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\).*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5 +$as_echo "$as_me: error: could not setup config files machinery" >&2;} + { (exit 1); exit 1; }; } +_ACEOF + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5 +$as_echo "$as_me: error: could not setup config headers machinery" >&2;} + { (exit 1); exit 1; }; } +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 +$as_echo "$as_me: error: invalid tag $ac_tag" >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + ac_file_inputs="$ac_file_inputs '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:$LINENO: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$tmp/config.h" "$ac_file" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5 +$as_echo "$as_me: error: could not create -" >&2;} + { (exit 1); exit 1; }; } + fi + ;; + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/usr.sbin/nsd/configure.ac b/usr.sbin/nsd/configure.ac new file mode 100644 index 00000000000..ccabe1462c9 --- /dev/null +++ b/usr.sbin/nsd/configure.ac @@ -0,0 +1,792 @@ +dnl +dnl Some global settings +dnl + +sinclude(acx_nlnetlabs.m4) + +AC_INIT(NSD,3.2.4,nsd-bugs@nlnetlabs.nl) +AC_CONFIG_HEADER([config.h]) + +CFLAGS="$CFLAGS" +AC_AIX + +dnl +dnl By default set $sysconfdir to /etc and $localstatedir to /var +dnl +case "$prefix" in + NONE) + case "$sysconfdir" in + '${prefix}/etc') + sysconfdir=/etc + ;; + esac + case "$localstatedir" in + '${prefix}/var') + localstatedir=/var + ;; + esac + ;; +esac + +# +# Determine configuration directory +# +configdir=$sysconfdir/nsd +AC_ARG_WITH([configdir], + AC_HELP_STRING([--with-configdir=dir], [NSD configuration directory]), + [configdir=$withval]) +AC_SUBST(configdir) + +# +# Determine configuration file +nsd_conf_file=${configdir}/nsd.conf +AC_ARG_WITH([nsd_conf_file], + AC_HELP_STRING([--with-nsd_conf_file=path], [Pathname to the NSD configuration file]), + [nsd_conf_file=$withval]) +AC_SUBST(nsd_conf_file) +# the eval is to evaluate shell expansion twice, once +# for $nsd_conf_file and once for the ${prefix} within it. +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) + +# +# Database directory +# +dbdir=${localstatedir}/db/nsd + +# +# Determine the pidfile location. Check if /var/run exists, if so set pidfile +# to /var/run/nsd.pid by default +# +if test -d ${localstatedir}/run; then + pidfile=${localstatedir}/run/nsd.pid +else + pidfile=${dbdir}/nsd.pid +fi +AC_ARG_WITH([pidfile], + AC_HELP_STRING([--with-pidfile=path], [Pathname to the NSD pidfile]), + [pidfile=$withval]) +AC_SUBST(pidfile) +AC_DEFINE_UNQUOTED(PIDFILE, ["`eval echo $pidfile`"], [Pathname to the NSD pidfile]) + +# +# Determine location of nsd.db +# +dbfile=${dbdir}/nsd.db +AC_ARG_WITH([dbfile], + AC_HELP_STRING([--with-dbfile=path], [Pathname to the NSD database]), + [dbfile=$withval]) +AC_SUBST(dbfile) +AC_DEFINE_UNQUOTED(DBFILE, ["`eval echo $dbfile`"], [Pathname to the NSD database]) + +dbdir=`dirname $dbfile` +AC_SUBST(dbdir) + +piddir=`dirname $pidfile` +AC_SUBST(piddir) + +# +# Determine the default directory for the zone files +# +zonesdir=$configdir +AC_ARG_WITH([zonesdir], + AC_HELP_STRING([--with-zonesdir=dir], [NSD default location for zone files]), + [zonesdir=$withval]) +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], + [Pathname to the NSD xfrd zone timer state file]), [xfrdfile=$withval]) +AC_DEFINE_UNQUOTED(XFRDFILE, ["`eval echo $xfrdfile`"], [Pathname to the NSD xfrd zone timer state file.]) +AC_SUBST(xfrdfile) + +# +# Determine the user name to drop privileges to +# +user=nsd +AC_ARG_WITH([user], + AC_HELP_STRING([--with-user=username], [User name or ID to answer the queries with]), + [user=$withval]) +AC_SUBST(user) +AC_DEFINE_UNQUOTED(USER, ["$user"], [the user name to drop privileges to]) + +# Checks for programs. +AC_PROG_AWK +AC_PROG_CC +AC_PROG_LN_S +AC_PROG_INSTALL +AC_PROG_LEX +AC_PROG_YACC + +AC_DEFUN([AC_CHECK_FORMAT_ATTRIBUTE], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "format" attribute) +AC_CACHE_VAL(ac_cv_c_format_attribute, +[ac_cv_c_format_attribute=no +AC_TRY_COMPILE( +[#include <stdio.h> +void f (char *format, ...) __attribute__ ((format (printf, 1, 2))); +void (*pf) (char *format, ...) __attribute__ ((format (printf, 1, 2))); +], [ + f ("%s", "str"); +], +[ac_cv_c_format_attribute="yes"], +[ac_cv_c_format_attribute="no"]) +]) + +AC_MSG_RESULT($ac_cv_c_format_attribute) +if test $ac_cv_c_format_attribute = yes; then + AC_DEFINE(HAVE_ATTR_FORMAT, 1, [Whether the C compiler accepts the "format" attribute]) +fi +])dnl + +AC_DEFUN([AC_CHECK_UNUSED_ATTRIBUTE], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "unused" attribute) +AC_CACHE_VAL(ac_cv_c_unused_attribute, +[ac_cv_c_unused_attribute=no +AC_TRY_COMPILE( +[#include <stdio.h> +void f (char *u __attribute__((unused))); +], [ + f ("x"); +], +[ac_cv_c_unused_attribute="yes"], +[ac_cv_c_unused_attribute="no"]) +]) + +AC_MSG_RESULT($ac_cv_c_unused_attribute) +if test $ac_cv_c_unused_attribute = yes; then + AC_DEFINE(HAVE_ATTR_UNUSED, 1, [Whether the C compiler accepts the "unused" attribute]) +fi +])dnl + +AC_DEFUN([CHECK_COMPILER_FLAG], +[ +AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether $CC supports -$1) +cache=`echo $1 | sed 'y%.=/+-%___p_%'` +AC_CACHE_VAL(cv_prog_cc_flag_$cache, +[ +echo 'void f(){}' >conftest.c +if test -z "`$CC -$1 -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* +]) +if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then +AC_MSG_RESULT(yes) +: +$2 +else +AC_MSG_RESULT(no) +: +$3 +fi +]) + +AC_DEFUN([AC_CHECK_CTIME_R], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether ctime_r works with two arguments) +AC_CACHE_VAL(ac_cv_c_ctime_c, +[ac_cv_c_ctime_c=no +AC_TRY_COMPILE( +[#include <time.h> +void testing (void) { time_t clock; char current_time[40]; ctime_r(&clock, current_time); }], +[ + testing(); +], +[ac_cv_c_ctime_c="yes"], +[ac_cv_c_ctime_c="no"]) +]) + +AC_MSG_RESULT($ac_cv_c_ctime_c) +if test $ac_cv_c_ctime_c = no; then + CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS" +fi +])dnl + +# Checks for typedefs, structures, and compiler characteristics. +CHECK_COMPILER_FLAG(O2, [], [ CFLAGS="-g" ]) +AC_C_CONST +AC_C_INLINE +AC_TYPE_UID_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_OFF_T + +AC_CHECK_FORMAT_ATTRIBUTE +AC_CHECK_UNUSED_ATTRIBUTE +AC_CHECK_CTIME_R + +# Checks for libraries. + +# Check for SSL, original taken from +# http://www.gnu.org/software/ac-archive/htmldoc/check_ssl.html and +# modified for NSD. +AC_DEFUN([CHECK_SSL], [ + AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname], + [enable SSL (will check /usr/local/ssl + /usr/lib/ssl /usr/ssl /usr/pkg /usr/sfw /usr/local /usr)]),[ + ],[ + withval="yes" + ]) + if test x_$withval != x_no; then + AC_MSG_CHECKING(for SSL) + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/sfw /usr/local /usr" + fi + for dir in $withval; do + ssldir="$dir" + if test -f "$dir/include/openssl/ssl.h"; then + found_ssl="yes"; + AC_DEFINE_UNQUOTED([HAVE_SSL], [], [Define if you have the SSL libraries installed.]) + CPPFLAGS="$CPPFLAGS -I$ssldir/include"; + break; + fi + done + if test x_$found_ssl != x_yes; then + AC_MSG_ERROR(Cannot find the SSL libraries in $withval) + else + AC_MSG_RESULT(found in $ssldir) + HAVE_SSL=yes + LDFLAGS="$LDFLAGS -L$ssldir/lib"; + if test x_$ssldir = x_/usr/sfw; then + LDFLAGS="$LDFLAGS -R$ssldir/lib"; + fi + AC_CHECK_LIB(crypto, HMAC_CTX_init,, [ + AC_MSG_ERROR([OpenSSL found in $ssldir, but version 0.9.7 or higher is required]) + ]) + AC_CHECK_FUNCS([EVP_sha1 EVP_sha256]) + fi + AC_SUBST(HAVE_SSL) + fi +])dnl + +# Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS([time.h arpa/inet.h signal.h strings.h fcntl.h limits.h netinet/in.h stddef.h sys/param.h sys/socket.h syslog.h unistd.h sys/select.h stdarg.h stdint.h netdb.h sys/bitypes.h tcpd.h grp.h]) + +AC_DEFUN([CHECK_VALIST_DEF], +[ +AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(for double definition of struct va_list) +AC_CACHE_VAL(ac_cv_c_va_list_def, +[ +cat >conftest.c <<EOF +#include <stdio.h> +#include <stdarg.h> +int foo(void); +EOF +if test -z "`$CC -Werror -D_XOPEN_SOURCE=600 -c conftest.c 2>&1`"; then +eval "ac_cv_c_va_list_def=no" +else +eval "ac_cv_c_va_list_def=yes" +fi +rm -f conftest* +]) +if test $ac_cv_c_va_list_def = yes; then +AC_MSG_RESULT(yes) +: +AC_DEFINE_UNQUOTED([HAVE_VA_LIST_DOUBLE_DEF], [], [Define this if you have double va_list definitions.]) +else +AC_MSG_RESULT(no) +: + +fi +]) + +CHECK_VALIST_DEF + +AC_DEFUN([AC_CHECK_STRPTIME], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether strptime needs defines) +AC_CACHE_VAL(ac_cv_c_strptime_needs_defs, +[ +cat >conftest.c <<EOF +#include <time.h> +void testing (void) { struct tm t; char *timestr; strptime(timestr, "%Y%m", &t); } +EOF +if test -z "`$CC -Wall -Werror -c conftest.c 2>&1`"; then +eval "ac_cv_c_strptime_needs_defs=no" +else +eval "ac_cv_c_strptime_needs_defs=yes" +fi +rm -f conftest* +]) + +AC_MSG_RESULT($ac_cv_c_strptime_needs_defs) +if test $ac_cv_c_strptime_needs_defs = yes; then +AC_DEFINE_UNQUOTED([STRPTIME_NEEDS_DEFINES], 1, [strptime is available from time.h with some defines.]) +fi +])dnl + +AC_CHECK_STRPTIME + +# check wether strptime also works +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([ +#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"]) +else +eval "ac_cv_c_strptime_works=maybe" +fi +AC_MSG_RESULT($ac_cv_c_strptime_works) +if test $ac_cv_c_strptime_works = no; then +AC_LIBOBJ(strptime) +else +AC_DEFINE_UNQUOTED([STRPTIME_WORKS], 1, [use default strptime.]) +fi +])dnl + +AC_CHECK_STRPTIME_WORKS + +AC_SEARCH_LIBS(inet_pton, [nsl]) +AC_SEARCH_LIBS(socket, [socket]) + +dnl LIBGTOP_CHECK_TYPE +dnl Stolen from Gnome's anjuta +dnl Improved version of AC_CHECK_TYPE which takes into account +dnl that we need to #include some other header files on some +dnl systems to get some types. + +dnl AC_LIBGTOP_CHECK_TYPE(TYPE, DEFAULT) +AC_DEFUN([AC_LIBGTOP_CHECK_TYPE], +[AC_REQUIRE([AC_HEADER_STDC])dnl +AC_MSG_CHECKING(for $1) +AC_CACHE_VAL(ac_cv_type_$1, +[AC_EGREP_CPP(dnl +changequote(<<,>>)dnl +<<(^|[^a-zA-Z_0-9])$1[^a-zA-Z_0-9]>>dnl +changequote([,]), [ +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif +], ac_cv_type_$1=yes, ac_cv_type_$1=no)])dnl +AC_MSG_RESULT($ac_cv_type_$1) +if test $ac_cv_type_$1 = no; then + AC_DEFINE($1, $2, Define "$1" to "$2" if "$1" is missing) +fi +]) + +AC_LIBGTOP_CHECK_TYPE(int8_t, char) +AC_LIBGTOP_CHECK_TYPE(int16_t, short) +AC_LIBGTOP_CHECK_TYPE(int32_t, int) +AC_LIBGTOP_CHECK_TYPE(int64_t, long long) +AC_LIBGTOP_CHECK_TYPE(uint8_t, unsigned char) +AC_LIBGTOP_CHECK_TYPE(uint16_t, unsigned short) +AC_LIBGTOP_CHECK_TYPE(uint32_t, unsigned int) +AC_LIBGTOP_CHECK_TYPE(uint64_t, unsigned long long) +AC_LIBGTOP_CHECK_TYPE(socklen_t, int) +AC_LIBGTOP_CHECK_TYPE(sig_atomic_t, int) +AC_LIBGTOP_CHECK_TYPE(ssize_t, int) + +AC_CHECK_TYPE(in_addr_t, [], [AC_DEFINE([in_addr_t], [uint32_t], [in_addr_t])], [ +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif]) + +# Checks for library functions. +AC_FUNC_CHOWN +AC_FUNC_FORK +AC_FUNC_MALLOC +AC_TYPE_SIGNAL +AC_FUNC_FSEEKO +AC_SYS_LARGEFILE +AC_CHECK_FUNCS([alarm chroot dup2 endpwent gethostname memset memcpy socket strcasecmp strchr strdup strerror strncasecmp strtol getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask strptime setusercontext initgroups setresuid setreuid setresgid setregid getpwnam]) + +# check if setreuid en setregid fail, on MacOSX10.4(darwin8). +if echo $build_os | grep darwin8 > /dev/null; then + AC_DEFINE(DARWIN_BROKEN_SETREUID, 1, [Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work]) +fi + +# +# Checking for missing functions we can replace +# +AC_REPLACE_FUNCS(basename) +AC_REPLACE_FUNCS(inet_aton) +AC_REPLACE_FUNCS(inet_pton) +AC_REPLACE_FUNCS(inet_ntop) +AC_REPLACE_FUNCS(snprintf) +AC_REPLACE_FUNCS(strlcpy) +AC_REPLACE_FUNCS(strptime) +AC_REPLACE_FUNCS(b64_pton) +AC_REPLACE_FUNCS(b64_ntop) +AC_REPLACE_FUNCS(pselect) +AC_REPLACE_FUNCS(memmove) + +AC_MSG_CHECKING(for pselect prototype in sys/select.h) +AC_EGREP_HEADER([[^a-zA-Z_]*pselect[^a-zA-Z_]], sys/select.h, AC_DEFINE(HAVE_PSELECT_PROTO, 1, + [if sys/select.h provides pselect prototype]) AC_MSG_RESULT(yes), AC_MSG_RESULT(no)) + +AC_MSG_CHECKING(for ctime_r prototype in time.h) +AC_EGREP_HEADER([[^a-zA-Z_]*ctime_r[^a-zA-Z_]], time.h, AC_DEFINE(HAVE_CTIME_R_PROTO, 1, + [if time.h provides ctime_r prototype]) AC_MSG_RESULT(yes), AC_MSG_RESULT(no)) + +AC_CHECK_TYPE([struct timespec], AC_DEFINE(HAVE_STRUCT_TIMESPEC, 1, [If time.h has a struct timespec (for pselect).]), [], [ +AC_INCLUDES_DEFAULT +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +]) + +dnl +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_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.]) + +dnl +dnl Determine the syslog facility to use +dnl +facility=LOG_DAEMON +AC_ARG_WITH([facility], + AC_HELP_STRING([--with-facility=name], [Syslog default facility (LOG_DAEMON)]), + [facility=$withval]) +AC_DEFINE_UNQUOTED([FACILITY], $facility, [Define to the default facility for syslog.]) + +dnl +dnl Determine the maximum number of interfaces that are allowed +dnl +max_interfaces=8 +AC_ARG_WITH([max_interfaces], + AC_HELP_STRING([--with-max_interfaces=number], [Limit on the number of ip-addresses that may be specified]), + [max_interfaces=$withval]) +AC_DEFINE_UNQUOTED([MAX_INTERFACES], $max_interfaces, [Define to the maximum interfaces to serve.]) + +dnl +dnl Determine the default tcp timeout +dnl +tcp_timeout=120 +AC_ARG_WITH([tcp_timeout], + AC_HELP_STRING([--with-tcp-timeout=number], [Limit the default tcp timeout]), + [tcp_timeout=$withval]) +AC_DEFINE_UNQUOTED([TCP_TIMEOUT], $tcp_timeout, [Define to the default tcp timeout.]) + +dnl +dnl Features +dnl +AC_ARG_ENABLE(root-server, AC_HELP_STRING([--enable-root-server], [Configure NSD as a root server])) +case "$enable_root_server" in + yes) + AC_DEFINE_UNQUOTED([ROOT_SERVER], [], [Define this to configure as a root server.]) + ;; + no|*) + ;; +esac + +AC_ARG_ENABLE(ipv6, AC_HELP_STRING([--disable-ipv6], [Disables IPv6 support])) +case "$enable_ipv6" in + no) + ;; + yes|*) + AC_DEFINE_UNQUOTED([INET6], [], [Define this to enable IPv6 support.]) + ;; +esac + +AC_ARG_ENABLE(dnssec, AC_HELP_STRING([--disable-dnssec], [Disable DNSSEC support.])) +case "$enable_dnssec" in + no) + ;; + yes|*) + AC_DEFINE_UNQUOTED([DNSSEC], [], [Define this to enable DNSSEC (RFCs 4033, 4034, and 4035) support.]) + ;; +esac + +AC_ARG_ENABLE(bind8-stats, AC_HELP_STRING([--enable-bind8-stats], [Enables BIND8 like NSTATS & XSTATS])) + +case "$enable_bind8_stats" in + yes|'') + AC_DEFINE_UNQUOTED([BIND8_STATS], [], [Define this to enable BIND8 like NSTATS & XSTATS.]) + ;; + no|*) + ;; +esac + +AC_ARG_ENABLE(checking, AC_HELP_STRING([--enable-checking], [Enable internal runtime checks])) +case "$enable_checking" in + yes) + CHECK_COMPILER_FLAG(W, [ CFLAGS="$CFLAGS -W" ]) + CHECK_COMPILER_FLAG(Wall, [ CFLAGS="$CFLAGS -Wall" ]) + CHECK_COMPILER_FLAG(Wextra, [ CFLAGS="$CFLAGS -Wextra" ]) + ;; + no|*) + AC_DEFINE([NDEBUG], [], [Undefine this to enable internal runtime checks.]) + ;; +esac + +AC_ARG_ENABLE(tsig, AC_HELP_STRING([--disable-tsig], [Disable TSIG support])) +case "$enable_tsig" in + no) + ;; + yes|*) + CHECK_SSL + if test x_$HAVE_SSL != x_yes; then + AC_MSG_ERROR(SSL is required to enable TSIG support. Use --with-ssl to specify the location of the SSL libraries or --disable-tsig to disable TSIG support.) + fi + AC_DEFINE([TSIG], [], [Define this to enable TSIG support.]) + ;; +esac + +AC_ARG_ENABLE(nsec3, AC_HELP_STRING([--disable-nsec3], [Disable NSEC3 support])) +case "$enable_nsec3" in + no) + ;; + yes|*) + AC_DEFINE_UNQUOTED([NSEC3], [], [Define this to enable NSEC3 support.]) + ;; +esac + +AC_ARG_ENABLE(nsid, AC_HELP_STRING([--enable-nsid], [Enable NSID support])) +case "$enable_nsid" in + yes) + AC_DEFINE([NSID], [], [Define this to enable NSID support.]) + ;; + no|*) + ;; +esac + +AH_BOTTOM([ +/* define before includes as it specifies what standard to use. */ +#if (defined(HAVE_PSELECT) && !defined (HAVE_PSELECT_PROTO)) \ + || !defined (HAVE_CTIME_R_PROTO) \ + || defined (STRPTIME_NEEDS_DEFINES) +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 600 +# endif +# ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112 +# endif +# ifndef _BSD_SOURCE +# define _BSD_SOURCE 1 +# endif +# ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +# endif +# ifndef _STDC_C99 +# define _STDC_C99 1 +# endif +# ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +# endif +#endif +]) + +AH_BOTTOM([ +#ifdef HAVE_VA_LIST_DOUBLE_DEF +/* workaround double va_list definition on some platforms */ +# ifndef _VA_LIST_DEFINED +# define _VA_LIST_DEFINED +# endif +#endif +]) + +AH_BOTTOM([ +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif + +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.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 + +/* For Tru64 */ +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif +]) + +AH_BOTTOM([ +#ifdef HAVE_ATTR_FORMAT +#define ATTR_FORMAT(archetype, string_index, first_to_check) \ + __attribute__ ((format (archetype, string_index, first_to_check))) +#else /* !HAVE_ATTR_FORMAT */ +#define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */ +#endif /* !HAVE_ATTR_FORMAT */ +#if defined(__cplusplus) +#define ATTR_UNUSED(x) +#elif defined(HAVE_ATTR_UNUSED) +#define ATTR_UNUSED(x) x __attribute__((unused)) +#else /* !HAVE_ATTR_UNUSED */ +#define ATTR_UNUSED(x) x +#endif /* !HAVE_ATTR_UNUSED */ +]) + +AH_BOTTOM([ +#ifndef IPV6_MIN_MTU +#define IPV6_MIN_MTU 1280 +#endif /* IPV6_MIN_MTU */ + +#ifndef AF_INET6 +#define AF_INET6 28 +#endif /* AF_INET6 */ +]) + +if test $ac_cv_func_getaddrinfo = no; then +AC_LIBOBJ([fake-rfc2553]) +fi + +AH_BOTTOM([ +/* maximum nesting of included files */ +#define MAXINCLUDES 10 +]) + +AH_BOTTOM([ +#ifndef B64_PTON +int b64_ntop(uint8_t const *src, size_t srclength, + char *target, size_t targsize); +#endif /* !B64_PTON */ +#ifndef B64_NTOP +int b64_pton(char const *src, uint8_t *target, size_t targsize); +#endif /* !B64_NTOP */ +#ifndef HAVE_FSEEKO +#define fseeko fseek +#define ftello ftell +#endif /* HAVE_FSEEKO */ +#ifndef HAVE_SNPRINTF +#include <stdarg.h> +int snprintf (char *str, size_t count, const char *fmt, ...); +int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); +#endif /* HAVE_SNPRINTF */ +#ifndef HAVE_INET_PTON +int inet_pton(int af, const char* src, void* dst); +#endif /* HAVE_INET_PTON */ +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, size_t size); +#endif +#ifndef HAVE_INET_ATON +int inet_aton(const char *cp, struct in_addr *addr); +#endif +#ifndef HAVE_MEMMOVE +void *memmove(void *dest, const void *src, size_t n); +#endif +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t siz); +#endif +#ifndef HAVE_GETADDRINFO +#include "compat/fake-rfc2553.h" +#endif +#ifndef HAVE_STRPTIME +#define HAVE_STRPTIME 1 +char *strptime(const char *s, const char *format, struct tm *tm); +#endif +#ifndef STRPTIME_WORKS +#define STRPTIME_WORKS 1 +#define strptime(a,b,c) nsd_strptime((a),(b),(c)) +#endif + +/* provide timespec def if not available */ +#ifndef CONFIG_DEFINES +#define CONFIG_DEFINES +#ifndef HAVE_STRUCT_TIMESPEC +#ifndef __timespec_defined +#define __timespec_defined 1 + struct timespec { + long tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ + }; +#endif /* !__timespec_defined */ +#endif /* !HAVE_STRUCT_TIMESPEC */ +#endif /* !CONFIG_DEFINES */ +]) + +# 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 + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/usr.sbin/nsd/configyyrename.h b/usr.sbin/nsd/configyyrename.h new file mode 100644 index 00000000000..f1e125a1d1c --- /dev/null +++ b/usr.sbin/nsd/configyyrename.h @@ -0,0 +1,89 @@ +/* + * configyyrename.h -- renames for config file yy values to avoid conflicts. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef CONFIGYYRENAME_H +#define CONFIGYYRENAME_H +#include <config.h> + +/* defines to change symbols so that no yacc/lex symbols clash */ +#define yymaxdepth c_maxdepth +#define yyparse c_parse +#define yylex c_lex +#define yyerror c_error +#define yylval c_lval +#define yychar c_char +#define yydebug c_debug +#define yypact c_pact +#define yyr1 c_r1 +#define yyr2 c_r2 +#define yydef c_def +#define yychk c_chk +#define yypgo c_pgo +#define yyact c_act +#define yyexca c_exca +#define yyerrflag c_errflag +#define yynerrs c_nerrs +#define yyps c_ps +#define yypv c_pv +#define yys c_s +#define yy_yys c_yys +#define yystate c_state +#define yytmp c_tmp +#define yyv c_v +#define yy_yyv c_yyv +#define yyval c_val +#define yylloc c_lloc +#define yyreds c_reds +#define yytoks c_toks +#define yylhs c_yylhs +#define yylen c_yylen +#define yydefred c_yydefred +#define yydgoto c_yydgoto +#define yysindex c_yysindex +#define yyrindex c_yyrindex +#define yygindex c_yygindex +#define yytable c_yytable +#define yycheck c_yycheck +#define yyname c_yyname +#define yyrule c_yyrule +#define yyin c_in +#define yyout c_out +#define yywrap c_wrap +#define yy_load_buffer_state c_load_buffer_state +#define yy_switch_to_buffer c_switch_to_buffer +#define yy_flush_buffer c_flush_buffer +#define yy_init_buffer c_init_buffer +#define yy_scan_buffer c_scan_buffer +#define yy_scan_bytes c_scan_bytes +#define yy_scan_string c_scan_string +#define yy_create_buffer c_create_buffer +#define yyrestart c_restart +#define yy_delete_buffer c_delete_buffer +#define yypop_buffer_state c_pop_buffer_state +#define yypush_buffer_state c_push_buffer_state +#define yyunput c_unput +#define yyset_in c_set_in +#define yyget_in c_get_in +#define yyset_out c_set_out +#define yyget_out c_get_out +#define yyget_lineno c_get_lineno +#define yyset_lineno c_set_lineno +#define yyset_debug c_set_debug +#define yyget_debug c_get_debug +#define yy_flex_debug c_flex_debug +#define yylex_destroy c_lex_destroy +#define yyfree c_free +#define yyrealloc c_realloc +#define yyalloc c_alloc +#define yymalloc c_malloc +#define yyget_leng c_get_leng +#define yylineno c_lineno +#define yyget_text c_get_text + +#endif /* CONFIGYYRENAME_H */ diff --git a/usr.sbin/nsd/dbaccess.c b/usr.sbin/nsd/dbaccess.c new file mode 100644 index 00000000000..83cd5e2eb62 --- /dev/null +++ b/usr.sbin/nsd/dbaccess.c @@ -0,0 +1,460 @@ +/* + * dbaccess.c -- access methods for nsd(8) database + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> /* DEBUG */ + +#include "dns.h" +#include "namedb.h" +#include "util.h" +#include "options.h" + +int +namedb_lookup(struct namedb *db, + const dname_type *dname, + domain_type **closest_match, + domain_type **closest_encloser) +{ + return domain_table_search( + db->domains, dname, closest_match, closest_encloser); +} + +static int +read_magic(namedb_type *db) +{ + char buf[NAMEDB_MAGIC_SIZE]; + + if (fread(buf, sizeof(char), sizeof(buf), db->fd) != sizeof(buf)) + return 0; + + return memcmp(buf, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE) == 0; +} + +static const dname_type * +read_dname(FILE *fd, region_type *region) +{ + uint8_t size; + uint8_t temp[MAXDOMAINLEN]; + + if (fread(&size, sizeof(uint8_t), 1, fd) != 1) + return NULL; + if (fread(temp, sizeof(uint8_t), size, fd) != size) + return NULL; + + return dname_make(region, temp, 1); +} + +static int +read_size(namedb_type *db, uint32_t *result) +{ + if (fread(result, sizeof(*result), 1, db->fd) == 1) { + *result = ntohl(*result); + return 1; + } else { + return 0; + } +} + +static domain_type * +read_domain(namedb_type *db, uint32_t domain_count, domain_type **domains) +{ + uint32_t domain_number; + + if (!read_size(db, &domain_number)) + return NULL; + + if (domain_number == 0 || domain_number > domain_count) + return NULL; + + return domains[domain_number - 1]; +} + +static zone_type * +read_zone(namedb_type *db, uint32_t zone_count, zone_type **zones) +{ + uint32_t zone_number; + + if (!read_size(db, &zone_number)) + return NULL; + + if (zone_number == 0 || zone_number > zone_count) + return NULL; + + return zones[zone_number - 1]; +} + +static int +read_rdata_atom(namedb_type *db, uint16_t type, int index, uint32_t domain_count, domain_type **domains, rdata_atom_type *result) +{ + uint8_t data[65536]; + + if (rdata_atom_is_domain(type, index)) { + result->domain = read_domain(db, domain_count, domains); + if (!result->domain) + return 0; + } else { + uint16_t size; + + if (fread(&size, sizeof(size), 1, db->fd) != 1) + return 0; + size = ntohs(size); + if (fread(data, sizeof(uint8_t), size, db->fd) != size) + return 0; + + result->data = (uint16_t *) region_alloc( + db->region, sizeof(uint16_t) + size); + memcpy(result->data, &size, sizeof(uint16_t)); + memcpy((uint8_t *) result->data + sizeof(uint16_t), data, size); + } + + return 1; +} + +static rrset_type * +read_rrset(namedb_type *db, + uint32_t domain_count, domain_type **domains, + uint32_t zone_count, zone_type **zones) +{ + rrset_type *rrset; + int i, j; + domain_type *owner; + uint16_t type; + uint16_t klass; + uint32_t soa_minimum; + + owner = read_domain(db, domain_count, domains); + if (!owner) + return NULL; + + rrset = (rrset_type *) region_alloc(db->region, sizeof(rrset_type)); + + rrset->zone = read_zone(db, zone_count, zones); + if (!rrset->zone) + return NULL; + + if (fread(&type, sizeof(type), 1, db->fd) != 1) + return NULL; + type = ntohs(type); + + if (fread(&klass, sizeof(klass), 1, db->fd) != 1) + return NULL; + klass = ntohs(klass); + + if (fread(&rrset->rr_count, sizeof(rrset->rr_count), 1, db->fd) != 1) + return NULL; + rrset->rr_count = ntohs(rrset->rr_count); + rrset->rrs = (rr_type *) region_alloc( + db->region, rrset->rr_count * sizeof(rr_type)); + + assert(rrset->rr_count > 0); + + for (i = 0; i < rrset->rr_count; ++i) { + rr_type *rr = &rrset->rrs[i]; + + rr->owner = owner; + rr->type = type; + rr->klass = klass; + + if (fread(&rr->rdata_count, sizeof(rr->rdata_count), 1, db->fd) != 1) + return NULL; + rr->rdata_count = ntohs(rr->rdata_count); + rr->rdatas = (rdata_atom_type *) region_alloc( + db->region, rr->rdata_count * sizeof(rdata_atom_type)); + + if (fread(&rr->ttl, sizeof(rr->ttl), 1, db->fd) != 1) + return NULL; + rr->ttl = ntohl(rr->ttl); + + for (j = 0; j < rr->rdata_count; ++j) { + if (!read_rdata_atom(db, rr->type, j, domain_count, domains, &rr->rdatas[j])) + return NULL; + } + } + + domain_add_rrset(owner, rrset); + + if (rrset_rrtype(rrset) == TYPE_SOA) { + assert(owner == rrset->zone->apex); + rrset->zone->soa_rrset = rrset; + + /* BUG #103 add another soa with a tweaked ttl */ + rrset->zone->soa_nx_rrset = region_alloc(db->region, sizeof(rrset_type)); + rrset->zone->soa_nx_rrset->rrs = + region_alloc(db->region, rrset->rr_count * sizeof(rr_type)); + + memcpy(rrset->zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type)); + rrset->zone->soa_nx_rrset->rr_count = 1; + rrset->zone->soa_nx_rrset->next = 0; + + /* also add a link to the zone */ + rrset->zone->soa_nx_rrset->zone = rrset->zone; + + /* check the ttl and MINIMUM value and set accordinly */ + memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]), + rdata_atom_size(rrset->rrs->rdatas[6])); + if (rrset->rrs->ttl > ntohl(soa_minimum)) { + rrset->zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum); + } + + } else if (owner == rrset->zone->apex + && rrset_rrtype(rrset) == TYPE_NS) + { + rrset->zone->ns_rrset = rrset; + } + +#ifdef DNSSEC + if (rrset_rrtype(rrset) == TYPE_RRSIG && owner == rrset->zone->apex) { + for (i = 0; i < rrset->rr_count; ++i) { + if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_SOA) { + rrset->zone->is_secure = 1; + break; + } + } + } +#endif + return rrset; +} + +struct namedb * +namedb_open (const char *filename, nsd_options_t* opt, size_t num_children) +{ + namedb_type *db; + + /* + * Region used to store the loaded database. The region is + * freed in namedb_close. + */ + region_type *db_region; + + /* + * Temporary region used while loading domain names from the + * database. The region is freed after each time a dname is + * read from the database. + */ + region_type *dname_region; + + /* + * Temporary region used to store array of domains and zones + * while loading the database. The region is freed before + * returning. + */ + region_type *temp_region; + + uint32_t dname_count; + domain_type **domains; /* Indexed by domain number. */ + + uint32_t zone_count; + zone_type **zones; /* Indexed by zone number. */ + + uint32_t i; + uint32_t rrset_count = 0; + uint32_t rr_count = 0; + + rrset_type *rrset; + + DEBUG(DEBUG_DBACCESS, 2, + (LOG_INFO, "sizeof(namedb_type) = %lu\n", (unsigned long) sizeof(namedb_type))); + DEBUG(DEBUG_DBACCESS, 2, + (LOG_INFO, "sizeof(zone_type) = %lu\n", (unsigned long) sizeof(zone_type))); + DEBUG(DEBUG_DBACCESS, 2, + (LOG_INFO, "sizeof(domain_type) = %lu\n", (unsigned long) sizeof(domain_type))); + DEBUG(DEBUG_DBACCESS, 2, + (LOG_INFO, "sizeof(rrset_type) = %lu\n", (unsigned long) sizeof(rrset_type))); + DEBUG(DEBUG_DBACCESS, 2, + (LOG_INFO, "sizeof(rr_type) = %lu\n", (unsigned long) sizeof(rr_type))); + DEBUG(DEBUG_DBACCESS, 2, + (LOG_INFO, "sizeof(rdata_atom_type) = %lu\n", (unsigned long) sizeof(rdata_atom_type))); + DEBUG(DEBUG_DBACCESS, 2, + (LOG_INFO, "sizeof(rbnode_t) = %lu\n", (unsigned long) sizeof(rbnode_t))); + + db_region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE, + DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1); + db = (namedb_type *) region_alloc(db_region, sizeof(struct namedb)); + db->region = db_region; + db->domains = domain_table_create(db->region); + db->zones = NULL; + db->zone_count = 0; + db->filename = region_strdup(db->region, filename); + db->crc = 0xffffffff; + db->diff_skip = 0; + + if (gettimeofday(&(db->diff_timestamp), NULL) != 0) { + log_msg(LOG_ERR, "unable to load %s: cannot initialize" + "timestamp", db->filename); + region_destroy(db_region); + return NULL; + } + + /* Open it... */ + db->fd = fopen(db->filename, "r"); + if (db->fd == NULL) { + log_msg(LOG_ERR, "unable to load %s: %s", + db->filename, strerror(errno)); + region_destroy(db_region); + return NULL; + } + + if (!read_magic(db)) { + log_msg(LOG_ERR, "corrupted database (read magic): %s", db->filename); + namedb_close(db); + return NULL; + } + + if (!read_size(db, &zone_count)) { + log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename); + namedb_close(db); + return NULL; + } + + DEBUG(DEBUG_DBACCESS, 1, + (LOG_INFO, "Retrieving %lu zones\n", (unsigned long) zone_count)); + + temp_region = region_create(xalloc, free); + dname_region = region_create(xalloc, free); + + db->zone_count = zone_count; + zones = (zone_type **) region_alloc(temp_region, + zone_count * sizeof(zone_type *)); + for (i = 0; i < zone_count; ++i) { + const dname_type *dname = read_dname(db->fd, dname_region); + if (!dname) { + log_msg(LOG_ERR, "corrupted database (read dname): %s", db->filename); + region_destroy(dname_region); + region_destroy(temp_region); + namedb_close(db); + return NULL; + } + zones[i] = (zone_type *) region_alloc(db->region, + sizeof(zone_type)); + zones[i]->next = db->zones; + db->zones = zones[i]; + zones[i]->apex = domain_table_insert(db->domains, dname); + zones[i]->soa_rrset = NULL; + zones[i]->soa_nx_rrset = NULL; + zones[i]->ns_rrset = NULL; +#ifdef NSEC3 + zones[i]->nsec3_soa_rr = NULL; + zones[i]->nsec3_last = NULL; +#endif + zones[i]->opts = zone_options_find(opt, domain_dname(zones[i]->apex)); + zones[i]->number = i + 1; + zones[i]->is_secure = 0; + zones[i]->updated = 1; + zones[i]->is_ok = 0; + zones[i]->dirty = region_alloc(db->region, sizeof(uint8_t)*num_children); + memset(zones[i]->dirty, 0, sizeof(uint8_t)*num_children); + if(!zones[i]->opts) { + log_msg(LOG_ERR, "cannot load database. Zone %s in db " + "%s, but not in config file (might " + "happen if you edited the config " + "file). Please rebuild database and " + "start again.", + dname_to_string(dname, NULL), db->filename); + region_destroy(dname_region); + region_destroy(temp_region); + namedb_close(db); + return NULL; + } + + region_free_all(dname_region); + } + + if (!read_size(db, &dname_count)) { + log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename); + region_destroy(dname_region); + region_destroy(temp_region); + namedb_close(db); + return NULL; + } + + DEBUG(DEBUG_DBACCESS, 1, + (LOG_INFO, "Retrieving %lu domain names\n", (unsigned long) dname_count)); + + domains = (domain_type **) region_alloc( + temp_region, dname_count * sizeof(domain_type *)); + for (i = 0; i < dname_count; ++i) { + const dname_type *dname = read_dname(db->fd, dname_region); + if (!dname) { + log_msg(LOG_ERR, "corrupted database (read dname): %s", db->filename); + region_destroy(dname_region); + region_destroy(temp_region); + namedb_close(db); + return NULL; + } + domains[i] = domain_table_insert(db->domains, dname); + region_free_all(dname_region); + } + + region_destroy(dname_region); + +#ifndef NDEBUG + fprintf(stderr, "database region after loading domain names: "); + region_dump_stats(db->region, stderr); + fprintf(stderr, "\n"); +#endif + + while ((rrset = read_rrset(db, dname_count, domains, zone_count, zones))) { + ++rrset_count; + rr_count += rrset->rr_count; + } + + DEBUG(DEBUG_DBACCESS, 1, + (LOG_INFO, "Retrieved %lu RRs in %lu RRsets\n", + (unsigned long) rr_count, (unsigned long) rrset_count)); + + region_destroy(temp_region); + + if ((db->crc_pos = ftello(db->fd)) == -1) { + log_msg(LOG_ERR, "ftello %s failed: %s", + db->filename, strerror(errno)); + namedb_close(db); + return NULL; + } + if (!read_size(db, &db->crc)) { + log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename); + namedb_close(db); + return NULL; + } + if (!read_magic(db)) { + log_msg(LOG_ERR, "corrupted database (read magic): %s", db->filename); + namedb_close(db); + return NULL; + } + + fclose(db->fd); + db->fd = NULL; + +#ifndef NDEBUG + fprintf(stderr, "database region after loading database: "); + region_dump_stats(db->region, stderr); + fprintf(stderr, "\n"); +#endif + + return db; +} + +void +namedb_close (struct namedb *db) +{ + if (db) { + if (db->fd) { + fclose(db->fd); + } + region_destroy(db->region); + } +} diff --git a/usr.sbin/nsd/dbcreate.c b/usr.sbin/nsd/dbcreate.c new file mode 100644 index 00000000000..bf55dbac813 --- /dev/null +++ b/usr.sbin/nsd/dbcreate.c @@ -0,0 +1,287 @@ +/* + * dbcreate.c -- routines to create an nsd(8) name database + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "namedb.h" + +static int write_db (namedb_type *db); +static int write_number(struct namedb *db, uint32_t number); + +struct namedb * +namedb_new (const char *filename) +{ + namedb_type *db; + region_type *region = region_create_custom(xalloc, free, + DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE, + DEFAULT_INITIAL_CLEANUP_SIZE, 1); + + /* Make a new structure... */ + db = (namedb_type *) region_alloc(region, sizeof(namedb_type)); + db->region = region; + db->domains = domain_table_create(region); + db->zones = NULL; + db->zone_count = 0; + db->filename = region_strdup(region, filename); + db->crc = 0xffffffff; + db->diff_skip = 0; + db->fd = NULL; + + if (gettimeofday(&(db->diff_timestamp), NULL) != 0) { + log_msg(LOG_ERR, "unable to load %s: cannot initialize " + "timestamp", db->filename); + region_destroy(region); + return NULL; + } + + /* + * Unlink the old database, if it exists. This is useful to + * ensure that NSD doesn't see the changes until a reload is done. + */ + if (unlink(db->filename) == -1 && errno != ENOENT) { + region_destroy(region); + return NULL; + } + + /* Create the database */ + if ((db->fd = fopen(db->filename, "w")) == NULL) { + region_destroy(region); + return NULL; + } + + if (!write_data_crc(db->fd, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE, &db->crc)) { + fclose(db->fd); + namedb_discard(db); + return NULL; + } + + return db; +} + + +int +namedb_save (struct namedb *db) +{ + if (write_db(db) != 0) { + return -1; + } + + /* Finish up and write the crc */ + if (!write_number(db, ~db->crc)) { + fclose(db->fd); + return -1; + } + + /* Write the magic... */ + if (!write_data_crc(db->fd, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE, &db->crc)) { + fclose(db->fd); + return -1; + } + + /* Close the database */ + fclose(db->fd); + + region_destroy(db->region); + return 0; +} + + +void +namedb_discard (struct namedb *db) +{ + unlink(db->filename); + region_destroy(db->region); +} + +static int +write_dname(struct namedb *db, domain_type *domain) +{ + const dname_type *dname = domain_dname(domain); + + if (!write_data_crc(db->fd, &dname->name_size, sizeof(dname->name_size), &db->crc)) + return -1; + + if (!write_data_crc(db->fd, dname_name(dname), dname->name_size, &db->crc)) + return -1; + + return 0; +} + +static int +write_number(struct namedb *db, uint32_t number) +{ + number = htonl(number); + return write_data_crc(db->fd, &number, sizeof(number), &db->crc); +} + +static int +write_rrset(struct namedb *db, domain_type *domain, rrset_type *rrset) +{ + uint16_t rr_count; + int i, j; + uint16_t type; + uint16_t klass; + + assert(db); + assert(domain); + assert(rrset); + + rr_count = htons(rrset->rr_count); + + if (!write_number(db, domain->number)) + return 1; + + if (!write_number(db, rrset->zone->number)) + return 1; + + type = htons(rrset_rrtype(rrset)); + if (!write_data_crc(db->fd, &type, sizeof(type), &db->crc)) + return 1; + + klass = htons(rrset_rrclass(rrset)); + if (!write_data_crc(db->fd, &klass, sizeof(klass), &db->crc)) + return 1; + + if (!write_data_crc(db->fd, &rr_count, sizeof(rr_count), &db->crc)) + return 1; + + for (i = 0; i < rrset->rr_count; ++i) { + rr_type *rr = &rrset->rrs[i]; + uint32_t ttl; + uint16_t rdata_count; + + rdata_count = htons(rr->rdata_count); + if (!write_data_crc(db->fd, &rdata_count, sizeof(rdata_count), &db->crc)) + return 1; + + ttl = htonl(rr->ttl); + if (!write_data_crc(db->fd, &ttl, sizeof(ttl), &db->crc)) + return 1; + + for (j = 0; j < rr->rdata_count; ++j) { + rdata_atom_type atom = rr->rdatas[j]; + if (rdata_atom_is_domain(rr->type, j)) { + if (!write_number(db, rdata_atom_domain(atom)->number)) + return 1; + + } else { + uint16_t size = htons(rdata_atom_size(atom)); + if (!write_data_crc(db->fd, &size, sizeof(size), &db->crc)) + return 1; + + if (!write_data_crc(db->fd, + rdata_atom_data(atom), + rdata_atom_size(atom), &db->crc)) + return 1; + + } + } + } + + return 0; +} + +static int +number_dnames_iterator(domain_type *node, void *user_data) +{ + uint32_t *current_number = (uint32_t *) user_data; + + node->number = *current_number; + ++*current_number; + + return 0; +} + +static int +write_dname_iterator(domain_type *node, void *user_data) +{ + namedb_type *db = (namedb_type *) user_data; + + return write_dname(db, node); +} + +static int +write_domain_iterator(domain_type *node, void *user_data) +{ + namedb_type *db = (namedb_type *) user_data; + rrset_type *rrset; + int error = 0; + + for (rrset = node->rrsets; rrset; rrset = rrset->next) { + error += write_rrset(db, node, rrset); + } + + return error; +} + +/* + * Writes databse data into open database *db + * + * Returns zero if success. + */ +static int +write_db(namedb_type *db) +{ + zone_type *zone; + uint32_t terminator = 0; + uint32_t dname_count = 1; + uint32_t zone_count = 1; + int errors = 0; + + for (zone = db->zones; zone; zone = zone->next) { + zone->number = zone_count; + ++zone_count; + + if (!zone->soa_rrset) { + fprintf(stderr, "SOA record not present in %s\n", + dname_to_string(domain_dname(zone->apex), + NULL)); + ++errors; + } + } + + if (errors > 0) + return -1; + + --zone_count; + if (!write_number(db, zone_count)) + return -1; + for (zone = db->zones; zone; zone = zone->next) { + if (write_dname(db, zone->apex)) + return -1; + } + + if (domain_table_iterate(db->domains, number_dnames_iterator, &dname_count)) + return -1; + + --dname_count; + if (!write_number(db, dname_count)) + return -1; + + DEBUG(DEBUG_ZONEC, 1, + (LOG_INFO, "Storing %lu domain names\n", (unsigned long) dname_count)); + + if (domain_table_iterate(db->domains, write_dname_iterator, db)) + return -1; + + if (domain_table_iterate(db->domains, write_domain_iterator, db)) + return -1; + + if (!write_data_crc(db->fd, &terminator, sizeof(terminator), &db->crc)) + return -1; + + return 0; +} diff --git a/usr.sbin/nsd/difffile.c b/usr.sbin/nsd/difffile.c new file mode 100644 index 00000000000..c11f5e7faaf --- /dev/null +++ b/usr.sbin/nsd/difffile.c @@ -0,0 +1,1490 @@ +/* + * difffile.c - DIFF file handling source code. Read and write diff files. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> +#include <assert.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include "difffile.h" +#include "util.h" +#include "packet.h" +#include "rdata.h" + +static int +write_32(FILE *out, uint32_t val) +{ + val = htonl(val); + return write_data(out, &val, sizeof(val)); +} + +static int +write_16(FILE *out, uint16_t val) +{ + val = htons(val); + return write_data(out, &val, sizeof(val)); +} + +static int +write_8(FILE *out, uint8_t val) +{ + return write_data(out, &val, sizeof(val)); +} + +static int +write_str(FILE *out, const char* str) +{ + uint32_t len = strlen(str); + if(!write_32(out, len)) + return 0; + return write_data(out, str, len); +} + +void +diff_write_packet(const char* zone, uint32_t new_serial, uint16_t id, + uint32_t seq_nr, uint8_t* data, size_t len, nsd_options_t* opt) +{ + const char* filename = opt->difffile; + struct timeval tv; + FILE *df; + uint32_t file_len = sizeof(uint32_t) + strlen(zone) + + sizeof(new_serial) + sizeof(id) + sizeof(seq_nr) + len; + + if (gettimeofday(&tv, NULL) != 0) { + log_msg(LOG_ERR, "could not set timestamp for %s: %s", + filename, strerror(errno)); + return; + } + + df = fopen(filename, "a"); + if(!df) { + log_msg(LOG_ERR, "could not open file %s for append: %s", + filename, strerror(errno)); + return; + } + + if(!write_32(df, DIFF_PART_IXFR) || + !write_32(df, (uint32_t) tv.tv_sec) || + !write_32(df, (uint32_t) tv.tv_usec) || + !write_32(df, file_len) || + !write_str(df, zone) || + !write_32(df, new_serial) || + !write_16(df, id) || + !write_32(df, seq_nr) || + !write_data(df, data, len) || + !write_32(df, file_len)) + { + log_msg(LOG_ERR, "could not write to file %s: %s", + filename, strerror(errno)); + } + fflush(df); + fclose(df); +} + +void +diff_write_commit(const char* zone, uint32_t old_serial, + uint32_t new_serial, uint16_t id, uint32_t num_parts, + uint8_t commit, const char* log_str, nsd_options_t* opt) +{ + const char* filename = opt->difffile; + struct timeval tv; + FILE *df; + uint32_t len; + + if (gettimeofday(&tv, NULL) != 0) { + log_msg(LOG_ERR, "could not set timestamp for %s: %s", + filename, strerror(errno)); + return; + } + + df = fopen(filename, "a"); + if(!df) { + log_msg(LOG_ERR, "could not open file %s for append: %s", + filename, strerror(errno)); + return; + } + + len = strlen(zone) + sizeof(len) + sizeof(old_serial) + + sizeof(new_serial) + sizeof(id) + sizeof(num_parts) + + sizeof(commit) + strlen(log_str) + sizeof(len); + + if(!write_32(df, DIFF_PART_SURE) || + !write_32(df, (uint32_t) tv.tv_sec) || + !write_32(df, (uint32_t) tv.tv_usec) || + !write_32(df, len) || + !write_str(df, zone) || + !write_32(df, old_serial) || + !write_32(df, new_serial) || + !write_16(df, id) || + !write_32(df, num_parts) || + !write_8(df, commit) || + !write_str(df, log_str) || + !write_32(df, len)) + { + log_msg(LOG_ERR, "could not write to file %s: %s", + filename, strerror(errno)); + } + fflush(df); + fclose(df); +} + +/* + * Checksum to signal no data change occured (for example, by a + * zonec run. + */ +int +db_crc_different(namedb_type* db) +{ + FILE *fd = fopen(db->filename, "r"); + uint32_t crc_file; + char buf[NAMEDB_MAGIC_SIZE]; + if(fd == NULL) { + log_msg(LOG_ERR, "unable to load %s: %s", + db->filename, strerror(errno)); + return -1; + } + + /* seek to position of CRC, check it and magic no */ + if(fseeko(fd, db->crc_pos, SEEK_SET)==-1) { + log_msg(LOG_ERR, "unable to fseeko %s: %s. db changed?", + db->filename, strerror(errno)); + fclose(fd); + return -1; + } + + if(fread(&crc_file, sizeof(crc_file), 1, fd) != 1) { + if(!feof(fd)) + log_msg(LOG_ERR, "could not read %s CRC: %s. " + "db changed?", db->filename, strerror(errno)); + fclose(fd); + return -1; + } + crc_file = ntohl(crc_file); + + if(fread(buf, sizeof(char), sizeof(buf), fd) != sizeof(buf)) { + if(!feof(fd)) + log_msg(LOG_ERR, "could not read %s magic: %s. " + "db changed?", db->filename, strerror(errno)); + fclose(fd); + return -1; + } + if(memcmp(buf, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE) != 0) { + fclose(fd); + return -1; + } + + fclose(fd); + + if(db->crc == crc_file) + return 0; + return 1; +} + +int +diff_read_32(FILE *in, uint32_t* result) +{ + if (fread(result, sizeof(*result), 1, in) == 1) { + *result = ntohl(*result); + return 1; + } else { + return 0; + } +} + +int +diff_read_16(FILE *in, uint16_t* result) +{ + if (fread(result, sizeof(*result), 1, in) == 1) { + *result = ntohs(*result); + return 1; + } else { + return 0; + } +} + +int +diff_read_8(FILE *in, uint8_t* result) +{ + if (fread(result, sizeof(*result), 1, in) == 1) { + return 1; + } else { + return 0; + } +} + +int +diff_read_str(FILE* in, char* buf, size_t len) +{ + uint32_t disklen; + if(!diff_read_32(in, &disklen)) + return 0; + if(disklen >= len) + return 0; + if(fread(buf, disklen, 1, in) != 1) + return 0; + buf[disklen] = 0; + return 1; +} + +static void +add_rdata_to_recyclebin(namedb_type* db, rr_type* rr) +{ + /* add rdatas to recycle bin. */ + size_t i; + for(i=0; i<rr->rdata_count; i++) + { + if(!rdata_atom_is_domain(rr->type, i)) + region_recycle(db->region, rr->rdatas[i].data, + rdata_atom_size(rr->rdatas[i]) + + sizeof(uint16_t)); + } + region_recycle(db->region, rr->rdatas, + sizeof(rdata_atom_type)*rr->rdata_count); +} + +/* this routine determines if below a domain there exist names with + * data (is_existing) or no names below the domain have data. + */ +static int +has_data_below(domain_type* top) +{ + domain_type* d = top; + assert(d != NULL); + /* in the canonical ordering subdomains are after this name */ + d = domain_next(d); + while(d != NULL && dname_is_subdomain(domain_dname(d), domain_dname(top))) { + if(d->is_existing) + return 1; + d = domain_next(d); + } + return 0; +} + +static void +rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset) +{ + int i; + /* find previous */ + rrset_type** pp = &domain->rrsets; + while(*pp && *pp != rrset) { + pp = &( (*pp)->next ); + } + if(!*pp) { + /* rrset does not exist for domain */ + return; + } + *pp = rrset->next; + + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete rrset of %s type %s", + dname_to_string(domain_dname(domain),0), + rrtype_to_string(rrset_rrtype(rrset)))); + + /* is this a SOA rrset ? */ + if(rrset->zone->soa_rrset == rrset) { + rrset->zone->soa_rrset = 0; + rrset->zone->updated = 1; + } + if(rrset->zone->ns_rrset == rrset) { + rrset->zone->ns_rrset = 0; + } +#ifdef DNSSEC + if(domain == rrset->zone->apex && rrset_rrtype(rrset) == TYPE_RRSIG) { + for (i = 0; i < rrset->rr_count; ++i) { + if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_SOA) { + rrset->zone->is_secure = 0; + break; + } + } + } +#endif + /* recycle the memory space of the rrset */ + for (i = 0; i < rrset->rr_count; ++i) + add_rdata_to_recyclebin(db, &rrset->rrs[i]); + region_recycle(db->region, rrset->rrs, + sizeof(rr_type) * rrset->rr_count); + region_recycle(db->region, rrset, sizeof(rrset_type)); + + /* is the node now an empty node (completely deleted) */ + if(domain->rrsets == 0) { + /* if there is no data below it, it becomes non existing. + also empty nonterminals above it become nonexisting */ + /* check for data below this node. */ + if(!has_data_below(domain)) { + /* nonexist this domain and all parent empty nonterminals */ + domain_type* p = domain; + while(p != NULL && p->rrsets == 0) { + p->is_existing = 0; + p = p->parent; + } + } + } + rrset->rr_count = 0; +} + +static int +rdatas_equal(rdata_atom_type *a, rdata_atom_type *b, int num, uint16_t type) +{ + int k; + for(k = 0; k < num; k++) + { + if(rdata_atom_is_domain(type, k)) { + if(dname_compare(domain_dname(a[k].domain), + domain_dname(b[k].domain))!=0) + return 0; + } else { + /* check length */ + if(a[k].data[0] != b[k].data[0]) + return 0; + /* check data */ + if(memcmp(a[k].data+1, b[k].data+1, a[k].data[0])!=0) + return 0; + } + } + return 1; +} + +static int +find_rr_num(rrset_type* rrset, + uint16_t type, uint16_t klass, + rdata_atom_type *rdatas, ssize_t rdata_num) +{ + int i; + + for(i=0; i < rrset->rr_count; ++i) { + if(rrset->rrs[i].type == type && + rrset->rrs[i].klass == klass && + rrset->rrs[i].rdata_count == rdata_num && + rdatas_equal(rdatas, rrset->rrs[i].rdatas, rdata_num, type)) + { + return i; + } + } + + return -1; +} + +static int +delete_RR(namedb_type* db, const dname_type* dname, + uint16_t type, uint16_t klass, + buffer_type* packet, size_t rdatalen, zone_type *zone, + region_type* temp_region) +{ + domain_type *domain; + rrset_type *rrset; + domain = domain_table_find(db->domains, dname); + if(!domain) { + log_msg(LOG_WARNING, "diff: domain %s does not exist", + dname_to_string(dname,0)); + buffer_skip(packet, rdatalen); + return 1; /* not fatal error */ + } + rrset = domain_find_rrset(domain, zone, type); + if(!rrset) { + log_msg(LOG_WARNING, "diff: rrset %s does not exist", + dname_to_string(dname,0)); + buffer_skip(packet, rdatalen); + return 1; /* not fatal error */ + } else { + /* find the RR in the rrset */ + domain_table_type *temptable; + rdata_atom_type *rdatas; + ssize_t rdata_num; + int rrnum; + temptable = domain_table_create(temp_region); + /* This will ensure that the dnames in rdata are + * normalized, conform RFC 4035, section 6.2 + */ + rdata_num = rdata_wireformat_to_rdata_atoms( + temp_region, temptable, type, rdatalen, packet, &rdatas); + if(rdata_num == -1) { + log_msg(LOG_ERR, "diff: bad rdata for %s", + dname_to_string(dname,0)); + return 0; + } + rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num); + if(rrnum == -1) { + log_msg(LOG_WARNING, "diff: RR %s does not exist", + dname_to_string(dname,0)); + return 1; /* not fatal error */ + } + if(rrset->rr_count == 1) { + /* delete entire rrset */ + rrset_delete(db, domain, rrset); + } else { + /* swap out the bad RR and decrease the count */ + rr_type* rrs_orig = rrset->rrs; + add_rdata_to_recyclebin(db, &rrset->rrs[rrnum]); + if(rrnum < rrset->rr_count-1) + rrset->rrs[rrnum] = rrset->rrs[rrset->rr_count-1]; + memset(&rrset->rrs[rrset->rr_count-1], 0, sizeof(rr_type)); + /* realloc the rrs array one smaller */ + rrset->rrs = region_alloc_init(db->region, rrs_orig, + sizeof(rr_type) * (rrset->rr_count-1)); + if(!rrset->rrs) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + region_recycle(db->region, rrs_orig, + sizeof(rr_type) * rrset->rr_count); + rrset->rr_count --; + } + } + return 1; +} + +static int +add_RR(namedb_type* db, const dname_type* dname, + uint16_t type, uint16_t klass, uint32_t ttl, + buffer_type* packet, size_t rdatalen, zone_type *zone) +{ + domain_type* domain; + rrset_type* rrset; + rdata_atom_type *rdatas; + rr_type *rrs_old; + ssize_t rdata_num; + int rrnum; + domain = domain_table_find(db->domains, dname); + if(!domain) { + /* create the domain */ + domain = domain_table_insert(db->domains, dname); + } + rrset = domain_find_rrset(domain, zone, type); + if(!rrset) { + /* create the rrset */ + rrset = region_alloc(db->region, sizeof(rrset_type)); + if(!rrset) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + rrset->zone = zone; + rrset->rrs = 0; + rrset->rr_count = 0; + domain_add_rrset(domain, rrset); + } + + /* dnames in rdata are normalized, conform RFC 4035, + * Section 6.2 + */ + rdata_num = rdata_wireformat_to_rdata_atoms( + db->region, db->domains, type, rdatalen, packet, &rdatas); + if(rdata_num == -1) { + log_msg(LOG_ERR, "diff: bad rdata for %s", + dname_to_string(dname,0)); + return 0; + } + rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num); + if(rrnum != -1) { + DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR %s already exists", + dname_to_string(dname,0))); + /* ignore already existing RR: lenient accepting of messages */ + return 1; + } + + /* re-alloc the rrs and add the new */ + rrs_old = rrset->rrs; + rrset->rrs = region_alloc(db->region, + (rrset->rr_count+1) * sizeof(rr_type)); + if(!rrset->rrs) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + if(rrs_old) + memcpy(rrset->rrs, rrs_old, rrset->rr_count * sizeof(rr_type)); + region_recycle(db->region, rrs_old, sizeof(rr_type) * rrset->rr_count); + rrset->rr_count ++; + + rrset->rrs[rrset->rr_count - 1].owner = domain; + rrset->rrs[rrset->rr_count - 1].rdatas = rdatas; + rrset->rrs[rrset->rr_count - 1].ttl = ttl; + rrset->rrs[rrset->rr_count - 1].type = type; + rrset->rrs[rrset->rr_count - 1].klass = klass; + rrset->rrs[rrset->rr_count - 1].rdata_count = rdata_num; + + /* see if it is a SOA */ + if(domain == zone->apex) { + if(type == TYPE_SOA) { + uint32_t soa_minimum; + zone->soa_rrset = rrset; + zone->updated = 1; + /* BUG #103 tweaked SOA ttl value */ + if(zone->soa_nx_rrset == 0) { + zone->soa_nx_rrset = region_alloc(db->region, + sizeof(rrset_type)); + if(!zone->soa_nx_rrset) { + log_msg(LOG_ERR, "out of memory, %s:%d", + __FILE__, __LINE__); + exit(1); + } + zone->soa_nx_rrset->rr_count = 1; + zone->soa_nx_rrset->next = 0; + zone->soa_nx_rrset->zone = zone; + zone->soa_nx_rrset->rrs = region_alloc(db->region, + sizeof(rr_type)); + if(!zone->soa_nx_rrset->rrs) { + log_msg(LOG_ERR, "out of memory, %s:%d", + __FILE__, __LINE__); + exit(1); + } + } + memcpy(zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type)); + memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]), + rdata_atom_size(rrset->rrs->rdatas[6])); + if (rrset->rrs->ttl > ntohl(soa_minimum)) { + rrset->zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum); + } + } + if(type == TYPE_NS) { + zone->ns_rrset = rrset; + } +#ifdef DNSSEC + if(type == TYPE_RRSIG) { + int i; + for (i = 0; i < rrset->rr_count; ++i) { + if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_SOA) { + zone->is_secure = 1; + break; + } + } + } +#endif + } + return 1; +} + +static zone_type* +find_zone(namedb_type* db, const dname_type* zone_name, nsd_options_t* opt, + size_t child_count) +{ + domain_type *domain; + zone_type* zone; + domain = domain_table_find(db->domains, zone_name); + if(!domain) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating domain %s", + dname_to_string(zone_name,0))); + /* create the zone and domain of apex (zone has config options) */ + domain = domain_table_insert(db->domains, zone_name); + } else { + /* O(1) if SOA exists */ + zone = domain_find_zone(domain); + /* if domain was empty (no rrsets, empty zone) search in zonelist */ + /* check apex to make sure we don't find a parent zone */ + if(!zone || zone->apex != domain) + zone = namedb_find_zone(db, domain); + if(zone) { + assert(zone->apex == domain); + return zone; + } + } + /* create the zone */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating zone_type %s", + dname_to_string(zone_name,0))); + zone = (zone_type *) region_alloc(db->region, sizeof(zone_type)); + if(!zone) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + zone->next = db->zones; + db->zones = zone; + db->zone_count++; + zone->apex = domain; + zone->soa_rrset = 0; + zone->soa_nx_rrset = 0; + zone->ns_rrset = 0; +#ifdef NSEC3 + zone->nsec3_soa_rr = NULL; + zone->nsec3_last = NULL; +#endif + zone->dirty = region_alloc(db->region, sizeof(uint8_t)*child_count); + if(!zone->dirty) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + memset(zone->dirty, 0, sizeof(uint8_t)*child_count); + zone->opts = zone_options_find(opt, domain_dname(zone->apex)); + if(!zone->opts) { + log_msg(LOG_ERR, "xfr: zone %s not in config.", + dname_to_string(zone_name,0)); + return 0; + } + zone->number = db->zone_count; + zone->is_secure = 0; + zone->updated = 1; + zone->is_ok = 0; + return zone; +} + +static void +delete_zone_rrs(namedb_type* db, zone_type* zone) +{ + rrset_type *rrset; + domain_type *domain = zone->apex; + /* go through entire tree below the zone apex (incl subzones) */ + while(domain && dname_is_subdomain( + domain_dname(domain), domain_dname(zone->apex))) + { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete zone visit %s", + dname_to_string(domain_dname(domain),0))); + /* delete all rrsets of the zone */ + while((rrset = domain_find_any_rrset(domain, zone))) { + rrset_delete(db, domain, rrset); + } + domain = domain_next(domain); + } + + DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: recyclebin holds %lu bytes", + (unsigned long) region_get_recycle_size(db->region))); +#ifndef NDEBUG + if(nsd_debug_level >= 1) + region_log_stats(db->region); +#endif + + assert(zone->soa_rrset == 0); + /* keep zone->soa_nx_rrset alloced */ + assert(zone->ns_rrset == 0); + assert(zone->is_secure == 0); + assert(zone->updated == 1); +} + +static int +apply_ixfr(namedb_type* db, FILE *in, const off_t* startpos, + const char* zone, uint32_t serialno, nsd_options_t* opt, + uint16_t id, uint32_t seq_nr, uint32_t seq_total, + int* is_axfr, int* delete_mode, int* rr_count, + size_t child_count) +{ + uint32_t filelen, msglen, pkttype, timestamp[2]; + int qcount, ancount, counter; + buffer_type* packet; + region_type* region; + int i; + uint16_t rrlen; + const dname_type *dname_zone, *dname; + zone_type* zone_db; + char file_zone_name[3072]; + uint32_t file_serial, file_seq_nr; + uint16_t file_id; + off_t mempos; + + memmove(&mempos, startpos, sizeof(off_t)); + if(fseeko(in, mempos, SEEK_SET) == -1) { + log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno)); + return 0; + } + /* read ixfr packet RRs and apply to in memory db */ + + if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_IXFR) { + log_msg(LOG_ERR, "could not read type or wrong type"); + return 0; + } + if(!diff_read_32(in, ×tamp[0]) || + !diff_read_32(in, ×tamp[1])) { + log_msg(LOG_ERR, "could not read timestamp"); + return 0; + } + + if(!diff_read_32(in, &filelen)) { + log_msg(LOG_ERR, "could not read len"); + return 0; + } + + /* read header */ + if(filelen < QHEADERSZ + sizeof(uint32_t)*3 + sizeof(uint16_t)) { + log_msg(LOG_ERR, "msg too short"); + return 0; + } + + region = region_create(xalloc, free); + if(!region) { + log_msg(LOG_ERR, "out of memory"); + return 0; + } + + if(!diff_read_str(in, file_zone_name, sizeof(file_zone_name)) || + !diff_read_32(in, &file_serial) || + !diff_read_16(in, &file_id) || + !diff_read_32(in, &file_seq_nr)) + { + log_msg(LOG_ERR, "could not part data"); + region_destroy(region); + return 0; + } + + if(strcmp(file_zone_name, zone) != 0 || serialno != file_serial || + id != file_id || seq_nr != file_seq_nr) { + log_msg(LOG_ERR, "internal error: reading part with changed id"); + region_destroy(region); + return 0; + } + msglen = filelen - sizeof(uint32_t)*3 - sizeof(uint16_t) + - strlen(file_zone_name); + packet = buffer_create(region, QIOBUFSZ); + dname_zone = dname_parse(region, zone); + zone_db = find_zone(db, dname_zone, opt, child_count); + if(!zone_db) { + log_msg(LOG_ERR, "no zone exists"); + region_destroy(region); + return 0; + } + + if(msglen > QIOBUFSZ) { + log_msg(LOG_ERR, "msg too long"); + region_destroy(region); + return 0; + } + buffer_clear(packet); + if(fread(buffer_begin(packet), msglen, 1, in) != 1) { + log_msg(LOG_ERR, "short fread: %s", strerror(errno)); + region_destroy(region); + return 0; + } + buffer_set_limit(packet, msglen); + + /* only answer section is really used, question, additional and + authority section RRs are skipped */ + qcount = QDCOUNT(packet); + ancount = ANCOUNT(packet); + buffer_skip(packet, QHEADERSZ); + + /* skip queries */ + for(i=0; i<qcount; ++i) + if(!packet_skip_rr(packet, 1)) { + log_msg(LOG_ERR, "bad RR in question section"); + region_destroy(region); + return 0; + } + + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: started packet for zone %s", + dname_to_string(dname_zone, 0))); + /* first RR: check if SOA and correct zone & serialno */ + if(*rr_count == 0) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parse first RR", + dname_to_string(dname_zone, 0))); + dname = dname_make_from_packet(region, packet, 1, 1); + if(!dname) { + log_msg(LOG_ERR, "could not parse dname"); + region_destroy(region); + return 0; + } + if(dname_compare(dname_zone, dname) != 0) { + log_msg(LOG_ERR, "SOA dname %s not equal to zone", + dname_to_string(dname,0)); + log_msg(LOG_ERR, "zone dname is %s", + dname_to_string(dname_zone,0)); + region_destroy(region); + return 0; + } + if(!buffer_available(packet, 10)) { + log_msg(LOG_ERR, "bad SOA RR"); + region_destroy(region); + return 0; + } + if(buffer_read_u16(packet) != TYPE_SOA || + buffer_read_u16(packet) != CLASS_IN) { + log_msg(LOG_ERR, "first RR not SOA IN"); + region_destroy(region); + return 0; + } + buffer_skip(packet, sizeof(uint32_t)); /* ttl */ + if(!buffer_available(packet, buffer_read_u16(packet)) || + !packet_skip_dname(packet) /* skip prim_ns */ || + !packet_skip_dname(packet) /* skip email */) { + log_msg(LOG_ERR, "bad SOA RR"); + region_destroy(region); + return 0; + } + if(buffer_read_u32(packet) != serialno) { + buffer_skip(packet, -4); + log_msg(LOG_ERR, "SOA serial %d different from commit %d", + buffer_read_u32(packet), serialno); + region_destroy(region); + return 0; + } + buffer_skip(packet, sizeof(uint32_t)*4); + counter = 1; + *rr_count = 1; + *is_axfr = 0; + *delete_mode = 0; + + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s start count %d, ax %d, delmode %d", + dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); + } + else counter = 0; + + for(; counter < ancount; ++counter,++(*rr_count)) + { + uint16_t type, klass; + uint32_t ttl; + + if(!(dname=dname_make_from_packet(region, packet, 1,1))) { + log_msg(LOG_ERR, "bad xfr RR dname %d", *rr_count); + region_destroy(region); + return 0; + } + if(!buffer_available(packet, 10)) { + log_msg(LOG_ERR, "bad xfr RR format %d", *rr_count); + region_destroy(region); + return 0; + } + type = buffer_read_u16(packet); + klass = buffer_read_u16(packet); + ttl = buffer_read_u32(packet); + rrlen = buffer_read_u16(packet); + if(!buffer_available(packet, rrlen)) { + log_msg(LOG_ERR, "bad xfr RR rdata %d, len %d have %d", + *rr_count, rrlen, (int)buffer_remaining(packet)); + region_destroy(region); + return 0; + } + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parsed count %d, ax %d, delmode %d", + dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); + + if(*rr_count == 1 && type != TYPE_SOA) { + /* second RR: if not SOA: this is an AXFR; delete all zone contents */ + delete_zone_rrs(db, zone_db); + /* add everything else (incl end SOA) */ + *delete_mode = 0; + *is_axfr = 1; + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s sawAXFR count %d, ax %d, delmode %d", + dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); + } + if(*rr_count == 1 && type == TYPE_SOA) { + /* if the serial no of the SOA equals the serialno, then AXFR */ + size_t bufpos = buffer_position(packet); + uint32_t thisserial; + if(!packet_skip_dname(packet) || + !packet_skip_dname(packet) || + buffer_remaining(packet) < sizeof(uint32_t)*5) + { + log_msg(LOG_ERR, "bad xfr SOA RR formerr."); + region_destroy(region); + return 0; + } + thisserial = buffer_read_u32(packet); + if(thisserial == serialno) { + /* AXFR */ + delete_zone_rrs(db, zone_db); + *delete_mode = 0; + *is_axfr = 1; + } + buffer_set_position(packet, bufpos); + } + if(type == TYPE_SOA && !*is_axfr) { + /* switch from delete-part to add-part and back again, + just before soa - so it gets deleted and added too */ + /* this means we switch to delete mode for the final SOA */ + *delete_mode = !*delete_mode; + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s IXFRswapdel count %d, ax %d, delmode %d", + dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); + } + if(type == TYPE_TSIG || type == TYPE_OPT) { + /* ignore pseudo RRs */ + buffer_skip(packet, rrlen); + continue; + } + + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfr %s RR dname is %s type %s", + *delete_mode?"del":"add", + dname_to_string(dname,0), rrtype_to_string(type))); + if(*delete_mode) { + /* delete this rr */ + if(!*is_axfr && type == TYPE_SOA && counter==ancount-1 + && seq_nr == seq_total-1) { + continue; /* do not delete final SOA RR for IXFR */ + } + if(!delete_RR(db, dname, type, klass, packet, + rrlen, zone_db, region)) { + region_destroy(region); + return 0; + } + } + else + { + /* add this rr */ + if(!add_RR(db, dname, type, klass, ttl, packet, + rrlen, zone_db)) { + region_destroy(region); + return 0; + } + } + } + region_destroy(region); + return 1; +} + +static int +check_for_bad_serial(namedb_type* db, const char* zone_str, uint32_t old_serial) +{ + /* see if serial OK with in-memory serial */ + domain_type* domain; + region_type* region = region_create(xalloc, free); + const dname_type* zone_name = dname_parse(region, zone_str); + zone_type* zone = 0; + domain = domain_table_find(db->domains, zone_name); + if(domain) + zone = domain_find_zone(domain); + if(zone && zone->apex == domain && zone->soa_rrset && old_serial) + { + uint32_t memserial; + memcpy(&memserial, rdata_atom_data(zone->soa_rrset->rrs[0].rdatas[2]), + sizeof(uint32_t)); + if(old_serial != ntohl(memserial)) { + region_destroy(region); + return 1; + } + } + region_destroy(region); + return 0; +} + +/* for multiple tcp packets use a data structure that has + * a rbtree (zone_names) with for each zone: + * has a rbtree by sequence number + * with inside a serial_number and ID (for checking only) + * and contains a off_t to the IXFR packet in the file. + * so when you get a commit for a zone, get zone obj, find sequence, + * then check if you have all sequence numbers available. Apply all packets. + */ +struct diff_read_data { + /* rbtree of struct diff_zone*/ + rbtree_t* zones; + /* region for allocation */ + region_type* region; +}; +struct diff_zone { + /* key is dname of zone */ + rbnode_t node; + /* rbtree of struct diff_xfrpart */ + rbtree_t* parts; +}; +struct diff_xfrpart { + /* key is sequence number */ + rbnode_t node; + uint32_t seq_nr; + uint32_t new_serial; + uint16_t id; + off_t file_pos; +}; + +static struct diff_read_data* +diff_read_data_create() +{ + region_type* region = region_create(xalloc, free); + struct diff_read_data* data = (struct diff_read_data*) + region_alloc(region, sizeof(struct diff_read_data)); + if(!data) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + data->region = region; + data->zones = rbtree_create(region, + (int (*)(const void *, const void *)) dname_compare); + return data; +} + +static struct diff_zone* +diff_read_find_zone(struct diff_read_data* data, const char* name) +{ + const dname_type* dname = dname_parse(data->region, name); + struct diff_zone* zp = (struct diff_zone*) + rbtree_search(data->zones, dname); + return zp; +} + +static int intcompf(const void* a, const void* b) +{ + if(*(uint32_t*)a < *(uint32_t*)b) + return -1; + if(*(uint32_t*)a > *(uint32_t*)b) + return +1; + return 0; +} + +static struct diff_zone* +diff_read_insert_zone(struct diff_read_data* data, const char* name) +{ + const dname_type* dname = dname_parse(data->region, name); + struct diff_zone* zp = region_alloc(data->region, + sizeof(struct diff_zone)); + if(!zp) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + zp->node = *RBTREE_NULL; + zp->node.key = dname; + zp->parts = rbtree_create(data->region, intcompf); + rbtree_insert(data->zones, (rbnode_t*)zp); + return zp; +} + +static struct diff_xfrpart* +diff_read_find_part(struct diff_zone* zp, uint32_t seq_nr) +{ + struct diff_xfrpart* xp = (struct diff_xfrpart*) + rbtree_search(zp->parts, &seq_nr); + return xp; +} + +static struct diff_xfrpart* +diff_read_insert_part(struct diff_read_data* data, + struct diff_zone* zp, uint32_t seq_nr) +{ + struct diff_xfrpart* xp = region_alloc(data->region, + sizeof(struct diff_xfrpart)); + if(!xp) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + xp->node = *RBTREE_NULL; + xp->node.key = &xp->seq_nr; + xp->seq_nr = seq_nr; + rbtree_insert(zp->parts, (rbnode_t*)xp); + return xp; +} + +/* mark commit as rollback and close inputfile, fatal exits */ +static void +mark_and_exit(nsd_options_t* opt, FILE* f, off_t commitpos, const char* desc) +{ + const char* filename = opt->difffile; + fclose(f); + if(!(f = fopen(filename, "r+"))) { + log_msg(LOG_ERR, "mark xfr, failed to re-open difffile %s: %s", + filename, strerror(errno)); + } else if(fseeko(f, commitpos, SEEK_SET) == -1) { + log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno)); + fclose(f); + } else { + uint8_t c = 0; + fwrite(&c, sizeof(c), 1, f); + fclose(f); + log_msg(LOG_ERR, "marked xfr as failed: %s", desc); + log_msg(LOG_ERR, "marked xfr so that next reload can succeed"); + } + exit(1); +} + +static int +read_sure_part(namedb_type* db, FILE *in, nsd_options_t* opt, + struct diff_read_data* data, struct diff_log** log, + size_t child_count) +{ + char zone_buf[3072]; + char log_buf[5120]; + uint32_t old_serial, new_serial, num_parts; + uint16_t id; + uint8_t committed; + struct diff_zone *zp; + uint32_t i; + int have_all_parts = 1; + struct diff_log* thislog = 0; + off_t commitpos; + + /* read zone name and serial */ + if(!diff_read_str(in, zone_buf, sizeof(zone_buf)) || + !diff_read_32(in, &old_serial) || + !diff_read_32(in, &new_serial) || + !diff_read_16(in, &id) || + !diff_read_32(in, &num_parts)) { + log_msg(LOG_ERR, "diff file bad commit part"); + return 0; + } + commitpos = ftello(in); /* position of commit byte */ + if(commitpos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno)); + return 0; + } + if(!diff_read_8(in, &committed) || + !diff_read_str(in, log_buf, sizeof(log_buf)) ) + { + log_msg(LOG_ERR, "diff file bad commit part"); + return 0; + } + + if(log) { + thislog = (struct diff_log*)region_alloc(db->region, sizeof(struct diff_log)); + if(!thislog) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + thislog->zone_name = region_strdup(db->region, zone_buf); + thislog->comment = region_strdup(db->region, log_buf); + thislog->error = 0; + thislog->next = *log; + *log = thislog; + } + + /* has been read in completely */ + zp = diff_read_find_zone(data, zone_buf); + if(!zp) { + log_msg(LOG_ERR, "diff file commit without IXFR"); + if(thislog) + thislog->error = "error no IXFR parts"; + return 1; + } + if(committed && check_for_bad_serial(db, zone_buf, old_serial)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, + "skipping diff file commit with bad serial")); + zp->parts->root = RBTREE_NULL; + zp->parts->count = 0; + if(thislog) + thislog->error = "error bad serial"; + return 1; + } + for(i=0; i<num_parts; i++) { + struct diff_xfrpart *xp = diff_read_find_part(zp, i); + if(!xp || xp->id != id || xp->new_serial != new_serial) { + have_all_parts = 0; + } + } + if(!have_all_parts) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, + "skipping diff file commit without all parts")); + if(thislog) + thislog->error = "error missing parts"; + } + + if(committed && have_all_parts) + { + int is_axfr=0, delete_mode=0, rr_count=0; + off_t resume_pos; + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "processing xfr: %s", log_buf)); + resume_pos = ftello(in); + if(resume_pos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno)); + return 0; + } + for(i=0; i<num_parts; i++) { + struct diff_xfrpart *xp = diff_read_find_part(zp, i); + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "processing xfr: apply part %d", (int)i)); + if(!apply_ixfr(db, in, &xp->file_pos, zone_buf, new_serial, opt, + id, xp->seq_nr, num_parts, &is_axfr, &delete_mode, + &rr_count, child_count)) { + log_msg(LOG_ERR, "bad ixfr packet part %d in %s", (int)i, + opt->difffile); + mark_and_exit(opt, in, commitpos, log_buf); + } + } + if(fseeko(in, resume_pos, SEEK_SET) == -1) { + log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno)); + return 0; + } + } + else { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "skipping xfr: %s", log_buf)); + } + + /* clean out the parts for the zone after the commit/rollback */ + zp->parts->root = RBTREE_NULL; + zp->parts->count = 0; + return 1; +} + +static int +store_ixfr_data(FILE *in, uint32_t len, struct diff_read_data* data, off_t* startpos) +{ + char zone_name[3072]; + struct diff_zone* zp; + struct diff_xfrpart* xp; + uint32_t new_serial, seq; + uint16_t id; + if(!diff_read_str(in, zone_name, sizeof(zone_name)) || + !diff_read_32(in, &new_serial) || + !diff_read_16(in, &id) || + !diff_read_32(in, &seq)) { + log_msg(LOG_INFO, "could not read ixfr store info: file format error"); + return 0; + } + len -= sizeof(uint32_t)*3 + sizeof(uint16_t) + strlen(zone_name); + if(fseeko(in, len, SEEK_CUR) == -1) + log_msg(LOG_INFO, "fseek failed: %s", strerror(errno)); + /* store the info */ + zp = diff_read_find_zone(data, zone_name); + if(!zp) + zp = diff_read_insert_zone(data, zone_name); + xp = diff_read_find_part(zp, seq); + if(xp) { + log_msg(LOG_INFO, "discarding partial xfr part: %s %d", zone_name, seq); + /* overwrite with newer value (which probably relates to next commit) */ + } + else { + xp = diff_read_insert_part(data, zp, seq); + } + xp->new_serial = new_serial; + xp->id = id; + memmove(&xp->file_pos, startpos, sizeof(off_t)); + return 1; +} + +static int +read_process_part(namedb_type* db, FILE *in, uint32_t type, + nsd_options_t* opt, struct diff_read_data* data, + struct diff_log** log, size_t child_count, off_t* startpos) +{ + uint32_t len, len2; + + /* read length */ + if(!diff_read_32(in, &len)) + return 1; + /* read content */ + if(type == DIFF_PART_IXFR) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "part IXFR len %d", len)); + if(!store_ixfr_data(in, len, data, startpos)) + return 0; + } + else if(type == DIFF_PART_SURE) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "part SURE len %d", len)); + if(!read_sure_part(db, in, opt, data, log, child_count)) + return 0; + } else { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "unknown part %x len %d", type, len)); + return 0; + } + /* read length */ + if(!diff_read_32(in, &len2)) + return 1; /* short read is OK */ + /* verify length */ + if(len != len2) + return 0; /* bad data is wrong */ + return 1; +} + +/* + * Finds smallest offset in data structs + * returns 0 if no offsets in the data structs. + */ +static int +find_smallest_offset(struct diff_read_data* data, off_t* offset) +{ + int found_any = 0; + struct diff_zone* dz; + struct diff_xfrpart* dx; + off_t mem_offset, mem_fpos; + + if(!data || !data->zones) + return 0; + RBTREE_FOR(dz, struct diff_zone*, data->zones) + { + if(!dz->parts) + continue; + RBTREE_FOR(dx, struct diff_xfrpart*, dz->parts) + { + memmove(&mem_fpos, &dx->file_pos, sizeof(off_t)); + + if(found_any) { + memmove(&mem_offset, offset, sizeof(off_t)); + + if(mem_fpos < mem_offset) + memmove(offset, &mem_fpos, sizeof(off_t)); + } else { + found_any = 1; + memmove(offset, &mem_fpos, sizeof(off_t)); + } + } + } + + return found_any; +} + +int +diff_read_file(namedb_type* db, nsd_options_t* opt, struct diff_log** log, + size_t child_count) +{ + const char* filename = opt->difffile; + FILE *df; + uint32_t type, timestamp[2], curr_timestamp[2]; + struct diff_read_data* data = diff_read_data_create(); + off_t startpos; + + df = fopen(filename, "r"); + if(!df) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "could not open file %s for reading: %s", + filename, strerror(errno))); + region_destroy(data->region); + return 1; + } + + /* check timestamp */ + curr_timestamp[0] = (uint32_t) db->diff_timestamp.tv_sec; + curr_timestamp[1] = (uint32_t) db->diff_timestamp.tv_usec; + + if(!diff_read_32(df, &type)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "difffile %s is empty", + filename)); + db->diff_skip = 0; + db->diff_pos = 0; + } + else if (!diff_read_32(df, ×tamp[0]) || + !diff_read_32(df, ×tamp[1])) { + log_msg(LOG_ERR, "difffile %s bad first part: no timestamp", + filename); + region_destroy(data->region); + return 0; + } + else if (curr_timestamp[0] != timestamp[0] || + curr_timestamp[1] != timestamp[1]) { + /* new timestamp, no skipping */ + db->diff_timestamp.tv_sec = (time_t) timestamp[0]; + db->diff_timestamp.tv_usec = (suseconds_t) timestamp[1]; + + if (db->diff_skip) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "new timestamp on " + "difffile %s, restoring diff_skip and diff_pos " + "[old timestamp: %u.%u; new timestamp: %u.%u]", + filename, curr_timestamp[0], curr_timestamp[1], + timestamp[0], timestamp[1])); + db->diff_skip = 0; + db->diff_pos = 0; + } + } + + /* Always seek, to diff_pos or to beginning of the file. */ + if (fseeko(df, 0, SEEK_SET)==-1) { + log_msg(LOG_INFO, "could not fseeko file %s: %s.", filename, + strerror(errno)); + region_destroy(data->region); + return 0; + } + if(db->diff_skip) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "skip diff file")); + if(fseeko(df, db->diff_pos, SEEK_SET)==-1) { + log_msg(LOG_INFO, "could not fseeko file %s: %s. " + "Reread from start.", filename, + strerror(errno)); + } + } + + startpos = ftello(df); + if(startpos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno)); + region_destroy(data->region); + return 0; + } + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "start of diff file read at pos %u", + (uint32_t) db->diff_pos)); + while(diff_read_32(df, &type)) + { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "iter loop")); + + /* read timestamp */ + if(!diff_read_32(df, ×tamp[0]) || + !diff_read_32(df, ×tamp[1])) { + log_msg(LOG_INFO, "could not read timestamp: %s.", + strerror(errno)); + region_destroy(data->region); + return 0; + } + + if(!read_process_part(db, df, type, opt, data, log, + child_count, &startpos)) + { + log_msg(LOG_INFO, "error processing diff file"); + region_destroy(data->region); + return 0; + } + startpos = ftello(df); + if(startpos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno)); + region_destroy(data->region); + return 0; + } + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "end of diff file read")); + + if(find_smallest_offset(data, &db->diff_pos)) { + /* can skip to the first unused element */ + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "next time skip diff file")); + db->diff_skip = 1; + } else { + /* all processed, can skip to here next time */ + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "next time skip diff file")); + db->diff_skip = 1; + db->diff_pos = ftello(df); + if(db->diff_pos == -1) { + log_msg(LOG_INFO, "could not ftello: %s.", + strerror(errno)); + db->diff_skip = 0; + } + } + + region_destroy(data->region); + fclose(df); + return 1; +} + +static int diff_broken(FILE *df, off_t* break_pos) +{ + uint32_t type, len, len2; + *break_pos = ftello(df); + + /* try to read and validate parts of the file */ + while(diff_read_32(df, &type)) /* cannot read type is no error, normal EOF */ + { + /* check type */ + if(type != DIFF_PART_IXFR && type != DIFF_PART_SURE) + return 1; + /* check length */ + if(!diff_read_32(df, &len)) + return 1; /* EOF inside the part is error */ + if(fseeko(df, len, SEEK_CUR) == -1) + { + log_msg(LOG_INFO, "fseeko failed: %s", strerror(errno)); + return 1; + } + /* fseek clears EOF flag, but try reading length value, + if EOF, the part is truncated */ + if(!diff_read_32(df, &len2)) + return 1; + if(len != len2) + return 1; /* bad part, lengths must agree */ + /* this part is ok */ + *break_pos = ftello(df); + } + return 0; +} + +void diff_snip_garbage(namedb_type* db, nsd_options_t* opt) +{ + off_t break_pos; + const char* filename = opt->difffile; + FILE *df; + + /* open file here and keep open, so it cannot change under our nose */ + df = fopen(filename, "r+"); + if(!df) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "could not open file %s for garbage collecting: %s", + filename, strerror(errno))); + return; + } + /* and skip into file, since nsd does not read anything before the pos */ + if(db->diff_skip) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "garbage collect skip diff file")); + if(fseeko(df, db->diff_pos, SEEK_SET)==-1) { + log_msg(LOG_INFO, "could not fseeko file %s: %s.", + filename, strerror(errno)); + fclose(df); + return; + } + } + + /* detect break point */ + if(diff_broken(df, &break_pos)) + { + /* snip off at break_pos */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "snipping off trailing partial part of %s", + filename)); + if(ftruncate(fileno(df), break_pos) == -1) + log_msg(LOG_ERR, "ftruncate %s failed: %s", + filename, strerror(errno)); + } + + fclose(df); +} diff --git a/usr.sbin/nsd/difffile.h b/usr.sbin/nsd/difffile.h new file mode 100644 index 00000000000..2f5f6cdf528 --- /dev/null +++ b/usr.sbin/nsd/difffile.h @@ -0,0 +1,70 @@ +/* + * difffile.h - nsd.diff file handling header file. Read/write diff files. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ +#ifndef DIFFFILE_H +#define DIFFFILE_H + +#include <config.h> +#include "rbtree.h" +#include "namedb.h" +#include "options.h" + +#define DIFF_PART_IXFR ('I'<<24 | 'X'<<16 | 'F'<<8 | 'R') +#define DIFF_PART_SURE ('S'<<24 | 'U'<<16 | 'R'<<8 | 'E') + +/* + * Used to pass commit logs + */ +struct diff_log { + char* zone_name; + char* error; + char* comment; + struct diff_log* next; +}; + +/* write an xfr packet data to the diff file, type=IXFR. + The diff file is created if necessary. */ +void diff_write_packet(const char* zone, uint32_t new_serial, uint16_t id, + uint32_t seq_nr, uint8_t* data, size_t len, nsd_options_t* opt); + +/* + * Write a commit packet to the diff file, type=SURE. + * The zone data (preceding ixfr packets) are committed. + * See NSD-DIFFFILE for meaning of the arguments. + */ +void diff_write_commit(const char* zone, uint32_t old_serial, + uint32_t new_serial, uint16_t id, uint32_t num_parts, + uint8_t commit, const char* log_msg, + nsd_options_t* opt); + +/* check if the crc in the nsd.db is the same in memory as on disk. + returns 1 if different. 0 if the same. returns -1 on error. */ +int db_crc_different(namedb_type* db); + +/* read the diff file and apply to the database in memory. + It will attempt to skip bad data. + If you pass a non-null value log, log comments are alloced in namedb.region + then, *log must be 0 on start of call (entries are prepended). + returns 0 on an unrecoverable error. */ +int diff_read_file(namedb_type* db, nsd_options_t* opt, struct diff_log** log, + size_t child_count); + +/* check the diff file for garbage at the end (bad type, partial write) + * and snip it off. + */ +void diff_snip_garbage(namedb_type* db, nsd_options_t* opt); + +/* + * These functions read parts of the diff file. + */ +int diff_read_32(FILE *in, uint32_t* result); +int diff_read_16(FILE *in, uint16_t* result); +int diff_read_8(FILE *in, uint8_t* result); +int diff_read_str(FILE* in, char* buf, size_t len); + +#endif /* DIFFFILE_H */ diff --git a/usr.sbin/nsd/dname.c b/usr.sbin/nsd/dname.c new file mode 100644 index 00000000000..d264d202f7e --- /dev/null +++ b/usr.sbin/nsd/dname.c @@ -0,0 +1,497 @@ +/* + * dname.c -- Domain name handling. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + + +#include <config.h> + +#include <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +#include "dns.h" +#include "dname.h" +#include "query.h" + +const dname_type * +dname_make(region_type *region, const uint8_t *name, int normalize) +{ + size_t name_size = 0; + uint8_t label_offsets[MAXDOMAINLEN]; + uint8_t label_count = 0; + const uint8_t *label = name; + dname_type *result; + ssize_t i; + + assert(name); + + while (1) { + if (label_is_pointer(label)) + return NULL; + + label_offsets[label_count] = (uint8_t) (label - name); + ++label_count; + name_size += label_length(label) + 1; + + if (label_is_root(label)) + break; + + label = label_next(label); + } + + if (name_size > MAXDOMAINLEN) + return NULL; + + assert(label_count <= MAXDOMAINLEN / 2 + 1); + + /* Reverse label offsets. */ + for (i = 0; i < label_count / 2; ++i) { + uint8_t tmp = label_offsets[i]; + label_offsets[i] = label_offsets[label_count - i - 1]; + label_offsets[label_count - i - 1] = tmp; + } + + result = (dname_type *) region_alloc( + region, + (sizeof(dname_type) + + (label_count + name_size) * sizeof(uint8_t))); + result->name_size = name_size; + result->label_count = label_count; + memcpy((uint8_t *) dname_label_offsets(result), + label_offsets, + label_count * sizeof(uint8_t)); + if (normalize) { + uint8_t *dst = (uint8_t *) dname_name(result); + const uint8_t *src = name; + while (!label_is_root(src)) { + ssize_t len = label_length(src); + *dst++ = *src++; + for (i = 0; i < len; ++i) { + *dst++ = DNAME_NORMALIZE(*src++); + } + } + *dst = *src; + } else { + memcpy((uint8_t *) dname_name(result), + name, + name_size * sizeof(uint8_t)); + } + return result; +} + + +const dname_type * +dname_make_from_packet(region_type *region, buffer_type *packet, + int allow_pointers, int normalize) +{ + uint8_t buf[MAXDOMAINLEN + 1]; + if(!dname_make_wire_from_packet(buf, packet, allow_pointers)) + return 0; + return dname_make(region, buf, normalize); +} + +int +dname_make_wire_from_packet(uint8_t *buf, buffer_type *packet, + int allow_pointers) +{ + int done = 0; + uint8_t visited[(MAX_PACKET_SIZE+7)/8]; + size_t dname_length = 0; + const uint8_t *label; + ssize_t mark = -1; + + memset(visited, 0, (buffer_limit(packet)+7)/8); + + while (!done) { + if (!buffer_available(packet, 1)) { +/* error("dname out of bounds"); */ + return 0; + } + + if (get_bit(visited, buffer_position(packet))) { +/* error("dname loops"); */ + return 0; + } + set_bit(visited, buffer_position(packet)); + + label = buffer_current(packet); + if (label_is_pointer(label)) { + size_t pointer; + if (!allow_pointers) { + return 0; + } + if (!buffer_available(packet, 2)) { +/* error("dname pointer out of bounds"); */ + return 0; + } + pointer = label_pointer_location(label); + if (pointer >= buffer_limit(packet)) { +/* error("dname pointer points outside packet"); */ + return 0; + } + buffer_skip(packet, 2); + if (mark == -1) { + mark = buffer_position(packet); + } + buffer_set_position(packet, pointer); + } else if (label_is_normal(label)) { + size_t length = label_length(label) + 1; + done = label_is_root(label); + if (!buffer_available(packet, length)) { +/* error("dname label out of bounds"); */ + return 0; + } + if (dname_length + length >= MAXDOMAINLEN+1) { +/* error("dname too large"); */ + return 0; + } + buffer_read(packet, buf + dname_length, length); + dname_length += length; + } else { +/* error("bad label type"); */ + return 0; + } + } + + if (mark != -1) { + buffer_set_position(packet, mark); + } + + return dname_length; +} + +const dname_type * +dname_parse(region_type *region, const char *name) +{ + uint8_t dname[MAXDOMAINLEN]; + if(!dname_parse_wire(dname, name)) + return 0; + return dname_make(region, dname, 1); +} + +int dname_parse_wire(uint8_t* dname, const char* name) +{ + const uint8_t *s = (const uint8_t *) name; + uint8_t *h; + uint8_t *p; + uint8_t *d = dname; + size_t label_length; + + if (strcmp(name, ".") == 0) { + /* Root domain. */ + dname[0] = 0; + return 1; + } + + for (h = d, p = h + 1; *s; ++s, ++p) { + if (p - dname >= MAXDOMAINLEN) { + return 0; + } + + switch (*s) { + case '.': + if (p == h + 1) { + /* Empty label. */ + return 0; + } else { + label_length = p - h - 1; + if (label_length > MAXLABELLEN) { + return 0; + } + *h = label_length; + h = p; + } + break; + case '\\': + /* Handle escaped characters (RFC1035 5.1) */ + if (isdigit(s[1]) && isdigit(s[2]) && isdigit(s[3])) { + int val = (hexdigit_to_int(s[1]) * 100 + + hexdigit_to_int(s[2]) * 10 + + hexdigit_to_int(s[3])); + if (0 <= val && val <= 255) { + s += 3; + *p = val; + } else { + *p = *++s; + } + } else if (s[1] != '\0') { + *p = *++s; + } + break; + default: + *p = *s; + break; + } + } + + if (p != h + 1) { + /* Terminate last label. */ + label_length = p - h - 1; + if (label_length > MAXLABELLEN) { + return 0; + } + *h = label_length; + h = p; + } + + /* Add root label. */ + *h = 0; + + return p-dname; +} + + +const dname_type * +dname_copy(region_type *region, const dname_type *dname) +{ + return (dname_type *) region_alloc_init( + region, dname, dname_total_size(dname)); +} + + +const dname_type * +dname_partial_copy(region_type *region, const dname_type *dname, uint8_t label_count) +{ + if (!dname) + return NULL; + + if (label_count == 0) { + /* Always copy the root label. */ + label_count = 1; + } + + assert(label_count <= dname->label_count); + + return dname_make(region, dname_label(dname, label_count - 1), 0); +} + + +const dname_type * +dname_origin(region_type *region, const dname_type *dname) +{ + return dname_partial_copy(region, dname, dname->label_count - 1); +} + + +int +dname_is_subdomain(const dname_type *left, const dname_type *right) +{ + uint8_t i; + + if (left->label_count < right->label_count) + return 0; + + for (i = 1; i < right->label_count; ++i) { + if (label_compare(dname_label(left, i), + dname_label(right, i)) != 0) + return 0; + } + + return 1; +} + + +int +dname_compare(const dname_type *left, const dname_type *right) +{ + int result; + uint8_t label_count; + uint8_t i; + + assert(left); + assert(right); + + if (left == right) { + return 0; + } + + label_count = (left->label_count <= right->label_count + ? left->label_count + : right->label_count); + + /* Skip the root label by starting at label 1. */ + for (i = 1; i < label_count; ++i) { + result = label_compare(dname_label(left, i), + dname_label(right, i)); + if (result) { + return result; + } + } + + /* Dname with the fewest labels is "first". */ + return (int) left->label_count - (int) right->label_count; +} + + +int +label_compare(const uint8_t *left, const uint8_t *right) +{ + int left_length; + int right_length; + size_t size; + int result; + + assert(left); + assert(right); + + assert(label_is_normal(left)); + assert(label_is_normal(right)); + + left_length = label_length(left); + right_length = label_length(right); + size = left_length < right_length ? left_length : right_length; + + result = memcmp(label_data(left), label_data(right), size); + if (result) { + return result; + } else { + return (int) left_length - (int) right_length; + } +} + + +uint8_t +dname_label_match_count(const dname_type *left, const dname_type *right) +{ + uint8_t i; + + assert(left); + assert(right); + + for (i = 1; i < left->label_count && i < right->label_count; ++i) { + if (label_compare(dname_label(left, i), + dname_label(right, i)) != 0) + { + return i; + } + } + + return i; +} + +const char * +dname_to_string(const dname_type *dname, const dname_type *origin) +{ + static char buf[MAXDOMAINLEN * 5]; + size_t i; + size_t labels_to_convert = dname->label_count - 1; + int absolute = 1; + char *dst; + const uint8_t *src; + + if (dname->label_count == 1) { + strcpy(buf, "."); + return buf; + } + + if (origin && dname_is_subdomain(dname, origin)) { + int common_labels = dname_label_match_count(dname, origin); + labels_to_convert = dname->label_count - common_labels; + absolute = 0; + } + + dst = buf; + src = dname_name(dname); + for (i = 0; i < labels_to_convert; ++i) { + size_t len = label_length(src); + size_t j; + ++src; + for (j = 0; j < len; ++j) { + uint8_t ch = *src++; + if (isalnum(ch) || ch == '-' || ch == '_') { + *dst++ = ch; + } else if (ch == '.' || ch == '\\') { + *dst++ = '\\'; + *dst++ = ch; + } else { + snprintf(dst, 5, "\\%03u", (unsigned int)ch); + dst += 4; + } + } + *dst++ = '.'; + } + if (absolute) { + *dst = '\0'; + } else { + *--dst = '\0'; + } + return buf; +} + + +const dname_type * +dname_make_from_label(region_type *region, + const uint8_t *label, const size_t length) +{ + uint8_t temp[MAXLABELLEN + 2]; + + assert(length > 0 && length <= MAXLABELLEN); + + temp[0] = length; + memcpy(temp + 1, label, length * sizeof(uint8_t)); + temp[length + 1] = '\000'; + + return dname_make(region, temp, 1); +} + + +const dname_type * +dname_concatenate(region_type *region, + const dname_type *left, + const dname_type *right) +{ + uint8_t temp[MAXDOMAINLEN]; + + assert(left->name_size + right->name_size - 1 <= MAXDOMAINLEN); + + memcpy(temp, dname_name(left), left->name_size - 1); + memcpy(temp + left->name_size - 1, dname_name(right), right->name_size); + + return dname_make(region, temp, 0); +} + + +const dname_type * +dname_replace(region_type* region, + const dname_type* name, + const dname_type* src, + const dname_type* dest) +{ + /* nomenclature: name is said to be <x>.<src>. x can be null. */ + dname_type* res; + int x_labels = name->label_count - src->label_count; + int x_len = name->name_size - src->name_size; + int i; + assert(dname_is_subdomain(name, src)); + + /* check if final size is acceptable */ + if(x_len+dest->name_size > MAXDOMAINLEN) + return NULL; + + res = (dname_type*)region_alloc(region, sizeof(dname_type) + + (x_labels+dest->label_count + x_len+dest->name_size) + *sizeof(uint8_t)); + res->name_size = x_len+dest->name_size; + res->label_count = x_labels+dest->label_count; + for(i=0; i<dest->label_count; i++) + ((uint8_t*)dname_label_offsets(res))[i] = + dname_label_offsets(dest)[i] + x_len; + for(i=dest->label_count; i<res->label_count; i++) + ((uint8_t*)dname_label_offsets(res))[i] = + dname_label_offsets(name)[i - dest->label_count + + src->label_count]; + memcpy((uint8_t*)dname_name(res), dname_name(name), x_len); + memcpy((uint8_t*)dname_name(res)+x_len, dname_name(dest), dest->name_size); + assert(dname_is_subdomain(res, dest)); + return res; +} + diff --git a/usr.sbin/nsd/dname.h b/usr.sbin/nsd/dname.h new file mode 100644 index 00000000000..b68bc0dfe40 --- /dev/null +++ b/usr.sbin/nsd/dname.h @@ -0,0 +1,377 @@ +/* + * dname.h -- Domain name handling. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _DNAME_H_ +#define _DNAME_H_ + +#include <assert.h> +#include <stdio.h> + +#include "buffer.h" +#include "region-allocator.h" + +#if defined(NAMEDB_UPPERCASE) || defined(USE_NAMEDB_UPPERCASE) +#define DNAME_NORMALIZE toupper +#else +#define DNAME_NORMALIZE tolower +#endif + + +/* + * Domain names stored in memory add some additional information to be + * able to quickly index and compare by label. + */ +typedef struct dname dname_type; +struct dname +{ + /* + * The size (in bytes) of the domain name in wire format. + */ + uint8_t name_size; + + /* + * The number of labels in this domain name (including the + * root label). + */ + uint8_t label_count; + + /* + uint8_t label_offsets[label_count]; + uint8_t name[name_size]; + */ +}; + + +/* + * Construct a new domain name based on NAME in wire format. NAME + * cannot contain compression pointers. + * + * Pre: NAME != NULL. + */ +const dname_type *dname_make(region_type *region, const uint8_t *name, + int normalize); + +/* + * Construct a new domain name based on wire format dname stored at + * PACKET's current position. Compression pointers are followed. The + * PACKET's current position is changed to the end of the wire format + * dname or set to just after the first compression pointer. + */ +const dname_type *dname_make_from_packet(region_type *region, + buffer_type *packet, + int allow_pointers, + int normalize); + +/* + * parse wireformat from packet (following pointers) into the + * given buffer. Returns length in buffer or 0 on error. + * buffer must be MAXDOMAINLEN+1 long. + */ +int dname_make_wire_from_packet(uint8_t *buf, + buffer_type *packet, + int allow_pointers); + +/* + * Construct a new domain name based on the ASCII representation NAME. + * If ORIGIN is not NULL and NAME is not terminated by a "." the + * ORIGIN is appended to the result. NAME can contain escape + * sequences. + * + * Returns NULL on failure. Otherwise a newly allocated domain name + * is returned. + * + * Pre: name != NULL. + */ +const dname_type *dname_parse(region_type *region, const char *name); + +/* + * parse ascii string to wireformat domain name (without compression ptrs) + * returns 0 on failure, the length of the wireformat on success. + * the result is stored in the wirefmt which must be at least MAXDOMAINLEN + * in size. On failure, the wirefmt can be altered. + */ +int dname_parse_wire(uint8_t* wirefmt, const char* name); + +/* + * Return NULL if DNAME is NULL or a copy of DNAME otherwise. + */ +const dname_type *dname_copy(region_type *region, const dname_type *dname); + + +/* + * Copy the most significant LABEL_COUNT labels from dname. + */ +const dname_type *dname_partial_copy(region_type *region, + const dname_type *dname, + uint8_t label_count); + + +/* + * The origin of DNAME. + */ +const dname_type *dname_origin(region_type *region, const dname_type *dname); + +/* + * Return true if LEFT is a subdomain of RIGHT. + */ +int dname_is_subdomain(const dname_type *left, const dname_type *right); + + +/* + * Offsets into NAME for each label starting with the most + * significant label (the root label, followed by the TLD, + * etc). + */ +static inline const uint8_t * +dname_label_offsets(const dname_type *dname) +{ + return (const uint8_t *) ((const char *) dname + sizeof(dname_type)); +} + + +/* + * The actual name in wire format (a sequence of label, each + * prefixed by a length byte, terminated by a zero length + * label). + */ +static inline const uint8_t * +dname_name(const dname_type *dname) +{ + return (const uint8_t *) ((const char *) dname + + sizeof(dname_type) + + dname->label_count * sizeof(uint8_t)); +} + + +/* + * Return the label for DNAME specified by LABEL_INDEX. The first + * label (LABEL_INDEX == 0) is the root label, the next label is the + * TLD, etc. + * + * Pre: dname != NULL && label_index < dname->label_count. + */ +static inline const uint8_t * +dname_label(const dname_type *dname, uint8_t label) +{ + uint8_t label_index; + + assert(dname != NULL); + assert(label < dname->label_count); + + label_index = dname_label_offsets(dname)[label]; + assert(label_index < dname->name_size); + + return dname_name(dname) + label_index; +} + + +/* + * Compare two domain names. The comparison defines a lexographical + * ordering based on the domain name's labels, starting with the most + * significant label. + * + * Return < 0 if LEFT < RIGHT, 0 if LEFT == RIGHT, and > 0 if LEFT > + * RIGHT. The comparison is case sensitive. + * + * Pre: left != NULL && right != NULL + */ +int dname_compare(const dname_type *left, const dname_type *right); + + +/* + * Compare two labels. The comparison defines a lexographical + * ordering based on the characters in the labels. + * + * Return < 0 if LEFT < RIGHT, 0 if LEFT == RIGHT, and > 0 if LEFT > + * RIGHT. The comparison is case sensitive. + * + * Pre: left != NULL && right != NULL + * label_is_normal(left) && label_is_normal(right) + */ +int label_compare(const uint8_t *left, const uint8_t *right); + + +/* + * Returns the number of labels that match in LEFT and RIGHT, starting + * with the most significant label. Because the root label always + * matches, the result will always be >= 1. + * + * Pre: left != NULL && right != NULL + */ +uint8_t dname_label_match_count(const dname_type *left, + const dname_type *right); + + +/* + * The total size (in bytes) allocated to store DNAME. + * + * Pre: dname != NULL + */ +static inline size_t +dname_total_size(const dname_type *dname) +{ + return (sizeof(dname_type) + + ((dname->label_count + dname->name_size) + * sizeof(uint8_t))); +} + + +/* + * Is LABEL a normal LABEL (not a pointer or reserved)? + * + * Pre: label != NULL; + */ +static inline int +label_is_normal(const uint8_t *label) +{ + assert(label); + return (label[0] & 0xc0) == 0; +} + + +/* + * Is LABEL a pointer? + * + * Pre: label != NULL; + */ +static inline int +label_is_pointer(const uint8_t *label) +{ + assert(label); + return (label[0] & 0xc0) == 0xc0; +} + + +/* + * LABEL's pointer location. + * + * Pre: label != NULL && label_is_pointer(label) + */ +static inline uint16_t +label_pointer_location(const uint8_t *label) +{ + assert(label); + assert(label_is_pointer(label)); + return ((uint16_t) (label[0] & ~0xc0) << 8) | (uint16_t) label[1]; +} + + +/* + * Length of LABEL. + * + * Pre: label != NULL && label_is_normal(label) + */ +static inline uint8_t +label_length(const uint8_t *label) +{ + assert(label); + assert(label_is_normal(label)); + return label[0]; +} + + +/* + * The data of LABEL. + * + * Pre: label != NULL && label_is_normal(label) + */ +static inline const uint8_t * +label_data(const uint8_t *label) +{ + assert(label); + assert(label_is_normal(label)); + return label + 1; +} + + +/* + * Is LABEL the root label? + * + * Pre: label != NULL + */ +static inline int +label_is_root(const uint8_t *label) +{ + assert(label); + return label[0] == 0; +} + + +/* + * Is LABEL the wildcard label? + * + * Pre: label != NULL + */ +static inline int +label_is_wildcard(const uint8_t *label) +{ + assert(label); + return label[0] == 1 && label[1] == '*'; +} + + +/* + * The next label of LABEL. + * + * Pre: label != NULL + * label_is_normal(label) + * !label_is_root(label) + */ +static inline const uint8_t * +label_next(const uint8_t *label) +{ + assert(label); + assert(label_is_normal(label)); + assert(!label_is_root(label)); + return label + label_length(label) + 1; +} + + +/* + * Convert DNAME to its string representation. The result points to a + * static buffer that is overwritten the next time this function is + * invoked. + * + * If ORIGIN is provided and DNAME is a subdomain of ORIGIN the dname + * will be represented relative to ORIGIN. + * + * Pre: dname != NULL + */ +const char *dname_to_string(const dname_type *dname, + const dname_type *origin); + + +/* + * Create a dname containing the single label specified by STR + * followed by the root label. + */ +const dname_type *dname_make_from_label(region_type *region, + const uint8_t *label, + const size_t length); + + +/* + * Concatenate two dnames. + */ +const dname_type *dname_concatenate(region_type *region, + const dname_type *left, + const dname_type *right); + + +/* + * Perform DNAME substitution on a name, replace src with dest. + * Name must be a subdomain of src. The returned name is a subdomain of dest. + * Returns NULL if the result domain name is too long. + */ +const dname_type *dname_replace(region_type* region, + const dname_type* name, + const dname_type* src, + const dname_type* dest); + +#endif /* _DNAME_H_ */ diff --git a/usr.sbin/nsd/dns.c b/usr.sbin/nsd/dns.c new file mode 100644 index 00000000000..2299f66a8a6 --- /dev/null +++ b/usr.sbin/nsd/dns.c @@ -0,0 +1,561 @@ +/* + * dns.c -- DNS definitions. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <netdb.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#include "dns.h" +#include "zonec.h" +#include "zparser.h" + +/* Taken from RFC 1035, section 3.2.4. */ +static lookup_table_type dns_rrclasses[] = { + { CLASS_IN, "IN" }, /* the Internet */ + { CLASS_CS, "CS" }, /* the CSNET class (Obsolete) */ + { CLASS_CH, "CH" }, /* the CHAOS class */ + { CLASS_HS, "HS" }, /* Hesiod */ + { 0, NULL } +}; + +static rrtype_descriptor_type rrtype_descriptors[(RRTYPE_DESCRIPTORS_LENGTH+1)] = { + /* 0 */ + { 0, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 1 */ + { TYPE_A, "A", T_A, 1, 1, + { RDATA_WF_A }, { RDATA_ZF_A } }, + /* 2 */ + { TYPE_NS, "NS", T_NS, 1, 1, + { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 3 */ + { TYPE_MD, "MD", T_MD, 1, 1, + { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 4 */ + { TYPE_MF, "MF", T_MF, 1, 1, + { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 5 */ + { TYPE_CNAME, "CNAME", T_CNAME, 1, 1, + { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 6 */ + { TYPE_SOA, "SOA", T_SOA, 7, 7, + { RDATA_WF_COMPRESSED_DNAME, RDATA_WF_COMPRESSED_DNAME, RDATA_WF_LONG, + RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_LONG }, + { RDATA_ZF_DNAME, RDATA_ZF_DNAME, RDATA_ZF_PERIOD, RDATA_ZF_PERIOD, + RDATA_ZF_PERIOD, RDATA_ZF_PERIOD, RDATA_ZF_PERIOD } }, + /* 7 */ + { TYPE_MB, "MB", T_MB, 1, 1, + { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 8 */ + { TYPE_MG, "MG", T_MG, 1, 1, + { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 9 */ + { TYPE_MR, "MR", T_MR, 1, 1, + { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 10 */ + { TYPE_NULL, "NULL", T_UTYPE, 1, 1, + { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 11 */ + { TYPE_WKS, "WKS", T_WKS, 2, 2, + { RDATA_WF_A, RDATA_WF_BINARY }, + { RDATA_ZF_A, RDATA_ZF_SERVICES } }, + /* 12 */ + { TYPE_PTR, "PTR", T_PTR, 1, 1, + { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 13 */ + { TYPE_HINFO, "HINFO", T_HINFO, 2, 2, + { RDATA_WF_TEXT, RDATA_WF_TEXT }, { RDATA_ZF_TEXT, RDATA_ZF_TEXT } }, + /* 14 */ + { TYPE_MINFO, "MINFO", T_MINFO, 2, 2, + { RDATA_WF_COMPRESSED_DNAME, RDATA_WF_COMPRESSED_DNAME }, + { RDATA_ZF_DNAME, RDATA_ZF_DNAME } }, + /* 15 */ + { TYPE_MX, "MX", T_MX, 2, 2, + { RDATA_WF_SHORT, RDATA_WF_COMPRESSED_DNAME }, + { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + /* 16 */ + { TYPE_TXT, "TXT", T_TXT, 1, MAXRDATALEN, + { RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT }, + { RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT } }, + /* 17 */ + { TYPE_RP, "RP", T_RP, 2, 2, + { RDATA_WF_COMPRESSED_DNAME, RDATA_WF_COMPRESSED_DNAME }, + { RDATA_ZF_DNAME, RDATA_ZF_DNAME } }, + /* 18 */ + { TYPE_AFSDB, "AFSDB", T_AFSDB, 2, 2, + { RDATA_WF_SHORT, RDATA_WF_COMPRESSED_DNAME }, + { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + /* 19 */ + { TYPE_X25, "X25", T_X25, 1, 1, + { RDATA_WF_TEXT }, + { RDATA_ZF_TEXT } }, + /* 20 */ + { TYPE_ISDN, "ISDN", T_ISDN, 1, 2, + { RDATA_WF_TEXT, RDATA_WF_TEXT }, + { RDATA_ZF_TEXT, RDATA_ZF_TEXT } }, + /* 21 */ + { TYPE_RT, "RT", T_RT, 2, 2, + { RDATA_WF_SHORT, RDATA_WF_COMPRESSED_DNAME }, + { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + /* 22 */ + { TYPE_NSAP, "NSAP", T_NSAP, 1, 1, + { RDATA_WF_BINARY }, + { RDATA_ZF_NSAP } }, + /* 23 */ + { 23, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 24 */ + { TYPE_SIG, "SIG", T_SIG, 9, 9, + { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_LONG, + RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_SHORT, + RDATA_WF_UNCOMPRESSED_DNAME, RDATA_WF_BINARY }, + { RDATA_ZF_RRTYPE, RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_PERIOD, + RDATA_ZF_TIME, RDATA_ZF_TIME, RDATA_ZF_SHORT, RDATA_ZF_DNAME, + RDATA_ZF_BASE64 } }, + /* 25 */ + { TYPE_KEY, "KEY", T_KEY, 4, 4, + { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, + { RDATA_ZF_SHORT, RDATA_ZF_BYTE, RDATA_ZF_ALGORITHM, + RDATA_ZF_BASE64 } }, + /* 26 */ + { TYPE_PX, "PX", T_PX, 3, 3, + { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME, + RDATA_WF_UNCOMPRESSED_DNAME }, + { RDATA_ZF_SHORT, RDATA_ZF_DNAME, RDATA_ZF_DNAME } }, + /* 27 */ + { 27, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 28 */ + { TYPE_AAAA, "AAAA", T_AAAA, 1, 1, + { RDATA_WF_AAAA }, + { RDATA_ZF_AAAA } }, + /* 29 */ + { TYPE_LOC, "LOC", T_LOC, 1, 1, + { RDATA_WF_BINARY }, + { RDATA_ZF_LOC } }, + /* 30 */ + { TYPE_NXT, "NXT", T_NXT, 2, 2, + { RDATA_WF_UNCOMPRESSED_DNAME, RDATA_WF_BINARY }, + { RDATA_ZF_DNAME, RDATA_ZF_NXT } }, + /* 31 */ + { 31, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 32 */ + { 32, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 33 */ + { TYPE_SRV, "SRV", T_SRV, 4, 4, + { RDATA_WF_SHORT, RDATA_WF_SHORT, RDATA_WF_SHORT, + RDATA_WF_UNCOMPRESSED_DNAME }, + { RDATA_ZF_SHORT, RDATA_ZF_SHORT, RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + /* 34 */ + { 34, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 35 */ + { TYPE_NAPTR, "NAPTR", T_NAPTR, 6, 6, + { RDATA_WF_SHORT, RDATA_WF_SHORT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_UNCOMPRESSED_DNAME }, + { RDATA_ZF_SHORT, RDATA_ZF_SHORT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_DNAME } }, + /* 36 */ + { TYPE_KX, "KX", T_KX, 2, 2, + { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME }, + { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + /* 37 */ + { TYPE_CERT, "CERT", T_CERT, 4, 4, + { RDATA_WF_SHORT, RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BINARY }, + { RDATA_ZF_CERTIFICATE_TYPE, RDATA_ZF_SHORT, RDATA_ZF_ALGORITHM, + RDATA_ZF_BASE64 } }, + /* 38 */ + { TYPE_A6, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 39 */ + { TYPE_DNAME, "DNAME", T_DNAME, 1, 1, + { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + /* 40 */ + { 40, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 41 */ + { TYPE_OPT, "OPT", T_UTYPE, 1, 1, + { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 42 */ + { TYPE_APL, "APL", T_APL, 0, MAXRDATALEN, + { RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, + RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL }, + { RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, + RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL } }, + /* 43 */ + { TYPE_DS, "DS", T_DS, 4, 4, + { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, + { RDATA_ZF_SHORT, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_HEX } }, + /* 44 */ + { TYPE_SSHFP, "SSHFP", T_SSHFP, 3, 3, + { RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, + { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_HEX } }, + /* 45 */ + { TYPE_IPSECKEY, "IPSECKEY", T_IPSECKEY, 4, 5, + { RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_IPSECGATEWAY, + RDATA_WF_BINARY }, + { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_IPSECGATEWAY, + RDATA_ZF_BASE64 } }, + /* 46 */ + { TYPE_RRSIG, "RRSIG", T_RRSIG, 9, 9, + { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_LONG, + RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_SHORT, + RDATA_WF_LITERAL_DNAME, RDATA_WF_BINARY }, + { RDATA_ZF_RRTYPE, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_PERIOD, + RDATA_ZF_TIME, RDATA_ZF_TIME, RDATA_ZF_SHORT, + RDATA_ZF_LITERAL_DNAME, RDATA_ZF_BASE64 } }, + /* 47 */ + { TYPE_NSEC, "NSEC", T_NSEC, 2, 2, + { RDATA_WF_LITERAL_DNAME, RDATA_WF_BINARY }, + { RDATA_ZF_LITERAL_DNAME, RDATA_ZF_NSEC } }, + /* 48 */ + { TYPE_DNSKEY, "DNSKEY", T_DNSKEY, 4, 4, + { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, + { RDATA_ZF_SHORT, RDATA_ZF_BYTE, RDATA_ZF_ALGORITHM, + RDATA_ZF_BASE64 } }, + /* 49 */ + { TYPE_DHCID, "DHCID", T_DHCID, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_BASE64 } }, + /* 50 */ + { TYPE_NSEC3, "NSEC3", T_NSEC3, 6, 6, + { RDATA_WF_BYTE, /* hash type */ + RDATA_WF_BYTE, /* flags */ + RDATA_WF_SHORT, /* iterations */ + RDATA_WF_BINARYWITHLENGTH, /* salt */ + RDATA_WF_BINARYWITHLENGTH, /* next hashed name */ + RDATA_WF_BINARY /* type bitmap */ }, + { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_SHORT, RDATA_ZF_HEX_LEN, + RDATA_ZF_BASE32, RDATA_ZF_NSEC } }, + /* 51 */ + { TYPE_NSEC3PARAM, "NSEC3PARAM", T_NSEC3PARAM, 4, 4, + { RDATA_WF_BYTE, /* hash type */ + RDATA_WF_BYTE, /* flags */ + RDATA_WF_SHORT, /* iterations */ + RDATA_WF_BINARYWITHLENGTH /* salt */ }, + { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_SHORT, RDATA_ZF_HEX_LEN } }, + /* 52 */ + { 52, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 53 */ + { 53, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 54 */ + { 54, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 55 */ + { 55, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 56 */ + { 56, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 57 */ + { 57, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 58 */ + { 58, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 59 */ + { 59, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 60 */ + { 60, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 61 */ + { 61, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 62 */ + { 62, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 63 */ + { 63, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 64 */ + { 64, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 65 */ + { 65, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 66 */ + { 66, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 67 */ + { 67, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 68 */ + { 68, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 69 */ + { 69, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 70 */ + { 70, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 71 */ + { 71, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 72 */ + { 72, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 73 */ + { 73, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 74 */ + { 74, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 75 */ + { 75, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 76 */ + { 76, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 77 */ + { 77, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 78 */ + { 78, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 79 */ + { 79, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 80 */ + { 80, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 81 */ + { 81, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 82 */ + { 82, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 83 */ + { 83, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 84 */ + { 84, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 85 */ + { 85, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 86 */ + { 86, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 87 */ + { 87, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 88 */ + { 88, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 89 */ + { 89, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 90 */ + { 90, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 91 */ + { 91, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 92 */ + { 92, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 93 */ + { 93, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 94 */ + { 94, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 95 */ + { 95, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 96 */ + { 96, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 97 */ + { 97, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 98 */ + { 98, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + /* 99 */ + { TYPE_SPF, "SPF", T_SPF, 1, MAXRDATALEN, + { RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, + RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT }, + { RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, + RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT } }, + /* 32769 */ + { TYPE_DLV, "DLV", T_DLV, 4, 4, + { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, + { RDATA_ZF_SHORT, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_HEX } }, +}; + +rrtype_descriptor_type * +rrtype_descriptor_by_type(uint16_t type) +{ + if (type < RRTYPE_DESCRIPTORS_LENGTH) + return &rrtype_descriptors[type]; + else if (type == TYPE_DLV) + return &rrtype_descriptors[PSEUDO_TYPE_DLV]; + return &rrtype_descriptors[0]; +} + +rrtype_descriptor_type * +rrtype_descriptor_by_name(const char *name) +{ + int i; + + for (i = 0; i < RRTYPE_DESCRIPTORS_LENGTH; ++i) { + if (rrtype_descriptors[i].name + && strcasecmp(rrtype_descriptors[i].name, name) == 0) + { + return &rrtype_descriptors[i]; + } + } + + if (rrtype_descriptors[PSEUDO_TYPE_DLV].name + && strcasecmp(rrtype_descriptors[PSEUDO_TYPE_DLV].name, name) == 0) + { + return &rrtype_descriptors[PSEUDO_TYPE_DLV]; + } + + return NULL; +} + +const char * +rrtype_to_string(uint16_t rrtype) +{ + static char buf[20]; + rrtype_descriptor_type *descriptor = rrtype_descriptor_by_type(rrtype); + if (descriptor->name) { + return descriptor->name; + } else { + snprintf(buf, sizeof(buf), "TYPE%d", (int) rrtype); + return buf; + } +} + +/* + * Lookup the type in the ztypes lookup table. If not found, check if + * the type uses the "TYPExxx" notation for unknown types. + * + * Return 0 if no type matches. + */ +uint16_t +rrtype_from_string(const char *name) +{ + char *end; + long rrtype; + rrtype_descriptor_type *entry; + + entry = rrtype_descriptor_by_name(name); + if (entry) { + return entry->type; + } + + if (strlen(name) < 5) + return 0; + + if (strncasecmp(name, "TYPE", 4) != 0) + return 0; + + if (!isdigit((int)name[4])) + return 0; + + /* The rest from the string must be a number. */ + rrtype = strtol(name + 4, &end, 10); + if (*end != '\0') + return 0; + if (rrtype < 0 || rrtype > 65535L) + return 0; + + return (uint16_t) rrtype; +} + +const char * +rrclass_to_string(uint16_t rrclass) +{ + static char buf[20]; + lookup_table_type *entry = lookup_by_id(dns_rrclasses, rrclass); + if (entry) { + assert(strlen(entry->name) < sizeof(buf)); + strcpy(buf, entry->name); + } else { + snprintf(buf, sizeof(buf), "CLASS%d", (int) rrclass); + } + return buf; +} + +uint16_t +rrclass_from_string(const char *name) +{ + char *end; + long rrclass; + lookup_table_type *entry; + + entry = lookup_by_name(dns_rrclasses, name); + if (entry) { + return (uint16_t) entry->id; + } + + if (strlen(name) < 6) + return 0; + + if (strncasecmp(name, "CLASS", 5) != 0) + return 0; + + if (!isdigit((int)name[5])) + return 0; + + /* The rest from the string must be a number. */ + rrclass = strtol(name + 5, &end, 10); + if (*end != '\0') + return 0; + if (rrclass < 0 || rrclass > 65535L) + return 0; + + return (uint16_t) rrclass; +} diff --git a/usr.sbin/nsd/dns.h b/usr.sbin/nsd/dns.h new file mode 100644 index 00000000000..650d6fa8883 --- /dev/null +++ b/usr.sbin/nsd/dns.h @@ -0,0 +1,257 @@ +/* + * dns.h -- DNS definitions. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _DNS_H_ +#define _DNS_H_ + +enum rr_section { + QUESTION_SECTION, + ANSWER_SECTION, + AUTHORITY_SECTION, + ADDITIONAL_SECTION, + /* + * Use a split additional section to ensure A records appear + * before any AAAA records (this is recommended practice to + * avoid truncating the additional section for IPv4 clients + * that do not specify EDNS0), and AAAA records before other + * types of additional records (such as X25 and ISDN). + * Encode_answer sets the ARCOUNT field of the response packet + * correctly. + */ + ADDITIONAL_A_SECTION = ADDITIONAL_SECTION, + ADDITIONAL_AAAA_SECTION, + ADDITIONAL_OTHER_SECTION, + + RR_SECTION_COUNT +}; +typedef enum rr_section rr_section_type; + +/* Possible OPCODE values */ +#define OPCODE_QUERY 0 /* a standard query (QUERY) */ +#define OPCODE_IQUERY 1 /* an inverse query (IQUERY) */ +#define OPCODE_STATUS 2 /* a server status request (STATUS) */ +#define OPCODE_NOTIFY 4 /* NOTIFY */ +#define OPCODE_UPDATE 5 /* Dynamic update */ + +/* Possible RCODE values */ +#define RCODE_OK 0 /* No error condition */ +#define RCODE_FORMAT 1 /* Format error */ +#define RCODE_SERVFAIL 2 /* Server failure */ +#define RCODE_NXDOMAIN 3 /* Name Error */ +#define RCODE_IMPL 4 /* Not implemented */ +#define RCODE_REFUSE 5 /* Refused */ +#define RCODE_YXDOMAIN 6 /* name should not exist */ +#define RCODE_YXRRSET 7 /* rrset should not exist */ +#define RCODE_NXRRSET 8 /* rrset does not exist */ +#define RCODE_NOTAUTH 9 /* server not authoritative */ +#define RCODE_NOTZONE 10 /* name not inside zone */ + +/* Standardized NSD return code. Partially maps to DNS RCODE values. */ +enum nsd_rc +{ + /* Discard the client request. */ + NSD_RC_DISCARD = -1, + /* OK, continue normal processing. */ + NSD_RC_OK = RCODE_OK, + /* Return the appropriate error code to the client. */ + NSD_RC_FORMAT = RCODE_FORMAT, + NSD_RC_SERVFAIL = RCODE_SERVFAIL, + NSD_RC_NXDOMAIN = RCODE_NXDOMAIN, + NSD_RC_IMPL = RCODE_IMPL, + NSD_RC_REFUSE = RCODE_REFUSE, + NSD_RC_NOTAUTH = RCODE_NOTAUTH +}; +typedef enum nsd_rc nsd_rc_type; + +/* RFC1035 */ +#define CLASS_IN 1 /* Class IN */ +#define CLASS_CS 2 /* Class CS */ +#define CLASS_CH 3 /* Class CHAOS */ +#define CLASS_HS 4 /* Class HS */ +#define CLASS_NONE 254 /* Class NONE rfc2136 */ +#define CLASS_ANY 255 /* Class ANY */ + +#define TYPE_A 1 /* a host address */ +#define TYPE_NS 2 /* an authoritative name server */ +#define TYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define TYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define TYPE_CNAME 5 /* the canonical name for an alias */ +#define TYPE_SOA 6 /* marks the start of a zone of authority */ +#define TYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define TYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define TYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define TYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define TYPE_WKS 11 /* a well known service description */ +#define TYPE_PTR 12 /* a domain name pointer */ +#define TYPE_HINFO 13 /* host information */ +#define TYPE_MINFO 14 /* mailbox or mail list information */ +#define TYPE_MX 15 /* mail exchange */ +#define TYPE_TXT 16 /* text strings */ +#define TYPE_RP 17 /* RFC1183 */ +#define TYPE_AFSDB 18 /* RFC1183 */ +#define TYPE_X25 19 /* RFC1183 */ +#define TYPE_ISDN 20 /* RFC1183 */ +#define TYPE_RT 21 /* RFC1183 */ +#define TYPE_NSAP 22 /* RFC1706 */ + +#define TYPE_SIG 24 /* 2535typecode */ +#define TYPE_KEY 25 /* 2535typecode */ +#define TYPE_PX 26 /* RFC2163 */ + +#define TYPE_AAAA 28 /* ipv6 address */ +#define TYPE_LOC 29 /* LOC record RFC1876 */ +#define TYPE_NXT 30 /* 2535typecode */ + +#define TYPE_SRV 33 /* SRV record RFC2782 */ + +#define TYPE_NAPTR 35 /* RFC2915 */ +#define TYPE_KX 36 /* RFC2230 Key Exchange Delegation Record */ +#define TYPE_CERT 37 /* RFC2538 */ + +#define TYPE_A6 38 /* RFC2874 */ + +#define TYPE_DNAME 39 /* RFC2672 */ + +#define TYPE_OPT 41 /* Pseudo OPT record... */ +#define TYPE_APL 42 /* RFC3123 */ +#define TYPE_DS 43 /* RFC 4033, 4034, and 4035 */ +#define TYPE_SSHFP 44 /* SSH Key Fingerprint */ +#define TYPE_IPSECKEY 45 /* public key for ipsec use. RFC 4025 */ + +#define TYPE_RRSIG 46 /* RFC 4033, 4034, and 4035 */ +#define TYPE_NSEC 47 /* RFC 4033, 4034, and 4035 */ +#define TYPE_DNSKEY 48 /* RFC 4033, 4034, and 4035 */ +#define TYPE_DHCID 49 /* RFC4701 DHCP information */ +#define TYPE_NSEC3 50 /* NSEC3, secure denial, prevents zonewalking */ +#define TYPE_NSEC3PARAM 51 /* NSEC3PARAM at zone apex nsec3 parameters */ + +#define TYPE_SPF 99 /* RFC 4408 */ + +#define TYPE_TSIG 250 +#define TYPE_IXFR 251 +#define TYPE_AXFR 252 +#define TYPE_MAILB 253 /* A request for mailbox-related records (MB, MG or MR) */ +#define TYPE_MAILA 254 /* A request for mail agent RRs (Obsolete - see MX) */ +#define TYPE_ANY 255 /* any type (wildcard) */ + +#define TYPE_DLV 32769 /* RFC 4431 */ +#define PSEUDO_TYPE_DLV RRTYPE_DESCRIPTORS_LENGTH + +#define MAXLABELLEN 63 +#define MAXDOMAINLEN 255 + +#define MAXRDATALEN 64 /* This is more than enough, think multiple TXT. */ +#define MAX_RDLENGTH 65535 + +/* Maximum size of a single RR. */ +#define MAX_RR_SIZE \ + (MAXDOMAINLEN + sizeof(uint32_t) + 4*sizeof(uint16_t) + MAX_RDLENGTH) + +#define IP4ADDRLEN (32/8) +#define IP6ADDRLEN (128/8) + +/* + * The different types of RDATA wireformat data. + */ +enum rdata_wireformat +{ + RDATA_WF_COMPRESSED_DNAME, /* Possibly compressed domain name. */ + RDATA_WF_UNCOMPRESSED_DNAME, /* Uncompressed domain name. */ + RDATA_WF_LITERAL_DNAME, /* Literal (not downcased) dname. */ + RDATA_WF_BYTE, /* 8-bit integer. */ + RDATA_WF_SHORT, /* 16-bit integer. */ + RDATA_WF_LONG, /* 32-bit integer. */ + RDATA_WF_TEXT, /* Text string. */ + RDATA_WF_A, /* 32-bit IPv4 address. */ + RDATA_WF_AAAA, /* 128-bit IPv6 address. */ + RDATA_WF_BINARY, /* Binary data (unknown length). */ + RDATA_WF_BINARYWITHLENGTH, /* Binary data preceded by 1 byte length */ + RDATA_WF_APL, /* APL data. */ + RDATA_WF_IPSECGATEWAY /* IPSECKEY gateway ip4, ip6 or dname. */ +}; +typedef enum rdata_wireformat rdata_wireformat_type; + +/* + * The different types of RDATA that can appear in the zone file. + */ +enum rdata_zoneformat +{ + RDATA_ZF_DNAME, /* Domain name. */ + RDATA_ZF_LITERAL_DNAME, /* DNS name (not lowercased domain name). */ + RDATA_ZF_TEXT, /* Text string. */ + RDATA_ZF_BYTE, /* 8-bit integer. */ + RDATA_ZF_SHORT, /* 16-bit integer. */ + RDATA_ZF_LONG, /* 32-bit integer. */ + RDATA_ZF_A, /* 32-bit IPv4 address. */ + RDATA_ZF_AAAA, /* 128-bit IPv6 address. */ + RDATA_ZF_RRTYPE, /* RR type. */ + RDATA_ZF_ALGORITHM, /* Cryptographic algorithm. */ + RDATA_ZF_CERTIFICATE_TYPE, + RDATA_ZF_PERIOD, /* Time period. */ + RDATA_ZF_TIME, + RDATA_ZF_BASE64, /* Base-64 binary data. */ + RDATA_ZF_BASE32, /* Base-32 binary data. */ + RDATA_ZF_HEX, /* Hexadecimal binary data. */ + RDATA_ZF_HEX_LEN, /* Hexadecimal binary data. Skip initial length byte. */ + RDATA_ZF_NSAP, /* NSAP. */ + RDATA_ZF_APL, /* APL. */ + RDATA_ZF_IPSECGATEWAY, /* IPSECKEY gateway ip4, ip6 or dname. */ + RDATA_ZF_SERVICES, /* Protocol and port number bitmap. */ + RDATA_ZF_NXT, /* NXT type bitmap. */ + RDATA_ZF_NSEC, /* NSEC type bitmap. */ + RDATA_ZF_LOC, /* Location data. */ + RDATA_ZF_UNKNOWN /* Unknown data. */ +}; +typedef enum rdata_zoneformat rdata_zoneformat_type; + +struct rrtype_descriptor +{ + uint16_t type; /* RR type */ + const char *name; /* Textual name. */ + int token; /* Parser token. */ + uint8_t minimum; /* Minimum number of RDATAs. */ + uint8_t maximum; /* Maximum number of RDATAs. */ + uint8_t wireformat[MAXRDATALEN]; /* rdata_wireformat_type */ + uint8_t zoneformat[MAXRDATALEN]; /* rdata_zoneformat_type */ +}; +typedef struct rrtype_descriptor rrtype_descriptor_type; + +/* + * Indexed by type. The special type "0" can be used to get a + * descriptor for unknown types (with one binary rdata). + * + * spf + 1 + */ +#define RRTYPE_DESCRIPTORS_LENGTH (TYPE_SPF + 1) +rrtype_descriptor_type *rrtype_descriptor_by_name(const char *name); +rrtype_descriptor_type *rrtype_descriptor_by_type(uint16_t type); + +const char *rrtype_to_string(uint16_t rrtype); + +/* + * Lookup the type in the ztypes lookup table. If not found, check if + * the type uses the "TYPExxx" notation for unknown types. + * + * Return 0 if no type matches. + */ +uint16_t rrtype_from_string(const char *name); + +const char *rrclass_to_string(uint16_t rrclass); +uint16_t rrclass_from_string(const char *name); + +#ifdef __cplusplus +inline rr_section_type +operator++(rr_section_type &lhs) +{ + lhs = (rr_section_type) ((int) lhs + 1); + return lhs; +} +#endif /* __cplusplus */ + +#endif /* _DNS_H_ */ diff --git a/usr.sbin/nsd/edns.c b/usr.sbin/nsd/edns.c new file mode 100644 index 00000000000..ab682d940a0 --- /dev/null +++ b/usr.sbin/nsd/edns.c @@ -0,0 +1,116 @@ +/* + * edns.c -- EDNS definitions (RFC 2671). + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + + +#include <config.h> + +#include <string.h> + +#include "dns.h" +#include "edns.h" + +void +edns_init_data(edns_data_type *data, uint16_t max_length) +{ + memset(data, 0, sizeof(edns_data_type)); + /* record type: OPT */ + data->ok[1] = (TYPE_OPT & 0xff00) >> 8; /* type_hi */ + data->ok[2] = TYPE_OPT & 0x00ff; /* type_lo */ + /* udp payload size */ + data->ok[3] = (max_length & 0xff00) >> 8; /* size_hi */ + data->ok[4] = max_length & 0x00ff; /* size_lo */ + + data->error[1] = (TYPE_OPT & 0xff00) >> 8; /* type_hi */ + data->error[2] = TYPE_OPT & 0x00ff; /* type_lo */ + data->error[3] = (max_length & 0xff00) >> 8; /* size_hi */ + data->error[4] = max_length & 0x00ff; /* size_lo */ + data->error[5] = 1; /* XXX Extended RCODE=BAD VERS */ +} + +void +edns_init_nsid(edns_data_type *data, uint16_t nsid_len) +{ + /* add nsid length bytes */ + data->rdata_nsid[0] = ((OPT_HDR + nsid_len) & 0xff00) >> 8; /* length_hi */ + data->rdata_nsid[1] = ((OPT_HDR + nsid_len) & 0x00ff); /* length_lo */ + + /* NSID OPT HDR */ + data->nsid[0] = (NSID_CODE & 0xff00) >> 8; + data->nsid[1] = (NSID_CODE & 0x00ff); + data->nsid[2] = (nsid_len & 0xff00) >> 8; + data->nsid[3] = (nsid_len & 0x00ff); +} + +void +edns_init_record(edns_record_type *edns) +{ + edns->status = EDNS_NOT_PRESENT; + edns->position = 0; + edns->maxlen = 0; + edns->dnssec_ok = 0; + edns->nsid = 0; +} + +int +edns_parse_record(edns_record_type *edns, buffer_type *packet) +{ + /* OPT record type... */ + uint8_t opt_owner; + uint16_t opt_type; + uint16_t opt_class; + uint8_t opt_extended_rcode; + uint8_t opt_version; + uint16_t opt_flags; + uint16_t opt_rdlen; + uint16_t opt_nsid; + + edns->position = buffer_position(packet); + + if (!buffer_available(packet, (OPT_LEN + OPT_RDATA))) + return 0; + + opt_owner = buffer_read_u8(packet); + opt_type = buffer_read_u16(packet); + if (opt_owner != 0 || opt_type != TYPE_OPT) { + /* Not EDNS. */ + buffer_set_position(packet, edns->position); + return 0; + } + + opt_class = buffer_read_u16(packet); + opt_extended_rcode = buffer_read_u8(packet); + opt_version = buffer_read_u8(packet); + opt_flags = buffer_read_u16(packet); + opt_rdlen = buffer_read_u16(packet); + + if (opt_version != 0) { + edns->status = EDNS_ERROR; + return 1; + } + + if (opt_rdlen > 0) { + /* there is more to come, read opt code + * should be NSID - there are no others */ + opt_nsid = buffer_read_u16(packet); + edns->nsid = (opt_nsid == NSID_CODE); + /* extra check for the value */ + } + + edns->status = EDNS_OK; + edns->maxlen = opt_class; + edns->dnssec_ok = opt_flags & DNSSEC_OK_MASK; + return 1; +} + +size_t +edns_reserved_space(edns_record_type *edns) +{ + /* MIEK; when a pkt is too large?? */ + return edns->status == EDNS_NOT_PRESENT ? 0 : (OPT_LEN + OPT_RDATA); +} diff --git a/usr.sbin/nsd/edns.h b/usr.sbin/nsd/edns.h new file mode 100644 index 00000000000..79e26a20b4b --- /dev/null +++ b/usr.sbin/nsd/edns.h @@ -0,0 +1,61 @@ +/* + * edns.h -- EDNS definitions (RFC 2671). + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _EDNS_H_ +#define _EDNS_H_ + +#include "buffer.h" + +#define OPT_LEN 9U /* Length of the NSD EDNS response record minus 2 */ +#define OPT_RDATA 2 /* holds the rdata length comes after OPT_LEN */ +#define OPT_HDR 4U /* NSID opt header length */ +#define NSID_CODE 3 /* nsid option code */ +#define DNSSEC_OK_MASK 0x8000U /* do bit mask */ + +struct edns_data +{ + char ok[OPT_LEN]; + char error[OPT_LEN]; + char rdata_none[OPT_RDATA]; + char rdata_nsid[OPT_RDATA]; + char nsid[OPT_HDR]; +}; +typedef struct edns_data edns_data_type; + +enum edns_status +{ + EDNS_NOT_PRESENT, + EDNS_OK, + EDNS_ERROR +}; +typedef enum edns_status edns_status_type; + +struct edns_record +{ + edns_status_type status; + size_t position; + size_t maxlen; + int dnssec_ok; + int nsid; +}; +typedef struct edns_record edns_record_type; + +void edns_init_data(edns_data_type *data, uint16_t max_length); +void edns_init_record(edns_record_type *data); +int edns_parse_record(edns_record_type *data, buffer_type *packet); + +/* + * The amount of space to reserve in the response for the EDNS data + * (if required). + */ +size_t edns_reserved_space(edns_record_type *data); + +void edns_init_nsid(edns_data_type *data, uint16_t nsid_len); + +#endif /* _EDNS_H_ */ diff --git a/usr.sbin/nsd/install-sh b/usr.sbin/nsd/install-sh new file mode 100644 index 00000000000..e9de23842dc --- /dev/null +++ b/usr.sbin/nsd/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/usr.sbin/nsd/ipc.c b/usr.sbin/nsd/ipc.c new file mode 100644 index 00000000000..449c0514fc1 --- /dev/null +++ b/usr.sbin/nsd/ipc.c @@ -0,0 +1,839 @@ +/* + * ipc.c - Interprocess communication routines. Handlers read and write. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include "ipc.h" +#include "buffer.h" +#include "xfrd-tcp.h" +#include "nsd.h" +#include "namedb.h" +#include "xfrd.h" +#include "xfrd-notify.h" + +/* set is_ok for the zone according to the zone message */ +static zone_type* handle_xfrd_zone_state(struct nsd* nsd, buffer_type* packet); +/* write ipc ZONE_STATE message into the buffer */ +static void write_zone_state_packet(buffer_type* packet, zone_type* zone); +/* attempt to send NSD_STATS command to child fd */ +static void send_stat_to_child(struct main_ipc_handler_data* data, int fd); +/* write IPC expire notification msg to a buffer */ +static void xfrd_write_expire_notification(buffer_type* buffer, xfrd_zone_t* zone); +/* send reload request over the IPC channel */ +static void xfrd_send_reload_req(xfrd_state_t* xfrd); +/* send quit request over the IPC channel */ +static void xfrd_send_quit_req(xfrd_state_t* xfrd); +/* get SOA INFO out of IPC packet buffer */ +static void xfrd_handle_ipc_SOAINFO(xfrd_state_t* xfrd, buffer_type* packet); +/* perform read part of handle ipc for xfrd */ +static void xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd); + +static zone_type* +handle_xfrd_zone_state(struct nsd* nsd, buffer_type* packet) +{ + uint8_t ok; + const dname_type *dname; + domain_type *domain; + zone_type *zone; + + ok = buffer_read_u8(packet); + dname = (dname_type*)buffer_current(packet); + DEBUG(DEBUG_IPC,1, (LOG_INFO, "handler zone state %s is %s", + dname_to_string(dname, NULL), ok?"ok":"expired")); + /* find in zone_types, if does not exist, we cannot serve anyway */ + /* find zone in config, since that one always exists */ + domain = domain_table_find(nsd->db->domains, dname); + if(!domain) { + DEBUG(DEBUG_IPC,1, (LOG_INFO, "zone state msg, empty zone (domain %s)", + dname_to_string(dname, NULL))); + return NULL; + } + zone = domain_find_zone(domain); + if(!zone || dname_compare(domain_dname(zone->apex), dname) != 0) { + DEBUG(DEBUG_IPC,1, (LOG_INFO, "zone state msg, empty zone (zone %s)", + dname_to_string(dname, NULL))); + return NULL; + } + assert(zone); + /* only update zone->is_ok if needed to minimize copy-on-write + of memory pages shared after fork() */ + if(ok && !zone->is_ok) + zone->is_ok = 1; + if(!ok && zone->is_ok) + zone->is_ok = 0; + return zone; +} + +void +child_handle_parent_command(netio_type *ATTR_UNUSED(netio), + netio_handler_type *handler, + netio_event_types_type event_types) +{ + sig_atomic_t mode; + int len; + struct ipc_handler_conn_data *data = + (struct ipc_handler_conn_data *) handler->user_data; + if (!(event_types & NETIO_EVENT_READ)) { + return; + } + + if(data->conn->is_reading) { + int ret = conn_read(data->conn); + if(ret == -1) { + log_msg(LOG_ERR, "handle_parent_command: error in conn_read: %s", + strerror(errno)); + data->conn->is_reading = 0; + return; + } + if(ret == 0) { + return; /* continue later */ + } + /* completed */ + data->conn->is_reading = 0; + buffer_flip(data->conn->packet); + (void)handle_xfrd_zone_state(data->nsd, data->conn->packet); + return; + } + + if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) { + log_msg(LOG_ERR, "handle_parent_command: read: %s", + strerror(errno)); + return; + } + if (len == 0) + { + /* parent closed the connection. Quit */ + data->nsd->mode = NSD_QUIT; + return; + } + + switch (mode) { + case NSD_STATS: + case NSD_QUIT: + data->nsd->mode = mode; + break; + case NSD_ZONE_STATE: + data->conn->is_reading = 1; + data->conn->total_bytes = 0; + data->conn->msglen = 0; + data->conn->fd = handler->fd; + buffer_clear(data->conn->packet); + break; + default: + log_msg(LOG_ERR, "handle_parent_command: bad mode %d", + (int) mode); + break; + } +} + +void +parent_handle_xfrd_command(netio_type *ATTR_UNUSED(netio), + netio_handler_type *handler, + netio_event_types_type event_types) +{ + sig_atomic_t mode; + int len; + struct ipc_handler_conn_data *data = + (struct ipc_handler_conn_data *) handler->user_data; + if (!(event_types & NETIO_EVENT_READ)) { + return; + } + + if(data->conn->is_reading) { + /* handle ZONE_STATE forward to children */ + int ret = conn_read(data->conn); + size_t i; + zone_type* zone; + if(ret == -1) { + log_msg(LOG_ERR, "main xfrd listener: error in conn_read: %s", + strerror(errno)); + data->conn->is_reading = 0; + return; + } + if(ret == 0) { + return; /* continue later */ + } + /* completed */ + data->conn->is_reading = 0; + buffer_flip(data->conn->packet); + zone = handle_xfrd_zone_state(data->nsd, data->conn->packet); + if(!zone) + return; + /* forward to all children */ + for (i = 0; i < data->nsd->child_count; ++i) { + if(!zone->dirty[i]) { + zone->dirty[i] = 1; + stack_push(data->nsd->children[i].dirty_zones, zone); + data->nsd->children[i].handler->event_types |= + NETIO_EVENT_WRITE; + } + } + return; + } + + if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) { + log_msg(LOG_ERR, "handle_xfrd_command: read: %s", + strerror(errno)); + return; + } + if (len == 0) + { + DEBUG(DEBUG_IPC,1, (LOG_ERR, "handle_xfrd_command: xfrd closed channel.")); + close(handler->fd); + handler->fd = -1; + return; + } + + switch (mode) { + case NSD_RELOAD: + data->nsd->signal_hint_reload = 1; + break; + case NSD_QUIT: + data->nsd->mode = mode; + break; + case NSD_REAP_CHILDREN: + data->nsd->signal_hint_child = 1; + break; + case NSD_ZONE_STATE: + data->conn->is_reading = 1; + data->conn->total_bytes = 0; + data->conn->msglen = 0; + data->conn->fd = handler->fd; + buffer_clear(data->conn->packet); + break; + default: + log_msg(LOG_ERR, "handle_xfrd_command: bad mode %d", + (int) mode); + break; + } +} + +static void +write_zone_state_packet(buffer_type* packet, zone_type* zone) +{ + sig_atomic_t cmd = NSD_ZONE_STATE; + uint8_t ok = zone->is_ok; + uint16_t sz; + if(!zone->apex) { + return; + } + sz = dname_total_size(domain_dname(zone->apex)) + 1; + sz = htons(sz); + + buffer_clear(packet); + buffer_write(packet, &cmd, sizeof(cmd)); + buffer_write(packet, &sz, sizeof(sz)); + buffer_write(packet, &ok, sizeof(ok)); + buffer_write(packet, domain_dname(zone->apex), + dname_total_size(domain_dname(zone->apex))); + buffer_flip(packet); +} + +static void +send_stat_to_child(struct main_ipc_handler_data* data, int fd) +{ + sig_atomic_t cmd = NSD_STATS; + if(write(fd, &cmd, sizeof(cmd)) == -1) { + if(errno == EAGAIN || errno == EINTR) + return; /* try again later */ + log_msg(LOG_ERR, "svrmain: problems sending stats to child %d command: %s", + (int)data->child->pid, strerror(errno)); + return; + } + data->child->need_to_send_STATS = 0; +} + +int packet_read_query_section(buffer_type *packet, uint8_t* dest, uint16_t* qtype, uint16_t* qclass); +static void +debug_print_fwd_name(int ATTR_UNUSED(len), buffer_type* packet, int acl_num) +{ + uint8_t qnamebuf[MAXDOMAINLEN]; + uint16_t qtype, qclass; + const dname_type* dname; + region_type* tempregion = region_create(xalloc, free); + + size_t bufpos = buffer_position(packet); + buffer_rewind(packet); + buffer_skip(packet, 12); + if(packet_read_query_section(packet, qnamebuf, &qtype, &qclass)) { + dname = dname_make(tempregion, qnamebuf, 1); + log_msg(LOG_INFO, "main: fwd packet for %s, acl %d", + dname_to_string(dname,0), acl_num); + } else { + log_msg(LOG_INFO, "main: fwd packet badqname, acl %d", acl_num); + } + buffer_set_position(packet, bufpos); + region_destroy(tempregion); +} + +static void +send_quit_to_child(struct main_ipc_handler_data* data, int fd) +{ + sig_atomic_t cmd = NSD_QUIT; + if(write(fd, &cmd, sizeof(cmd)) == -1) { + if(errno == EAGAIN || errno == EINTR) + return; /* try again later */ + log_msg(LOG_ERR, "svrmain: problems sending quit to child %d command: %s", + (int)data->child->pid, strerror(errno)); + return; + } + data->child->need_to_send_QUIT = 0; + DEBUG(DEBUG_IPC,2, (LOG_INFO, "main: sent quit to child %d", + (int)data->child->pid)); +} + +void +parent_handle_child_command(netio_type *ATTR_UNUSED(netio), + netio_handler_type *handler, + netio_event_types_type event_types) +{ + sig_atomic_t mode; + int len; + struct main_ipc_handler_data *data = + (struct main_ipc_handler_data*)handler->user_data; + + /* do a nonblocking write to the child if it is ready. */ + if (event_types & NETIO_EVENT_WRITE) { + if(!data->busy_writing_zone_state && + !data->child->need_to_send_STATS && + !data->child->need_to_send_QUIT && + !data->child->need_to_exit && + data->child->dirty_zones->num > 0) { + /* create packet from next dirty zone */ + zone_type* zone = (zone_type*)stack_pop(data->child->dirty_zones); + assert(zone); + zone->dirty[data->child_num] = 0; + data->busy_writing_zone_state = 1; + write_zone_state_packet(data->write_conn->packet, zone); + data->write_conn->msglen = buffer_limit(data->write_conn->packet); + data->write_conn->total_bytes = sizeof(uint16_t); /* len bytes already in packet */ + data->write_conn->fd = handler->fd; + } + if(data->busy_writing_zone_state) { + /* write more of packet */ + int ret = conn_write(data->write_conn); + if(ret == -1) { + log_msg(LOG_ERR, "handle_child_cmd %d: could not write: %s", + (int)data->child->pid, strerror(errno)); + data->busy_writing_zone_state = 0; + } else if(ret == 1) { + data->busy_writing_zone_state = 0; /* completed */ + } + } else if(data->child->need_to_send_STATS && + !data->child->need_to_exit) { + send_stat_to_child(data, handler->fd); + } else if(data->child->need_to_send_QUIT) { + send_quit_to_child(data, handler->fd); + if(!data->child->need_to_send_QUIT) + handler->event_types = NETIO_EVENT_READ; + } + if(!data->busy_writing_zone_state && + !data->child->need_to_send_STATS && + !data->child->need_to_send_QUIT && + data->child->dirty_zones->num == 0) { + handler->event_types = NETIO_EVENT_READ; + } + } + + if (!(event_types & NETIO_EVENT_READ)) { + return; + } + + if (data->forward_mode) { + int got_acl; + /* forward the data to xfrd */ + DEBUG(DEBUG_IPC,2, (LOG_INFO, + "main passed packet readup %d", (int)data->got_bytes)); + if(data->got_bytes < sizeof(data->total_bytes)) + { + if ((len = read(handler->fd, + (char*)&data->total_bytes+data->got_bytes, + sizeof(data->total_bytes)-data->got_bytes)) == -1) { + log_msg(LOG_ERR, "handle_child_command: read: %s", + strerror(errno)); + return; + } + if(len == 0) { + /* EOF */ + data->forward_mode = 0; + return; + } + data->got_bytes += len; + if(data->got_bytes < sizeof(data->total_bytes)) + return; + data->total_bytes = ntohs(data->total_bytes); + buffer_clear(data->packet); + if(data->total_bytes > buffer_capacity(data->packet)) { + log_msg(LOG_ERR, "internal error: ipc too large"); + exit(1); + } + return; + } + /* read the packet */ + if(data->got_bytes-sizeof(data->total_bytes) < data->total_bytes) { + if((len = read(handler->fd, buffer_current(data->packet), + data->total_bytes - (data->got_bytes-sizeof(data->total_bytes)) + )) == -1 ) { + log_msg(LOG_ERR, "handle_child_command: read: %s", + strerror(errno)); + return; + } + if(len == 0) { + /* EOF */ + data->forward_mode = 0; + return; + } + data->got_bytes += len; + buffer_skip(data->packet, len); + /* read rest later */ + return; + } + /* read the acl number */ + got_acl = data->got_bytes - sizeof(data->total_bytes) - data->total_bytes; + if((len = read(handler->fd, (char*)&data->acl_num+got_acl, + sizeof(data->acl_num)-got_acl)) == -1 ) { + log_msg(LOG_ERR, "handle_child_command: read: %s", + strerror(errno)); + return; + } + if(len == 0) { + /* EOF */ + data->forward_mode = 0; + return; + } + got_acl += len; + data->got_bytes += len; + if(got_acl >= (int)sizeof(data->acl_num)) { + uint16_t len = htons(data->total_bytes); + DEBUG(DEBUG_IPC,2, (LOG_INFO, + "main fwd passed packet write %d", (int)data->got_bytes)); +#ifndef NDEBUG + if(nsd_debug_level >= 2) + debug_print_fwd_name(len, data->packet, data->acl_num); +#endif + data->forward_mode = 0; + mode = NSD_PASS_TO_XFRD; + if(!write_socket(*data->xfrd_sock, &mode, sizeof(mode)) || + !write_socket(*data->xfrd_sock, &len, sizeof(len)) || + !write_socket(*data->xfrd_sock, buffer_begin(data->packet), + data->total_bytes) || + !write_socket(*data->xfrd_sock, &data->acl_num, + sizeof(data->acl_num))) { + log_msg(LOG_ERR, "error in ipc fwd main2xfrd: %s", + strerror(errno)); + } + } + return; + } + + /* read command from ipc */ + if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) { + log_msg(LOG_ERR, "handle_child_command: read: %s", + strerror(errno)); + return; + } + if (len == 0) + { + size_t i; + if(handler->fd > 0) close(handler->fd); + for(i=0; i<data->nsd->child_count; ++i) + if(data->nsd->children[i].child_fd == handler->fd) { + data->nsd->children[i].child_fd = -1; + data->nsd->children[i].has_exited = 1; + DEBUG(DEBUG_IPC,1, (LOG_INFO, + "server %d closed cmd channel", + (int) data->nsd->children[i].pid)); + } + handler->fd = -1; + parent_check_all_children_exited(data->nsd); + return; + } + + switch (mode) { + case NSD_QUIT: + data->nsd->mode = mode; + break; + case NSD_STATS: + data->nsd->signal_hint_stats = 1; + break; + case NSD_REAP_CHILDREN: + data->nsd->signal_hint_child = 1; + break; + case NSD_PASS_TO_XFRD: + /* set mode for handle_child_command; echo to xfrd. */ + data->forward_mode = 1; + data->got_bytes = 0; + data->total_bytes = 0; + break; + default: + log_msg(LOG_ERR, "handle_child_command: bad mode %d", + (int) mode); + break; + } +} + +void +parent_check_all_children_exited(struct nsd* nsd) +{ + size_t i; + for(i=0; i < nsd->child_count; i++) { + if(!nsd->children[i].need_to_exit) + return; + if(!nsd->children[i].has_exited) + return; + } + nsd->mode = NSD_QUIT_SYNC; + DEBUG(DEBUG_IPC,2, (LOG_INFO, "main: all children exited. quit sync.")); +} + +void +parent_handle_reload_command(netio_type *ATTR_UNUSED(netio), + netio_handler_type *handler, + netio_event_types_type event_types) +{ + sig_atomic_t mode; + int len; + size_t i; + struct nsd *nsd = (struct nsd*) handler->user_data; + if (!(event_types & NETIO_EVENT_READ)) { + return; + } + /* read command from ipc */ + if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) { + log_msg(LOG_ERR, "handle_reload_command: read: %s", + strerror(errno)); + return; + } + if (len == 0) + { + if(handler->fd > 0) { + close(handler->fd); + handler->fd = -1; + } + log_msg(LOG_ERR, "handle_reload_cmd: reload closed cmd channel"); + return; + } + switch (mode) { + case NSD_QUIT_SYNC: + /* set all children to exit, only then notify xfrd. */ + /* so that buffered packets to pass to xfrd can arrive. */ + for(i=0; i < nsd->child_count; i++) { + nsd->children[i].need_to_exit = 1; + if(nsd->children[i].pid > 0 && + nsd->children[i].child_fd > 0) { + nsd->children[i].need_to_send_QUIT = 1; + nsd->children[i].handler->event_types + |= NETIO_EVENT_WRITE; + } else { + if(nsd->children[i].child_fd == -1) + nsd->children[i].has_exited = 1; + } + } + parent_check_all_children_exited(nsd); + break; + default: + log_msg(LOG_ERR, "handle_reload_command: bad mode %d", + (int) mode); + break; + } +} + +static void +xfrd_write_expire_notification(buffer_type* buffer, xfrd_zone_t* zone) +{ + sig_atomic_t cmd = NSD_ZONE_STATE; + uint8_t ok = 1; + uint16_t sz = dname_total_size(zone->apex) + 1; + sz = htons(sz); + if(zone->state == xfrd_zone_expired) + ok = 0; + + DEBUG(DEBUG_IPC,1, (LOG_INFO, + "xfrd encoding ipc zone state msg for zone %s state %d.", + zone->apex_str, (int)zone->state)); + + buffer_clear(buffer); + buffer_write(buffer, &cmd, sizeof(cmd)); + buffer_write(buffer, &sz, sizeof(sz)); + buffer_write(buffer, &ok, sizeof(ok)); + buffer_write(buffer, zone->apex, dname_total_size(zone->apex)); + buffer_flip(buffer); +} + +static void +xfrd_send_reload_req(xfrd_state_t* xfrd) +{ + sig_atomic_t req = NSD_RELOAD; + /* ask server_main for a reload */ + if(write(xfrd->ipc_handler.fd, &req, sizeof(req)) == -1) { + if(errno == EAGAIN || errno == EINTR) + return; /* try again later */ + log_msg(LOG_ERR, "xfrd: problems sending reload command: %s", + strerror(errno)); + return; + } + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: asked nsd to reload new updates")); + xfrd_prepare_zones_for_reload(); + xfrd->reload_cmd_last_sent = xfrd_time(); + xfrd->need_to_send_reload = 0; + xfrd->can_send_reload = 0; +} + +static void +xfrd_send_quit_req(xfrd_state_t* xfrd) +{ + sig_atomic_t cmd = NSD_QUIT; + xfrd->ipc_send_blocked = 1; + xfrd->ipc_handler.event_types &= (~NETIO_EVENT_WRITE); + xfrd->sending_zone_state = 0; + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc send ackreload(quit)")); + if(write_socket(xfrd->ipc_handler.fd, &cmd, sizeof(cmd)) == -1) { + log_msg(LOG_ERR, "xfrd: error writing ack to main: %s", + strerror(errno)); + } + xfrd->need_to_send_quit = 0; +} + +static void +xfrd_handle_ipc_SOAINFO(xfrd_state_t* xfrd, buffer_type* packet) +{ + xfrd_soa_t soa; + xfrd_soa_t* soa_ptr = &soa; + xfrd_zone_t* zone; + /* dname is sent in memory format */ + const dname_type* dname = (const dname_type*)buffer_begin(packet); + + /* find zone and decode SOA */ + zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname); + buffer_skip(packet, dname_total_size(dname)); + + if(!buffer_available(packet, 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(dname,0))); + soa_ptr = NULL; + } else { + /* read soa info */ + memset(&soa, 0, sizeof(soa)); + /* left out type, klass, count for speed */ + soa.type = htons(TYPE_SOA); + soa.klass = htons(CLASS_IN); + soa.ttl = htonl(buffer_read_u32(packet)); + soa.rdata_count = htons(7); + soa.prim_ns[0] = buffer_read_u8(packet); + if(!buffer_available(packet, soa.prim_ns[0])) + return; + buffer_read(packet, soa.prim_ns+1, soa.prim_ns[0]); + soa.email[0] = buffer_read_u8(packet); + if(!buffer_available(packet, soa.email[0])) + return; + buffer_read(packet, soa.email+1, soa.email[0]); + + soa.serial = htonl(buffer_read_u32(packet)); + soa.refresh = htonl(buffer_read_u32(packet)); + soa.retry = htonl(buffer_read_u32(packet)); + soa.expire = htonl(buffer_read_u32(packet)); + soa.minimum = htonl(buffer_read_u32(packet)); + DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s %u", + dname_to_string(dname,0), ntohl(soa.serial))); + } + + if(!zone) { + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: zone %s master zone updated", + dname_to_string(dname,0))); + notify_handle_master_zone_soainfo(xfrd->notify_zones, + dname, soa_ptr); + return; + } + xfrd_handle_incoming_soa(zone, soa_ptr, xfrd_time()); +} + +void +xfrd_handle_ipc(netio_type* ATTR_UNUSED(netio), + netio_handler_type *handler, + netio_event_types_type event_types) +{ + xfrd_state_t* xfrd = (xfrd_state_t*)handler->user_data; + if ((event_types & NETIO_EVENT_READ)) + { + /* first attempt to read as a signal from main + * could block further send operations */ + xfrd_handle_ipc_read(handler, xfrd); + } + if ((event_types & NETIO_EVENT_WRITE)) + { + if(xfrd->ipc_send_blocked) { /* wait for SOA_END */ + handler->event_types = NETIO_EVENT_READ; + return; + } + /* if necessary prepare a packet */ + if(!(xfrd->can_send_reload && xfrd->need_to_send_reload) && + !xfrd->need_to_send_quit && + !xfrd->sending_zone_state && + xfrd->dirty_zones->num > 0) { + xfrd_zone_t* zone = (xfrd_zone_t*)stack_pop(xfrd->dirty_zones); + assert(zone); + zone->dirty = 0; + xfrd->sending_zone_state = 1; + xfrd_write_expire_notification(xfrd->ipc_conn_write->packet, zone); + xfrd->ipc_conn_write->msglen = buffer_limit(xfrd->ipc_conn_write->packet); + /* skip length bytes; they are encoded in the packet, after cmd */ + xfrd->ipc_conn_write->total_bytes = sizeof(uint16_t); + } + /* write a bit */ + if(xfrd->sending_zone_state) { + /* call conn_write */ + int ret = conn_write(xfrd->ipc_conn_write); + if(ret == -1) { + log_msg(LOG_ERR, "xfrd: error in write ipc: %s", strerror(errno)); + xfrd->sending_zone_state = 0; + } + else if(ret == 1) { /* done */ + xfrd->sending_zone_state = 0; + } + } else if(xfrd->need_to_send_quit) { + xfrd_send_quit_req(xfrd); + } else if(xfrd->can_send_reload && xfrd->need_to_send_reload) { + xfrd_send_reload_req(xfrd); + } + if(!(xfrd->can_send_reload && xfrd->need_to_send_reload) && + !xfrd->need_to_send_quit && + !xfrd->sending_zone_state && + xfrd->dirty_zones->num == 0) { + handler->event_types = NETIO_EVENT_READ; /* disable writing for now */ + } + } + +} + +static void +xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd) +{ + sig_atomic_t cmd; + int len; + + if(xfrd->ipc_conn->is_reading==2) { + buffer_type* tmp = xfrd->ipc_pass; + uint32_t acl_num; + /* read acl_num */ + int ret = conn_read(xfrd->ipc_conn); + if(ret == -1) { + log_msg(LOG_ERR, "xfrd: error in read ipc: %s", strerror(errno)); + xfrd->ipc_conn->is_reading = 0; + return; + } + if(ret == 0) + return; + buffer_flip(xfrd->ipc_conn->packet); + xfrd->ipc_pass = xfrd->ipc_conn->packet; + xfrd->ipc_conn->packet = tmp; + xfrd->ipc_conn->is_reading = 0; + acl_num = buffer_read_u32(xfrd->ipc_pass); + xfrd_handle_passed_packet(xfrd->ipc_conn->packet, acl_num); + return; + } + if(xfrd->ipc_conn->is_reading) { + /* reading an IPC message */ + int ret = conn_read(xfrd->ipc_conn); + if(ret == -1) { + log_msg(LOG_ERR, "xfrd: error in read ipc: %s", strerror(errno)); + xfrd->ipc_conn->is_reading = 0; + return; + } + if(ret == 0) + return; + buffer_flip(xfrd->ipc_conn->packet); + if(xfrd->ipc_is_soa) { + xfrd->ipc_conn->is_reading = 0; + xfrd_handle_ipc_SOAINFO(xfrd, xfrd->ipc_conn->packet); + } else { + /* use ipc_conn to read remaining data as well */ + buffer_type* tmp = xfrd->ipc_pass; + xfrd->ipc_conn->is_reading=2; + xfrd->ipc_pass = xfrd->ipc_conn->packet; + xfrd->ipc_conn->packet = tmp; + xfrd->ipc_conn->total_bytes = sizeof(xfrd->ipc_conn->msglen); + xfrd->ipc_conn->msglen = sizeof(uint32_t); + buffer_clear(xfrd->ipc_conn->packet); + buffer_set_limit(xfrd->ipc_conn->packet, xfrd->ipc_conn->msglen); + } + return; + } + + if((len = read(handler->fd, &cmd, sizeof(cmd))) == -1) { + log_msg(LOG_ERR, "xfrd_handle_ipc: read: %s", + strerror(errno)); + return; + } + if(len == 0) + { + /* parent closed the connection. Quit */ + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: main closed connection.")); + xfrd->shutdown = 1; + return; + } + + switch(cmd) { + case NSD_QUIT: + case NSD_SHUTDOWN: + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: main send shutdown cmd.")); + xfrd->shutdown = 1; + break; + case NSD_SOA_BEGIN: + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_BEGIN")); + /* reload starts sending SOA INFOs; don't block */ + xfrd->parent_soa_info_pass = 1; + /* reset the nonblocking ipc write; + the new parent does not want half a packet */ + xfrd->sending_zone_state = 0; + break; + case NSD_SOA_INFO: + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_INFO")); + assert(xfrd->parent_soa_info_pass); + xfrd->ipc_is_soa = 1; + xfrd->ipc_conn->is_reading = 1; + break; + case NSD_SOA_END: + /* reload has finished */ + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_END")); + xfrd->can_send_reload = 1; + xfrd->parent_soa_info_pass = 0; + xfrd->ipc_send_blocked = 0; + handler->event_types |= NETIO_EVENT_WRITE; + xfrd_reopen_logfile(); + xfrd_check_failed_updates(); + xfrd_send_expy_all_zones(); + break; + case NSD_PASS_TO_XFRD: + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv PASS_TO_XFRD")); + xfrd->ipc_is_soa = 0; + xfrd->ipc_conn->is_reading = 1; + break; + case NSD_RELOAD: + /* main tells us that reload is done, stop ipc send to main */ + DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv RELOAD")); + handler->event_types |= NETIO_EVENT_WRITE; + xfrd->need_to_send_quit = 1; + break; + default: + log_msg(LOG_ERR, "xfrd_handle_ipc: bad mode %d (%d)", (int)cmd, + ntohl(cmd)); + break; + } + + if(xfrd->ipc_conn->is_reading) { + /* setup read of info */ + xfrd->ipc_conn->total_bytes = 0; + xfrd->ipc_conn->msglen = 0; + buffer_clear(xfrd->ipc_conn->packet); + } +} + diff --git a/usr.sbin/nsd/ipc.h b/usr.sbin/nsd/ipc.h new file mode 100644 index 00000000000..0f057388b2b --- /dev/null +++ b/usr.sbin/nsd/ipc.h @@ -0,0 +1,93 @@ +/* + * ipc.h - Interprocess communication routines. Handlers read and write. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef NSD_IPC_H +#define NSD_IPC_H + +#include <config.h> +#include "netio.h" +struct buffer; +struct nsd; +struct nsd_child; +struct xfrd_tcp; + +/* + * Data for the server_main IPC handler + * Used by parent side to listen to children, and write to children. + */ +struct main_ipc_handler_data +{ + struct nsd *nsd; + struct nsd_child *child; + int child_num; + + /* pointer to the socket, as it may change if it is restarted */ + int *xfrd_sock; + struct buffer *packet; + int forward_mode; + size_t got_bytes; + uint16_t total_bytes; + uint32_t acl_num; + + /* writing data, connection and state */ + uint8_t busy_writing_zone_state; + struct xfrd_tcp *write_conn; +}; + +/* + * Data for ipc handler, nsd and a conn for reading ipc msgs. + * Used by children to listen to parent. + * Used by parent to listen to xfrd. + */ +struct ipc_handler_conn_data +{ + struct nsd *nsd; + struct xfrd_tcp *conn; +}; + +/* + * Routine used by server_main. + * Handle a command received from the xfrdaemon processes. + */ +void parent_handle_xfrd_command(netio_type *netio, + netio_handler_type *handler, netio_event_types_type event_types); + +/* + * Routine used by server_main. + * Handle a command received from the reload process. + */ +void parent_handle_reload_command(netio_type *netio, + netio_handler_type *handler, netio_event_types_type event_types); + +/* + * Routine used by server_main. + * Handle a command received from the children processes. + * Send commands and forwarded xfrd packets when writable. + */ +void parent_handle_child_command(netio_type *netio, + netio_handler_type *handler, netio_event_types_type event_types); + +/* + * Routine used by server_child. + * Handle a command received from the parent process. + */ +void child_handle_parent_command(netio_type *netio, + netio_handler_type *handler, netio_event_types_type event_types); + +/* + * Routine used by xfrd + * Handle interprocess communication with parent process, read and write. + */ +void xfrd_handle_ipc(netio_type *netio, + netio_handler_type *handler, netio_event_types_type event_types); + +/* check if all children have exited in an orderly fashion and set mode */ +void parent_check_all_children_exited(struct nsd* nsd); + +#endif /* NSD_IPC_H */ diff --git a/usr.sbin/nsd/iterated_hash.c b/usr.sbin/nsd/iterated_hash.c new file mode 100644 index 00000000000..da29482087f --- /dev/null +++ b/usr.sbin/nsd/iterated_hash.c @@ -0,0 +1,43 @@ +/* + * iterated_hash.c -- nsec3 hash calculation. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + * With thanks to Ben Laurie. + */ +#include <config.h> +#ifdef NSEC3 +#include <openssl/sha.h> +#include <stdio.h> +#include <assert.h> + +#include "iterated_hash.h" + +int +iterated_hash(unsigned char out[SHA_DIGEST_LENGTH], + const unsigned char *salt, int saltlength, + const unsigned char *in, int inlength, int iterations) +{ +#if defined(NSEC3) && defined(HAVE_SSL) + SHA_CTX ctx; + int n; + assert(in && inlength > 0 && iterations >= 0); + for(n=0 ; n <= iterations ; ++n) + { + SHA1_Init(&ctx); + SHA1_Update(&ctx, in, inlength); + if(saltlength > 0) + SHA1_Update(&ctx, salt, saltlength); + SHA1_Final(out, &ctx); + in=out; + inlength=SHA_DIGEST_LENGTH; + } + return SHA_DIGEST_LENGTH; +#else + return 0; +#endif +} + +#endif /* NSEC3 */ diff --git a/usr.sbin/nsd/iterated_hash.h b/usr.sbin/nsd/iterated_hash.h new file mode 100644 index 00000000000..96ea89e8bf8 --- /dev/null +++ b/usr.sbin/nsd/iterated_hash.h @@ -0,0 +1,22 @@ +/* + * iterated_hash.h -- nsec3 hash calculation. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + * With thanks to Ben Laurie. + */ +#ifndef ITERATED_HASH_H +#define ITERATED_HASH_H + +#include <config.h> +#ifdef NSEC3 +#include <openssl/sha.h> + +int iterated_hash(unsigned char out[SHA_DIGEST_LENGTH], + const unsigned char *salt,int saltlength, + const unsigned char *in,int inlength,int iterations); + +#endif /* NSEC3 */ +#endif /* ITERATED_HASH_H */ diff --git a/usr.sbin/nsd/mkinstalldirs b/usr.sbin/nsd/mkinstalldirs new file mode 100644 index 00000000000..2c8850ef89b --- /dev/null +++ b/usr.sbin/nsd/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs,v 1.1 2010/01/15 19:24:49 jakob Exp $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/usr.sbin/nsd/namedb.c b/usr.sbin/nsd/namedb.c new file mode 100644 index 00000000000..82801fb545a --- /dev/null +++ b/usr.sbin/nsd/namedb.c @@ -0,0 +1,383 @@ +/* + * namedb.c -- common namedb operations. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +#include "namedb.h" + + +static domain_type * +allocate_domain_info(domain_table_type *table, + const dname_type *dname, + domain_type *parent) +{ + domain_type *result; + + assert(table); + assert(dname); + assert(parent); + + result = (domain_type *) region_alloc(table->region, + sizeof(domain_type)); + result->node.key = dname_partial_copy( + table->region, dname, domain_dname(parent)->label_count + 1); + result->parent = parent; + result->wildcard_child_closest_match = result; + result->rrsets = NULL; + result->number = 0; +#ifdef NSEC3 + result->nsec3_cover = NULL; + result->nsec3_wcard_child_cover = NULL; + result->nsec3_ds_parent_cover = NULL; + result->nsec3_lookup = NULL; + result->nsec3_is_exact = 0; + result->nsec3_ds_parent_is_exact = 0; +#endif + result->is_existing = 0; + result->is_apex = 0; + + return result; +} + +domain_table_type * +domain_table_create(region_type *region) +{ + const dname_type *origin; + domain_table_type *result; + domain_type *root; + + assert(region); + + origin = dname_make(region, (uint8_t *) "", 0); + + root = (domain_type *) region_alloc(region, sizeof(domain_type)); + root->node.key = origin; + root->parent = NULL; + root->wildcard_child_closest_match = root; + root->rrsets = NULL; + root->number = 1; /* 0 is used for after header */ + root->is_existing = 0; + root->is_apex = 0; +#ifdef NSEC3 + root->nsec3_is_exact = 0; + root->nsec3_ds_parent_is_exact = 0; + root->nsec3_cover = NULL; + root->nsec3_wcard_child_cover = NULL; + root->nsec3_ds_parent_cover = NULL; + root->nsec3_lookup = NULL; +#endif + + result = (domain_table_type *) region_alloc(region, + sizeof(domain_table_type)); + result->region = region; + result->names_to_domains = rbtree_create( + region, (int (*)(const void *, const void *)) dname_compare); + rbtree_insert(result->names_to_domains, (rbnode_t *) root); + + result->root = root; + + return result; +} + +int +domain_table_search(domain_table_type *table, + const dname_type *dname, + domain_type **closest_match, + domain_type **closest_encloser) +{ + int exact; + uint8_t label_match_count; + + assert(table); + assert(dname); + assert(closest_match); + assert(closest_encloser); + + exact = rbtree_find_less_equal(table->names_to_domains, dname, (rbnode_t **) closest_match); + assert(*closest_match); + + *closest_encloser = *closest_match; + + if (!exact) { + label_match_count = dname_label_match_count( + domain_dname(*closest_encloser), + dname); + assert(label_match_count < dname->label_count); + while (label_match_count < domain_dname(*closest_encloser)->label_count) { + (*closest_encloser) = (*closest_encloser)->parent; + assert(*closest_encloser); + } + } + + return exact; +} + +domain_type * +domain_table_find(domain_table_type *table, + const dname_type *dname) +{ + domain_type *closest_match; + domain_type *closest_encloser; + int exact; + + exact = domain_table_search( + table, dname, &closest_match, &closest_encloser); + return exact ? closest_encloser : NULL; +} + + +domain_type * +domain_table_insert(domain_table_type *table, + const dname_type *dname) +{ + domain_type *closest_match; + domain_type *closest_encloser; + domain_type *result; + int exact; + + assert(table); + assert(dname); + + exact = domain_table_search( + table, dname, &closest_match, &closest_encloser); + if (exact) { + result = closest_encloser; + } else { + assert(domain_dname(closest_encloser)->label_count < dname->label_count); + + /* Insert new node(s). */ + do { + result = allocate_domain_info(table, + dname, + closest_encloser); + rbtree_insert(table->names_to_domains, (rbnode_t *) result); + result->number = table->names_to_domains->count; + + /* + * If the newly added domain name is larger + * than the parent's current + * wildcard_child_closest_match but smaller or + * equal to the wildcard domain name, update + * the parent's wildcard_child_closest_match + * field. + */ + if (label_compare(dname_name(domain_dname(result)), + (const uint8_t *) "\001*") <= 0 + && dname_compare(domain_dname(result), + domain_dname(closest_encloser->wildcard_child_closest_match)) > 0) + { + closest_encloser->wildcard_child_closest_match + = result; + } + closest_encloser = result; + } while (domain_dname(closest_encloser)->label_count < dname->label_count); + } + + return result; +} + +int +domain_table_iterate(domain_table_type *table, + domain_table_iterator_type iterator, + void *user_data) +{ + const void *dname; + void *node; + int error = 0; + + assert(table); + + RBTREE_WALK(table->names_to_domains, dname, node) { + error += iterator((domain_type *) node, user_data); + } + + return error; +} + + +void +domain_add_rrset(domain_type *domain, rrset_type *rrset) +{ +#if 0 /* fast */ + rrset->next = domain->rrsets; + domain->rrsets = rrset; +#else + /* preserve ordering, add at end */ + rrset_type** p = &domain->rrsets; + while(*p) + p = &((*p)->next); + *p = rrset; + rrset->next = 0; +#endif + + while (domain && !domain->is_existing) { + domain->is_existing = 1; + domain = domain->parent; + } +} + + +rrset_type * +domain_find_rrset(domain_type *domain, zone_type *zone, uint16_t type) +{ + rrset_type *result = domain->rrsets; + + while (result) { + if (result->zone == zone && rrset_rrtype(result) == type) { + return result; + } + result = result->next; + } + return NULL; +} + +rrset_type * +domain_find_any_rrset(domain_type *domain, zone_type *zone) +{ + rrset_type *result = domain->rrsets; + + while (result) { + if (result->zone == zone) { + return result; + } + result = result->next; + } + return NULL; +} + +zone_type * +domain_find_zone(domain_type *domain) +{ + rrset_type *rrset; + while (domain) { + for (rrset = domain->rrsets; rrset; rrset = rrset->next) { + if (rrset_rrtype(rrset) == TYPE_SOA) { + return rrset->zone; + } + } + domain = domain->parent; + } + return NULL; +} + +zone_type * +domain_find_parent_zone(zone_type *zone) +{ + rrset_type *rrset; + + assert(zone); + + for (rrset = zone->apex->rrsets; rrset; rrset = rrset->next) { + if (rrset->zone != zone && rrset_rrtype(rrset) == TYPE_NS) { + return rrset->zone; + } + } + return NULL; +} + +domain_type * +domain_find_ns_rrsets(domain_type *domain, zone_type *zone, rrset_type **ns) +{ + while (domain && domain != zone->apex) { + *ns = domain_find_rrset(domain, zone, TYPE_NS); + if (*ns) + return domain; + domain = domain->parent; + } + + *ns = NULL; + return NULL; +} + +int +domain_is_glue(domain_type *domain, zone_type *zone) +{ + rrset_type *unused; + domain_type *ns_domain = domain_find_ns_rrsets(domain, zone, &unused); + return (ns_domain != NULL && + domain_find_rrset(ns_domain, zone, TYPE_SOA) == NULL); +} + +domain_type * +domain_wildcard_child(domain_type *domain) +{ + domain_type *wildcard_child; + + assert(domain); + assert(domain->wildcard_child_closest_match); + + wildcard_child = domain->wildcard_child_closest_match; + if (wildcard_child != domain + && label_is_wildcard(dname_name(domain_dname(wildcard_child)))) + { + return wildcard_child; + } else { + return NULL; + } +} + +int +zone_is_secure(zone_type *zone) +{ + assert(zone); + return zone->is_secure; +} + +uint16_t +rr_rrsig_type_covered(rr_type *rr) +{ + assert(rr->type == TYPE_RRSIG); + assert(rr->rdata_count > 0); + assert(rdata_atom_size(rr->rdatas[0]) == sizeof(uint16_t)); + + return ntohs(* (uint16_t *) rdata_atom_data(rr->rdatas[0])); +} + +zone_type * +namedb_find_zone(namedb_type *db, domain_type *domain) +{ + zone_type *zone; + + for (zone = db->zones; zone; zone = zone->next) { + if (zone->apex == domain) + break; + } + + return zone; +} + +rrset_type * +domain_find_non_cname_rrset(domain_type *domain, zone_type *zone) +{ + /* find any rrset type that is not allowed next to a CNAME */ + /* nothing is allowed next to a CNAME, except RRSIG, NSEC, NSEC3 */ + rrset_type *result = domain->rrsets; + + while (result) { + if (result->zone == zone && /* here is the list of exceptions*/ + rrset_rrtype(result) != TYPE_CNAME && + rrset_rrtype(result) != TYPE_RRSIG && + rrset_rrtype(result) != TYPE_NXT && + rrset_rrtype(result) != TYPE_SIG && + rrset_rrtype(result) != TYPE_NSEC && + rrset_rrtype(result) != TYPE_NSEC3 ) { + return result; + } + result = result->next; + } + return NULL; +} diff --git a/usr.sbin/nsd/namedb.h b/usr.sbin/nsd/namedb.h new file mode 100644 index 00000000000..c22a69034c8 --- /dev/null +++ b/usr.sbin/nsd/namedb.h @@ -0,0 +1,330 @@ +/* + * namedb.h -- nsd(8) internal namespace database definitions + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _NAMEDB_H_ +#define _NAMEDB_H_ + +#include <stdio.h> + +#include "dname.h" +#include "dns.h" +#include "rbtree.h" +struct zone_options; +struct nsd_options; + +#define NAMEDB_MAGIC "NSDdbV07" +#define NAMEDB_MAGIC_SIZE 8 + +typedef union rdata_atom rdata_atom_type; +typedef struct rrset rrset_type; +typedef struct rr rr_type; + +/* + * A domain name table supporting fast insert and search operations. + */ +typedef struct domain_table domain_table_type; +typedef struct domain domain_type; +typedef struct zone zone_type; + +struct domain_table +{ + region_type *region; + rbtree_t *names_to_domains; + domain_type *root; +}; + +struct domain +{ + rbnode_t node; + domain_type *parent; + domain_type *wildcard_child_closest_match; + rrset_type *rrsets; +#ifdef NSEC3 + /* (if nsec3 chain complete) always the covering nsec3 record */ + domain_type *nsec3_cover; + /* the nsec3 that covers the wildcard child of this domain. */ + domain_type *nsec3_wcard_child_cover; + /* for the DS case we must answer on the parent side of zone cut */ + domain_type *nsec3_ds_parent_cover; + /* the NSEC3 domain that has a hash-base32 <= than this dname. */ + /* or NULL (no smaller one within this zone) + * this variable is used to look up the NSEC3 record that matches + * or covers a given b64-encoded-hash-string domain name. + * The result of the lookup is stored in the *_cover variables. + * The variable makes it possible to perform a rbtree lookup for + * a name, then take this 'jump' to the previous element that contains + * an NSEC3 record, with hopefully the correct parameters. */ + domain_type *nsec3_lookup; +#endif + uint32_t number; /* Unique domain name number. */ + + /* + * This domain name exists (see wildcard clarification draft). + */ + unsigned is_existing : 1; + unsigned is_apex : 1; +#ifdef NSEC3 + /* if the domain has an NSEC3 for it, use cover ptr to get it. */ + unsigned nsec3_is_exact : 1; + /* same but on parent side */ + unsigned nsec3_ds_parent_is_exact : 1; +#endif +}; + +struct zone +{ + zone_type *next; + domain_type *apex; + rrset_type *soa_rrset; + rrset_type *soa_nx_rrset; /* see bug #103 */ + rrset_type *ns_rrset; +#ifdef NSEC3 + rr_type *nsec3_soa_rr; /* rrset with SOA bit set */ + domain_type *nsec3_last; /* last domain with nsec3, wraps */ +#endif + struct zone_options *opts; + uint32_t number; + uint8_t* dirty; /* array of dirty-flags, per child */ + unsigned is_secure : 1; /* zone uses DNSSEC */ + unsigned updated : 1; /* zone SOA was updated */ + unsigned is_ok : 1; /* zone has not expired. */ +}; + +/* a RR in DNS */ +struct rr { + domain_type *owner; + rdata_atom_type *rdatas; + uint32_t ttl; + uint16_t type; + uint16_t klass; + uint16_t rdata_count; +}; + +/* + * An RRset consists of at least one RR. All RRs are from the same + * zone. + */ +struct rrset +{ + rrset_type *next; + zone_type *zone; + rr_type *rrs; + uint16_t rr_count; +}; + +/* + * The field used is based on the wireformat the atom is stored in. + * The allowed wireformats are defined by the rdata_wireformat_type + * enumeration. + */ +union rdata_atom +{ + /* RDATA_WF_COMPRESSED_DNAME, RDATA_WF_UNCOMPRESSED_DNAME */ + domain_type *domain; + + /* Default. */ + uint16_t *data; +}; + +/* + * Create a new domain_table containing only the root domain. + */ +domain_table_type *domain_table_create(region_type *region); + +/* + * Search the domain table for a match and the closest encloser. + */ +int domain_table_search(domain_table_type *table, + const dname_type *dname, + domain_type **closest_match, + domain_type **closest_encloser); + +/* + * The number of domains stored in the table (minimum is one for the + * root domain). + */ +static inline uint32_t +domain_table_count(domain_table_type *table) +{ + return table->names_to_domains->count; +} + +/* + * Find the specified dname in the domain_table. NULL is returned if + * there is no exact match. + */ +domain_type *domain_table_find(domain_table_type *table, + const dname_type *dname); + +/* + * Insert a domain name in the domain table. If the domain name is + * not yet present in the table it is copied and a new dname_info node + * is created (as well as for the missing parent domain names, if + * any). Otherwise the domain_type that is already in the + * domain_table is returned. + */ +domain_type *domain_table_insert(domain_table_type *table, + const dname_type *dname); + + +/* + * Iterate over all the domain names in the domain tree. + */ +typedef int (*domain_table_iterator_type)(domain_type *node, + void *user_data); + +int domain_table_iterate(domain_table_type *table, + domain_table_iterator_type iterator, + void *user_data); + +/* + * Add an RRset to the specified domain. Updates the is_existing flag + * as required. + */ +void domain_add_rrset(domain_type *domain, rrset_type *rrset); + +rrset_type *domain_find_rrset(domain_type *domain, zone_type *zone, uint16_t type); +rrset_type *domain_find_any_rrset(domain_type *domain, zone_type *zone); + +zone_type *domain_find_zone(domain_type *domain); +zone_type *domain_find_parent_zone(zone_type *zone); + +domain_type *domain_find_ns_rrsets(domain_type *domain, zone_type *zone, rrset_type **ns); + +int domain_is_glue(domain_type *domain, zone_type *zone); + +rrset_type *domain_find_non_cname_rrset(domain_type *domain, zone_type *zone); + +domain_type *domain_wildcard_child(domain_type *domain); + +int zone_is_secure(zone_type *zone); + +static inline const dname_type * +domain_dname(domain_type *domain) +{ + return (const dname_type *) domain->node.key; +} + +static inline domain_type * +domain_previous(domain_type *domain) +{ + rbnode_t *prev = rbtree_previous((rbnode_t *) domain); + return prev == RBTREE_NULL ? NULL : (domain_type *) prev; +} + +static inline domain_type * +domain_next(domain_type *domain) +{ + rbnode_t *next = rbtree_next((rbnode_t *) domain); + return next == RBTREE_NULL ? NULL : (domain_type *) next; +} + +/* + * The type covered by the signature in the specified RRSIG RR. + */ +uint16_t rr_rrsig_type_covered(rr_type *rr); + +typedef struct namedb namedb_type; +struct namedb +{ + region_type *region; + domain_table_type *domains; + zone_type *zones; + size_t zone_count; + char *filename; + FILE *fd; + /* the timestamp on the ixfr.db file */ + struct timeval diff_timestamp; + /* the CRC on the nsd.db file and position of CRC in the db file */ + uint32_t crc; + off_t crc_pos; + /* if diff_skip=1, diff_pos contains the nsd.diff place to continue */ + uint8_t diff_skip; + off_t diff_pos; +}; + +static inline int rdata_atom_is_domain(uint16_t type, size_t index); + +static inline domain_type * +rdata_atom_domain(rdata_atom_type atom) +{ + return atom.domain; +} + +static inline uint16_t +rdata_atom_size(rdata_atom_type atom) +{ + return *atom.data; +} + +static inline uint8_t * +rdata_atom_data(rdata_atom_type atom) +{ + return (uint8_t *) (atom.data + 1); +} + + +/* + * Find the zone for the specified DOMAIN in DB. + */ +zone_type *namedb_find_zone(namedb_type *db, domain_type *domain); + +/* dbcreate.c */ +struct namedb *namedb_new(const char *filename); +int namedb_save(struct namedb *db); +void namedb_discard(struct namedb *db); + + +/* dbaccess.c */ +int namedb_lookup (struct namedb *db, + const dname_type *dname, + domain_type **closest_match, + domain_type **closest_encloser); +/* pass number of children (to alloc in dirty array */ +struct namedb *namedb_open(const char *filename, struct nsd_options* opt, + size_t num_children); +void namedb_close(struct namedb *db); + +static inline int +rdata_atom_is_domain(uint16_t type, size_t index) +{ + const rrtype_descriptor_type *descriptor + = rrtype_descriptor_by_type(type); + return (index < descriptor->maximum + && (descriptor->wireformat[index] == RDATA_WF_COMPRESSED_DNAME + || descriptor->wireformat[index] == RDATA_WF_UNCOMPRESSED_DNAME)); +} + +static inline rdata_wireformat_type +rdata_atom_wireformat_type(uint16_t type, size_t index) +{ + const rrtype_descriptor_type *descriptor + = rrtype_descriptor_by_type(type); + assert(index < descriptor->maximum); + return (rdata_wireformat_type) descriptor->wireformat[index]; +} + +static inline uint16_t +rrset_rrtype(rrset_type *rrset) +{ + assert(rrset); + assert(rrset->rr_count > 0); + return rrset->rrs[0].type; +} + +static inline uint16_t +rrset_rrclass(rrset_type *rrset) +{ + assert(rrset); + assert(rrset->rr_count > 0); + return rrset->rrs[0].klass; +} + + +#endif diff --git a/usr.sbin/nsd/netio.c b/usr.sbin/nsd/netio.c new file mode 100644 index 00000000000..664edfb3bd1 --- /dev/null +++ b/usr.sbin/nsd/netio.c @@ -0,0 +1,268 @@ +/* + * netio.c -- network I/O support. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ +#include <config.h> + +#include <assert.h> +#include <errno.h> +#include <sys/time.h> +#include <string.h> +#include <stdlib.h> + +#include "netio.h" +#include "util.h" + + +#ifndef HAVE_PSELECT +int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + const struct timespec *timeout, const sigset_t *sigmask); +#else +#include <sys/select.h> +#endif + + +struct netio_handler_list +{ + netio_handler_list_type *next; + netio_handler_type *handler; +}; + +netio_type * +netio_create(region_type *region) +{ + netio_type *result; + + assert(region); + + result = (netio_type *) region_alloc(region, sizeof(netio_type)); + result->region = region; + result->handlers = NULL; + result->deallocated = NULL; + result->dispatch_next = NULL; + return result; +} + +void +netio_add_handler(netio_type *netio, netio_handler_type *handler) +{ + netio_handler_list_type *elt; + + assert(netio); + assert(handler); + + if (netio->deallocated) { + /* + * If we have deallocated handler list elements, reuse + * the first one. + */ + elt = netio->deallocated; + netio->deallocated = elt->next; + } else { + /* + * Allocate a new one. + */ + elt = (netio_handler_list_type *) region_alloc( + netio->region, sizeof(netio_handler_list_type)); + } + + elt->next = netio->handlers; + elt->handler = handler; + netio->handlers = elt; +} + +void +netio_remove_handler(netio_type *netio, netio_handler_type *handler) +{ + netio_handler_list_type **elt_ptr; + + assert(netio); + assert(handler); + + for (elt_ptr = &netio->handlers; *elt_ptr; elt_ptr = &(*elt_ptr)->next) { + if ((*elt_ptr)->handler == handler) { + netio_handler_list_type *next = (*elt_ptr)->next; + if ((*elt_ptr) == netio->dispatch_next) + netio->dispatch_next = next; + (*elt_ptr)->handler = NULL; + (*elt_ptr)->next = netio->deallocated; + netio->deallocated = *elt_ptr; + *elt_ptr = next; + break; + } + } +} + +const struct timespec * +netio_current_time(netio_type *netio) +{ + assert(netio); + + if (!netio->have_current_time) { + struct timeval current_timeval; + if (gettimeofday(¤t_timeval, NULL) == -1) { + log_msg(LOG_CRIT, "gettimeofday: %s, aborting.", strerror(errno)); + abort(); + } + timeval_to_timespec(&netio->cached_current_time, ¤t_timeval); + netio->have_current_time = 1; + } + + return &netio->cached_current_time; +} + +int +netio_dispatch(netio_type *netio, const struct timespec *timeout, const sigset_t *sigmask) +{ + fd_set readfds, writefds, exceptfds; + int max_fd; + int have_timeout = 0; + struct timespec minimum_timeout; + netio_handler_type *timeout_handler = NULL; + netio_handler_list_type *elt; + int rc; + int result = 0; + + assert(netio); + + /* + * Clear the cached current time. + */ + netio->have_current_time = 0; + + /* + * Initialize the minimum timeout with the timeout parameter. + */ + if (timeout) { + have_timeout = 1; + memcpy(&minimum_timeout, timeout, sizeof(struct timespec)); + } + + /* + * Initialize the fd_sets and timeout based on the handler + * information. + */ + max_fd = -1; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + + for (elt = netio->handlers; elt; elt = elt->next) { + netio_handler_type *handler = elt->handler; + if (handler->fd >= 0 && handler->fd < (int)FD_SETSIZE) { + if (handler->fd > max_fd) { + max_fd = handler->fd; + } + if (handler->event_types & NETIO_EVENT_READ) { + FD_SET(handler->fd, &readfds); + } + if (handler->event_types & NETIO_EVENT_WRITE) { + FD_SET(handler->fd, &writefds); + } + if (handler->event_types & NETIO_EVENT_EXCEPT) { + FD_SET(handler->fd, &exceptfds); + } + } + if (handler->timeout && (handler->event_types & NETIO_EVENT_TIMEOUT)) { + struct timespec relative; + + relative.tv_sec = handler->timeout->tv_sec; + relative.tv_nsec = handler->timeout->tv_nsec; + timespec_subtract(&relative, netio_current_time(netio)); + + if (!have_timeout || + timespec_compare(&relative, &minimum_timeout) < 0) + { + have_timeout = 1; + minimum_timeout.tv_sec = relative.tv_sec; + minimum_timeout.tv_nsec = relative.tv_nsec; + timeout_handler = handler; + } + } + } + + if (have_timeout && minimum_timeout.tv_sec < 0) { + /* + * On negative timeout for a handler, immediatly + * dispatch the timeout event without checking for + * other events. + */ + if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) { + timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT); + } + return result; + } + + /* Check for events. */ + rc = pselect(max_fd + 1, &readfds, &writefds, &exceptfds, + have_timeout ? &minimum_timeout : NULL, + sigmask); + if (rc == -1) { + if(errno == EINVAL || errno == EACCES || errno == EBADF) { + log_msg(LOG_ERR, "fatal error pselect: %s.", + strerror(errno)); + exit(1); + } + return -1; + } + + /* + * Clear the cached current_time (pselect(2) may block for + * some time so the cached value is likely to be old). + */ + netio->have_current_time = 0; + + if (rc == 0) { + /* + * No events before the minimum timeout expired. + * Dispatch to handler if interested. + */ + if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) { + timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT); + } + } else { + /* + * Dispatch all the events to interested handlers + * based on the fd_sets. Note that a handler might + * deinstall itself, so store the next handler before + * calling the current handler! + */ + assert(netio->dispatch_next == NULL); + for (elt = netio->handlers; elt && rc; ) { + netio_handler_type *handler = elt->handler; + netio->dispatch_next = elt->next; + if (handler->fd >= 0 && handler->fd < (int)FD_SETSIZE) { + netio_event_types_type event_types + = NETIO_EVENT_NONE; + if (FD_ISSET(handler->fd, &readfds)) { + event_types |= NETIO_EVENT_READ; + FD_CLR(handler->fd, &readfds); + rc--; + } + if (FD_ISSET(handler->fd, &writefds)) { + event_types |= NETIO_EVENT_WRITE; + FD_CLR(handler->fd, &writefds); + rc--; + } + if (FD_ISSET(handler->fd, &exceptfds)) { + event_types |= NETIO_EVENT_EXCEPT; + FD_CLR(handler->fd, &exceptfds); + rc--; + } + + if (event_types & handler->event_types) { + handler->event_handler(netio, handler, event_types & handler->event_types); + ++result; + } + } + elt = netio->dispatch_next; + } + netio->dispatch_next = NULL; + } + + return result; +} diff --git a/usr.sbin/nsd/netio.h b/usr.sbin/nsd/netio.h new file mode 100644 index 00000000000..99d9c316aa3 --- /dev/null +++ b/usr.sbin/nsd/netio.h @@ -0,0 +1,182 @@ +/* + * netio.h -- network I/O support. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + * + * The netio module implements event based I/O handling using + * pselect(2). Multiple event handlers can wait for a certain event + * to occur simultaneously. Each event handler is called when an + * event occurs that the event handler has indicated that it is + * willing to handle. + * + * There are four types of events that can be handled: + * + * NETIO_EVENT_READ: reading will not block. + * NETIO_EVENT_WRITE: writing will not block. + * NETIO_EVENT_EXCEPT: an exception occurred. + * NETIO_EVENT_TIMEOUT: the timeout expired. + * + * A file descriptor must be specified if the handler is interested in + * the first three event types. A timeout must be specified if the + * event handler is interested in timeouts. These event types can be + * OR'ed together if the handler is willing to handle multiple types + * of events. + * + * The special event type NETIO_EVENT_NONE is available if you wish to + * temporarily disable the event handler without removing and adding + * the handler to the netio structure. + * + * The event callbacks are free to modify the netio_handler_type + * structure to change the file descriptor, timeout, event types, user + * data, or handler functions. + * + * The main loop of the program must call netio_dispatch to check for + * events and dispatch them to the handlers. An additional timeout + * can be specified as well as the signal mask to install while + * blocked in pselect(2). + */ + +#ifndef _NETIO_H_ +#define _NETIO_H_ + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#include <signal.h> + +#include "region-allocator.h" + +/* + * The type of events a handler is interested in. These can be OR'ed + * together to specify multiple event types. + */ +enum netio_event_types { + NETIO_EVENT_NONE = 0, + NETIO_EVENT_READ = 1, + NETIO_EVENT_WRITE = 2, + NETIO_EVENT_EXCEPT = 4, + NETIO_EVENT_TIMEOUT = 8 +}; +typedef enum netio_event_types netio_event_types_type; + +typedef struct netio netio_type; +typedef struct netio_handler netio_handler_type; +typedef struct netio_handler_list netio_handler_list_type; + +struct netio +{ + region_type *region; + netio_handler_list_type *handlers; + netio_handler_list_type *deallocated; + + /* + * Cached value of the current time. The cached value is + * cleared at the start of netio_dispatch to calculate the + * relative timeouts of the event handlers and after calling + * pselect(2) so handlers can use it to calculate a new + * absolute timeout. + * + * Use netio_current_time() to read the current time. + */ + int have_current_time; + struct timespec cached_current_time; + + /* + * Next handler in the dispatch. Only valid during callbacks. + * To make sure that deletes respect the state of the iterator. + */ + netio_handler_list_type *dispatch_next; +}; + +typedef void (*netio_event_handler_type)(netio_type *netio, + netio_handler_type *handler, + netio_event_types_type event_types); + +struct netio_handler +{ + /* + * The file descriptor that should be checked for events. If + * the file descriptor is negative only timeout events are + * checked for. + */ + int fd; + + /* + * The time when no events should be checked for and the + * handler should be called with the NETIO_EVENT_TIMEOUT + * event type. Unlike most timeout parameters the time should + * be absolute, not relative! + */ + struct timespec *timeout; + + /* + * Additional user data. + */ + void *user_data; + + /* + * The type of events that should be checked for. These types + * can be OR'ed together to wait for multiple types of events. + */ + netio_event_types_type event_types; + + /* + * The event handler. The event_types parameter contains the + * OR'ed set of event types that actually triggered. The + * event handler is allowed to modify this handler object. + * The event handler SHOULD NOT block. + */ + netio_event_handler_type event_handler; +}; + + +/* + * Create a new netio instance using the specified REGION. The netio + * instance is cleaned up when the REGION is deallocated. + */ +netio_type *netio_create(region_type *region); + +/* + * Add a new HANDLER to NETIO. + */ +void netio_add_handler(netio_type *netio, netio_handler_type *handler); + +/* + * Remove the HANDLER from NETIO. + */ +void netio_remove_handler(netio_type *netio, netio_handler_type *handler); + +/* + * Retrieve the current time (using gettimeofday(2). + */ +const struct timespec *netio_current_time(netio_type *netio); + +/* + * Check for events and dispatch them to the handlers. If TIMEOUT is + * specified it specifies the maximum time to wait for an event to + * arrive. SIGMASK is passed to the underlying pselect(2) call. + * Returns the number of non-timeout events dispatched, 0 on timeout, + * and -1 on error (with errno set appropriately). + */ +int netio_dispatch(netio_type *netio, + const struct timespec *timeout, + const sigset_t *sigmask); + + +#ifdef __cplusplus +inline netio_event_types_type +operator | (netio_event_types_type lhs, netio_event_types_type rhs) { + return (netio_event_types_type) (lhs | rhs); +} +inline netio_event_types_type +operator |= (netio_event_types_type &lhs, netio_event_types_type rhs) { + lhs = (netio_event_types_type) (lhs | rhs); + return lhs; +} +#endif /* __cplusplus */ + +#endif /* _NETIO_H_ */ diff --git a/usr.sbin/nsd/nsd-checkconf.8 b/usr.sbin/nsd/nsd-checkconf.8 new file mode 100644 index 00000000000..802017c766d --- /dev/null +++ b/usr.sbin/nsd/nsd-checkconf.8 @@ -0,0 +1,94 @@ +.TH "nsd\-checkconf" "8" "Jan 6, 2010" "NLnet Labs" "nsd 3.2.4" +."\ Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. +."\ See LICENSE for the license. +.SH "NAME" +.LP +.B nsd\-checkconf +\- NSD configuration file checker. +.SH "SYNOPSIS" +.LP +.B nsd\-checkconf +.RB [ \-v ] +.RB [ \-h ] +.RB [ \-o +.IR option ] +.RB [ \-z +.IR zonename ] +.RB [ \-s +.IR keyname ] +.I configfile +.SH "DESCRIPTION" +.LP +.B nsd\-checkconf +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 zonec(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 +.B \-v +After reading print the options to standard output in configfile +format. Without this option, only success or parse errors are +reported. +.TP +.B \-h +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 +.B \-z +option. +The special value +.I zones +prints out a list of configured zones. +.P +.RS +This option is primarily used by +.B nsdc +to parse the config file from the shell. If the +.B \-z +option is given, but the +.B \-o +option is not given, nothing is printed. +.RE +.TP +.B \-s\fI keyname +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 \-z\fI zonename +Return the option specified with +.B \-o +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. +.RE +.P +.RS +The \-o, \-s and \-z option print configfile options to standard output. +.RE +.SH "FILES" +.TP +/etc/nsd/nsd.conf +default +.B NSD +configuration file +.SH "SEE ALSO" +.LP +nsd(8), nsdc(8), nsd.conf(5), nsd\-notify(8), nsd\-patch(8), +nsd-xfer(8), zonec(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. diff --git a/usr.sbin/nsd/nsd-checkconf.c b/usr.sbin/nsd/nsd-checkconf.c new file mode 100644 index 00000000000..19d89396620 --- /dev/null +++ b/usr.sbin/nsd/nsd-checkconf.c @@ -0,0 +1,507 @@ +/* + * checkconf - Read and repeat configuration file to output. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> +#include "tsig.h" +#include "options.h" +#include "util.h" +#include "dname.h" + +extern char *optarg; +extern int optind; + +#define ZONE_GET_ACL(NAME, VAR) \ + if (strcasecmp(#NAME, (VAR)) == 0) { \ + quote_acl((zone->NAME)); \ + return; \ + } + +#define ZONE_GET_STR(NAME, VAR) \ + if (strcasecmp(#NAME, (VAR)) == 0) { \ + quote(zone->NAME); \ + return; \ + } + +#define ZONE_GET_BIN(NAME, VAR) \ + if (strcasecmp(#NAME, (VAR)) == 0) { \ + printf("%s\n", zone->NAME?"yes":"no"); \ + } + +#define SERV_GET_BIN(NAME, VAR) \ + if (strcasecmp(#NAME, (VAR)) == 0) { \ + printf("%s\n", opt->NAME?"yes":"no"); \ + } + +#define SERV_GET_STR(NAME, VAR) \ + if (strcasecmp(#NAME, (VAR)) == 0) { \ + quote(opt->NAME); \ + return; \ + } + +#define SERV_GET_INT(NAME, VAR) \ + if (strcasecmp(#NAME, (VAR)) == 0) { \ + printf("%d\n", (int) opt->NAME); \ + return; \ + } + +#define SERV_GET_IP(NAME, VAR) \ + if (strcasecmp(#NAME, (VAR)) == 0) { \ + for(ip = opt->ip_addresses; ip; ip=ip->next) \ + { \ + quote(ip->address); \ + } \ + return; \ + } + +static char buf[BUFSIZ]; + +static char * +underscore(const char *s) { + const char *j = s; + size_t i = 0; + + while(j && *j) { + if (*j == '-') { + buf[i++] = '_'; + } else { + buf[i++] = *j; + } + j++; + if (i > BUFSIZ) { + return NULL; + } + } + buf[i] = '\0'; + return buf; +} + +static void +usage(void) +{ + fprintf(stderr, "usage: nsd-checkconf [-v|-h] [-o option] [-z zonename]\n"); + fprintf(stderr, " [-s keyname] <configfilename>\n"); + fprintf(stderr, " Checks NSD configuration file for errors.\n"); + fprintf(stderr, " Version %s. Report bugs to <%s>.\n\n", + PACKAGE_VERSION, PACKAGE_BUGREPORT); + fprintf(stderr, "Use with a configfile as argument to check syntax.\n"); + fprintf(stderr, "Use with -o, -z or -s options to query the configuration.\n\n"); + 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, "-z zonename Print option value for the zone given.\n"); + fprintf(stderr, "-s keyname Print base64 secret blob for the TSIG key.\n"); + exit(1); +} + +static void +print_string_var(const char* varname, const char* value) +{ + if (!value) { + printf("\t#%s\n", varname); + } else { + printf("\t%s \"%s\"\n", varname, value); + } +} + +static void +quote(const char *v) +{ + if(v==NULL) + printf("\n"); + else + printf("%s\n", v); +} + +static void +quote_acl(acl_options_t* acl) +{ + while(acl) + { + printf("%s %s\n", acl->ip_address_spec, + acl->nokey?"NOKEY":(acl->blocked?"BLOCKED": + (acl->key_name?acl->key_name:"(null)"))); + acl=acl->next; + } +} + +static void +print_acl(const char* varname, acl_options_t* acl) +{ + while(acl) + { + printf("\t%s ", varname); + if(acl->use_axfr_only) + printf("AXFR "); + if(acl->allow_udp) + printf("UDP "); + printf("%s %s\n", acl->ip_address_spec, + acl->nokey?"NOKEY":(acl->blocked?"BLOCKED": + (acl->key_name?acl->key_name:"(null)"))); + if(1) { + printf("\t# %s", acl->is_ipv6?"ip6":"ip4"); + if(acl->port == 0) printf(" noport"); + else printf(" port=%d", acl->port); + if(acl->rangetype == acl_range_single) printf(" single"); + if(acl->rangetype == acl_range_mask) printf(" masked"); + if(acl->rangetype == acl_range_subnet) printf(" subnet"); + if(acl->rangetype == acl_range_minmax) printf(" minmax"); + if(acl->is_ipv6) { +#ifdef INET6 + char dest[128]; + inet_ntop(AF_INET6, &acl->addr.addr6, dest, sizeof(dest)); + printf(" addr=%s", dest); + if(acl->rangetype != acl_range_single) { + inet_ntop(AF_INET6, &acl->range_mask.addr6, dest, sizeof(dest)); + printf(" rangemask=%s", dest); + } +#else + printf(" ip6addr-noip6defined"); +#endif + } else { + char dest[128]; + inet_ntop(AF_INET, &acl->addr.addr, dest, sizeof(dest)); + printf(" addr=%s", dest); + if(acl->rangetype != acl_range_single) { + inet_ntop(AF_INET, &acl->range_mask.addr, dest, sizeof(dest)); + printf(" rangemask=%s", dest); + } + } + printf("\n"); + } + acl=acl->next; + } +} + + +void +config_print_zone(nsd_options_t* opt, const char* k, const char *o, const char *z) +{ + 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) { + quote(key->secret); + return; + } + } + printf("Could not find key %s\n", k); + return; + } + + if (!o) { + return; + } + + if (z) { + 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_ACL(outgoing_interface, o); + ZONE_GET_BIN(allow_axfr_fallback, o); + printf("Zone option not handled: %s %s\n", z, o); + exit(1); + } + } + printf("Zone does not exist: %s\n", z); + exit(1); + } else { + /* look in the server section */ + SERV_GET_IP(ip_address, o); + /* bin */ + SERV_GET_BIN(debug_mode, o); + SERV_GET_BIN(ip4_only, o); + SERV_GET_BIN(ip6_only, o); + SERV_GET_BIN(hide_version, o); + /* str */ + SERV_GET_STR(database, o); + SERV_GET_STR(identity, o); + SERV_GET_STR(logfile, o); + SERV_GET_STR(pidfile, o); + 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(port, o); + /* int */ + SERV_GET_INT(server_count, o); + SERV_GET_INT(tcp_count, o); + SERV_GET_INT(tcp_query_count, o); + SERV_GET_INT(tcp_timeout, o); + SERV_GET_INT(ipv4_edns_size, o); + SERV_GET_INT(ipv6_edns_size, o); + SERV_GET_INT(statistics, o); + SERV_GET_INT(xfrd_reload_timeout, o); + SERV_GET_INT(verbosity, o); + + if(strcasecmp(o, "zones") == 0) { + RBTREE_FOR(zone, zone_options_t*, opt->zone_options) + quote(zone->name); + return; + } + printf("Server option not handled: %s\n", o); + exit(1); + } +} + +void +config_test_print_server(nsd_options_t* opt) +{ + ip_address_option_t* ip; + key_options_t* key; + zone_options_t* zone; + + printf("# Config settings.\n"); + printf("server:\n"); + printf("\tdebug-mode: %s\n", opt->debug_mode?"yes":"no"); + printf("\tip4-only: %s\n", opt->ip4_only?"yes":"no"); + printf("\tip6-only: %s\n", opt->ip6_only?"yes":"no"); + printf("\thide-version: %s\n", opt->hide_version?"yes":"no"); + print_string_var("database:", opt->database); + print_string_var("identity:", opt->identity); + print_string_var("logfile:", opt->logfile); + printf("\tserver_count: %d\n", opt->server_count); + printf("\ttcp_count: %d\n", opt->tcp_count); + printf("\ttcp_query_count: %d\n", opt->tcp_query_count); + printf("\ttcp_timeout: %d\n", opt->tcp_timeout); + printf("\tipv4-edns-size: %d\n", (int) opt->ipv4_edns_size); + printf("\tipv6-edns-size: %d\n", (int) opt->ipv6_edns_size); + print_string_var("pidfile:", opt->pidfile); + print_string_var("port:", opt->port); + printf("\tstatistics: %d\n", opt->statistics); + 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); + 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); + } + for(key = opt->keys; key; key=key->next) + { + printf("\nkey:\n"); + print_string_var("name:", key->name); + print_string_var("algorithm:", key->algorithm); + print_string_var("secret:", key->secret); + } + RBTREE_FOR(zone, zone_options_t*, opt->zone_options) + { + printf("\nzone:\n"); + print_string_var("name:", zone->name); + print_string_var("zonefile:", zone->zonefile); + 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("outgoing-interface:", zone->outgoing_interface); + printf("\tallow-axfr-fallback: %s\n", zone->allow_axfr_fallback?"yes":"no"); + } + +} + +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) { + num++; + ip = ip->next; + } + if(num > MAX_INTERFACES) { + fprintf(stderr, "%s: too many interfaces (ip-address:) specified.\n", filename); + errors ++; + } + + RBTREE_FOR(zone, zone_options_t*, opt->zone_options) + { + const dname_type* dname = dname_parse(opt->region, zone->name); /* memory leak. */ + if(!dname) { + fprintf(stderr, "%s: cannot parse zone name syntax for zone %s.\n", filename, zone->name); + errors ++; + } + if(zone->allow_notify && !zone->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); + errors ++; + } + } + + 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) + { + fprintf(stderr, "%s: 'statistics: %d' but BIND 8 statistics feature not enabled.\n", + filename, opt->statistics); + errors ++; + } +#endif +#ifndef HAVE_CHROOT + if(opt->chroot != 0) + { + fprintf(stderr, "%s: chroot %s given. chroot not supported on this platform.\n", + filename, opt->chroot); + errors ++; + } +#endif + if (opt->identity && strlen(opt->identity) > UCHAR_MAX) { + fprintf(stderr, "%s: server identity too long (%u characters)\n", + filename, (unsigned) strlen(opt->identity)); + errors ++; + } + + /* 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) { + 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", + 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) { + fprintf(stderr, "%s: xfrdfile %s is not relative to chroot %s.\n", + filename, opt->xfrdfile, opt->chroot); + errors ++; + } + } + if (atoi(opt->port) <= 0) { + fprintf(stderr, "%s: port number '%s' is not a positive number.\n", + filename, opt->port); + errors ++; + } + 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); + } + + return (errors == 0); +} + +int +main(int argc, char* argv[]) +{ + int c; + int verbose = 0; + 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* configfile; + nsd_options_t *options; + + /* Parse the command line... */ + while ((c = getopt(argc, argv, "vo:s:z:")) != -1) { + switch (c) { + case 'v': + verbose = 1; + break; + case 'o': + conf_opt = optarg; + break; + case 's': + conf_key = optarg; + break; + case 'z': + conf_zone = optarg; + break; + default: + usage(); + }; + } + argc -= optind; + argv += optind; + if (argc == 0 || argc>=2) { + usage(); + } + configfile = argv[0]; + + /* read config file */ + options = nsd_options_create(region_create(xalloc, free)); + if (!parse_options_file(options, configfile) || + !additional_checks(options, configfile)) { + exit(2); + } + if (conf_opt || conf_key) { + config_print_zone(options, conf_key, underscore(conf_opt), conf_zone); + } else { + if (verbose) { + printf("# Read file %s: %d zones, %d keys.\n", + configfile, + (int)nsd_options_num_zones(options), + (int)options->numkeys); + config_test_print_server(options); + } + } + return 0; +} diff --git a/usr.sbin/nsd/nsd-notify.8 b/usr.sbin/nsd/nsd-notify.8 new file mode 100644 index 00000000000..eb33178758c --- /dev/null +++ b/usr.sbin/nsd/nsd-notify.8 @@ -0,0 +1,66 @@ +.TH "nsd\-notify" "8" "Jan 6, 2010" "NLnet Labs" "nsd 3.2.4" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. +.\" See LICENSE for the license. +.SH "NAME" +.LP +.B nsd\-notify +\- program to send NOTIFY's to remote nameservers. +.SH "SYNOPSIS" +.LP +.B nsd\-notify +.RB [ \-4 ] +.RB [ \-6 ] +.RB [ \-h ] +.RB [ \-a +.IR address[@port] ] +.RB [ \-p +.IR port ] +.RB [ \-y +.IR key:secret[:algorithm] ] +.B \-z +.I zone servers +.SH "DESCRIPTION" +.LP +.B Nsd\-notify +is simple program to send NOTIFY's to remoter nameservers. +.B NSD +is a complete implementation of an authoritative DNS nameserver. +.SH "OPTIONS" +.TP +.B \-4 +Only send to IPv4 addresses. +.TP +.B \-6 +Only send to IPv6 addresses. +.TP +.B \-h +Print help information and exit. +.TP +.B \-a\fI address[@port] +Specify the source address (and port) to send from. +.TP +.B \-p\fI port +Specify the port to send to. +.TP +.B \-y\fI key:secret[:algorithm] +Specify a TSIG key and base64 encoded secret to sign the notification with. If +the TSIG algorithm is not defined, MD5 is used. +.TP +.B z\fI zone +Specify the zone to notify about. +.TP +.I servers +List of nameservers to send to. +.SH "EXAMPLES" +.LP +To run this program the standard way type: +.LP +.B # nsd\-notify \-z foobar.cz 1.2.3.4 +.SH "SEE ALSO" +.LP +nsd(8), nsdc(8), nsd.conf(5), nsd\-checkconf(8), +nsd\-patch(8), nsd\-xfer(8), zonec(8) +.SH "AUTHORS" +.B NSD +was written by NLnet Labs and RIPE NCC joint team. Please see CREDITS +file in the distribution for further details. diff --git a/usr.sbin/nsd/nsd-notify.c b/usr.sbin/nsd/nsd-notify.c new file mode 100644 index 00000000000..32e1ac874e4 --- /dev/null +++ b/usr.sbin/nsd/nsd-notify.c @@ -0,0 +1,476 @@ +/* + * nsd-notify.c -- sends notify(rfc1996) message to a list of servers + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <tsig.h> +#include <unistd.h> +#include <netdb.h> + +#include "query.h" + +extern char *optarg; +extern int optind; + +/* + * Check if two getaddrinfo result lists have records with matching + * ai_family fields. + */ +int check_matching_address_family(struct addrinfo *a, struct addrinfo *b); + +/* + * Returns the first record with ai_family == FAMILY, or NULL if no + * such record is found. + */ +struct addrinfo *find_by_address_family(struct addrinfo *addrs, int family); + +/* + * Assigns pointers to hostname and port and wipes out the optional delimiter. + */ +void get_hostname_port_frm_str(char* arg, const char** hostname, + const char** port); + +/* + * Log a warning message. + */ +static void warning(const char *format, ...) ATTR_FORMAT(printf, 1, 2); +static void +warning(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_vmsg(LOG_WARNING, format, args); + va_end(args); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: nsd-notify [-4] [-6] [-a src[@port] [-h] [-p \ +port] [-y key:secret[:algo]] -z zone servers\n\n"); + fprintf(stderr, "Send NOTIFY to secondary servers to force a zone \ +update.\n"); + fprintf(stderr, "Version %s. Report bugs to <%s>.\n\n", + PACKAGE_VERSION, PACKAGE_BUGREPORT); + fprintf(stderr, " -4 Send using IPv4.\n"); + fprintf(stderr, " -6 Send using IPv6.\n"); + fprintf(stderr, " -a src[@port] Local hostname/ip-address for " + "the connection, including optional source port.\n"); + fprintf(stderr, " -h Print this help " + "information.\n"); + fprintf(stderr, " -p port Port number of secondary " + "server.\n"); + fprintf(stderr, " -y key:secret[:algo] TSIG keyname, base64 secret " + "blob and HMAC algorithm.\n" + " If algo is not provided, " + "HMAC-MD5 is assumed.\n"); + fprintf(stderr, " -z zone Name of zone to be updated.\n"); + fprintf(stderr, " servers IP addresses of the secondary " + "server(s).\n"); + exit(1); +} + +/* + * Send NOTIFY messages to the host, as in struct q, + * waiting for ack packet (received in buffer answer). + * Will retry transmission after a timeout. + * addrstr is the string describing the address of the host. + */ +static void +notify_host(int udp_s, struct query* q, struct query *answer, + struct addrinfo* res, const dname_type *zone, const char* addrstr) +{ + int timeout_retry = 1; /* seconds */ + int num_retry = 6; /* times to try */ + fd_set rfds; + struct timeval tv; + int retval = 0; + ssize_t received = 0; + int got_ack = 0; + socklen_t addrlen = 0; + + while(!got_ack) { + /* WE ARE READY SEND IT OUT */ + if (sendto(udp_s, + buffer_current(q->packet), + buffer_remaining(q->packet), 0, + res->ai_addr, res->ai_addrlen) == -1) { + warning("send to %s failed: %s\n", addrstr, + strerror(errno)); + close(udp_s); + return; + } + + /* wait for ACK packet */ + FD_ZERO(&rfds); + FD_SET(udp_s, &rfds); + tv.tv_sec = timeout_retry; /* seconds */ + tv.tv_usec = 0; /* microseconds */ + retval = select(udp_s + 1, &rfds, NULL, NULL, &tv); + if (retval == -1) { + warning("error waiting for reply from %s: %s\n", + addrstr, strerror(errno)); + close(udp_s); + return; + } + if (retval == 0) { + num_retry--; + if(num_retry == 0) { + warning("error: failed to send notify to %s.\n", + addrstr); + exit(1); + } + warning("timeout (%d s) expired, retry notify to %s.\n", + timeout_retry, addrstr); + } + if (retval == 1) { + got_ack = 1; + } + /* Exponential backoff */ + timeout_retry *= 2; + } + + /* receive reply */ + addrlen = res->ai_addrlen; + received = recvfrom(udp_s, buffer_begin(answer->packet), + buffer_remaining(answer->packet), 0, + res->ai_addr, &addrlen); + res->ai_addrlen = addrlen; + + if (received == -1) { + warning("recv %s failed: %s\n", addrstr, strerror(errno)); + } else { + /* check the answer */ + if ((ID(q->packet) == ID(answer->packet)) && + (OPCODE(answer->packet) == OPCODE_NOTIFY) && + AA(answer->packet) && + QR(answer->packet) && (RCODE(answer->packet) == RCODE_OK)) { + /* no news is good news */ + /* info("reply from: %s, acknowledges notify.\n", + addrstr); */ + } else { + warning("bad reply from %s for zone %s, error response %s (%d).\n", + addrstr, dname_to_string(zone, NULL), rcode2str(RCODE(answer->packet)), + RCODE(answer->packet)); + } + } + close(udp_s); +} + +#ifdef TSIG +static tsig_key_type* +add_key(region_type* region, const char* opt, tsig_algorithm_type** algo) +{ + /* parse -y key:secret_base64 format option */ + char* delim = strchr(opt, ':'); + char* delim2 = NULL; + + if (delim) + delim2 = strchr(delim+1, ':'); + + tsig_key_type *key = (tsig_key_type*)region_alloc( + region, sizeof(tsig_key_type)); + size_t len; + int sz; + if(!key) { + log_msg(LOG_ERR, "region_alloc failed (add_key)"); + return 0; + } + + if(!delim) { + log_msg(LOG_ERR, "bad key syntax %s", opt); + return 0; + } + *delim = '\0'; + key->name = dname_parse(region, opt); + if(!key->name) { + log_msg(LOG_ERR, "bad key name %s", opt); + return 0; + } + *delim = ':'; + + if (!delim2) + *algo = tsig_get_algorithm_by_name("hmac-md5"); + else { + char* by_name = (char*) malloc(sizeof(char)*(5+strlen(delim2))); + snprintf(by_name, 5+strlen(delim2), "hmac-%s", delim2+1); + *algo = tsig_get_algorithm_by_name(by_name); + free(by_name); + *delim2 = '\0'; + } + + if (!(*algo)) { + *delim2 = ':'; + log_msg(LOG_ERR, "bad tsig algorithm %s", opt); + return 0; + } + + len = strlen(delim+1); + key->data = region_alloc(region, len+1); + if(!key->data) { + log_msg(LOG_ERR, "region_alloc failed (add_key, key->data)"); + return 0; + } + sz= b64_pton(delim+1, (uint8_t*)key->data, len); + if(sz == -1) { + log_msg(LOG_ERR, "bad key syntax %s", opt); + return 0; + } + key->size = sz; + tsig_add_key(key); + return key; +} +#endif /* TSIG */ + +int +main (int argc, char *argv[]) +{ + int c, udp_s; + struct query q; + struct query answer; + const dname_type *zone = NULL; + struct addrinfo hints, *res0, *res; + int error; + int default_family = DEFAULT_AI_FAMILY; + const char *local_hostname = NULL; + struct addrinfo *local_address, *local_addresses = NULL; + const char *port = UDP_PORT; + const char *local_port = NULL; + region_type *region = region_create(xalloc, free); +#ifdef TSIG + tsig_key_type *tsig_key = 0; + tsig_record_type tsig; + tsig_algorithm_type* algo = NULL; +#endif /* TSIG */ + + log_init("nsd-notify"); +#ifdef TSIG + if(!tsig_init(region)) { + log_msg(LOG_ERR, "could not init tsig\n"); + exit(1); + } +#endif /* TSIG */ + + /* Parse the command line... */ + while ((c = getopt(argc, argv, "46a:hp:y:z:")) != -1) { + switch (c) { + case '4': + default_family = AF_INET; + break; + case '6': +#ifdef INET6 + default_family = AF_INET6; + break; +#else /* !INET6 */ + log_msg(LOG_ERR, "IPv6 support not enabled\n"); + exit(1); +#endif /* !INET6 */ + case 'a': + get_hostname_port_frm_str((char *) optarg, + &local_hostname, &local_port); + break; + case 'p': + port = optarg; + break; + case 'y': +#ifdef TSIG + if (!(tsig_key = add_key(region, optarg, &algo))) + exit(1); +#else + log_msg(LOG_ERR, "option -y given but TSIG not enabled"); +#endif /* TSIG */ + break; + case 'z': + zone = dname_parse(region, optarg); + if (!zone) { + log_msg(LOG_ERR, + "incorrect domain name '%s'", + optarg); + exit(1); + } + break; + case 'h': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 0 || zone == NULL) { + usage(); + } + + /* Initialize the query */ + memset(&q, 0, sizeof(struct query)); + q.addrlen = sizeof(q.addr); + q.maxlen = 512; + q.packet = buffer_create(region, QIOBUFSZ); + memset(buffer_begin(q.packet), 0, buffer_remaining(q.packet)); + + /* Set up the header */ + OPCODE_SET(q.packet, OPCODE_NOTIFY); + ID_SET(q.packet, 42); /* Does not need to be random. */ + AA_SET(q.packet); + QDCOUNT_SET(q.packet, 1); + buffer_skip(q.packet, QHEADERSZ); + buffer_write(q.packet, dname_name(zone), zone->name_size); + buffer_write_u16(q.packet, TYPE_SOA); + buffer_write_u16(q.packet, CLASS_IN); +#ifdef TSIG + if(tsig_key) { + assert(algo); + tsig_create_record(&tsig, region); + tsig_init_record(&tsig, algo, tsig_key); + tsig_init_query(&tsig, ID(q.packet)); + tsig_prepare(&tsig); + tsig_update(&tsig, q.packet, buffer_position(q.packet)); + tsig_sign(&tsig); + tsig_append_rr(&tsig, q.packet); + ARCOUNT_SET(q.packet, ARCOUNT(q.packet) + 1); + } +#endif + buffer_flip(q.packet); + + /* initialize buffer for ack */ + memset(&answer, 0, sizeof(struct query)); + answer.addrlen = sizeof(answer.addr); + answer.maxlen = 512; + answer.packet = buffer_create(region, QIOBUFSZ); + memset(buffer_begin(answer.packet), 0, buffer_remaining(answer.packet)); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = default_family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + if (local_hostname) { + int rc = getaddrinfo(local_hostname, local_port, + &hints, &local_addresses); + if (rc) { + warning("local hostname '%s' not found: %s", + local_hostname, gai_strerror(rc)); + } + } + + for (/*empty*/; *argv; argv++) { + error = getaddrinfo(*argv, port, &hints, &res0); + if (error) { + warning("skipping bad address %s: %s\n", *argv, + gai_strerror(error)); + continue; + } + + if (local_addresses + && !check_matching_address_family(res0, local_addresses)) + { + warning("no local address family matches remote " + "address family, skipping server '%s'", + *argv); + continue; + } + + for (res = res0; res; res = res->ai_next) { + if (res->ai_addrlen > sizeof(q.addr)) { + continue; + } + + /* + * If a local address is specified, use an + * address with the same family as the remote + * address. + */ + local_address = find_by_address_family(local_addresses, + res->ai_family); + if (local_addresses && !local_address) { + /* Continue with next remote address. */ + continue; + } + + udp_s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (udp_s == -1) { + warning("cannot create socket: %s\n", + strerror(errno)); + continue; + } + + /* Bind socket to local address, if required. */ + if (local_address && bind(udp_s, + local_address->ai_addr, + local_address->ai_addrlen) < 0) + { + warning("cannot bind to %s: %s\n", + local_hostname, strerror(errno)); + } + + memcpy(&q.addr, res->ai_addr, res->ai_addrlen); + notify_host(udp_s, &q, &answer, res, zone, *argv); + } + freeaddrinfo(res0); + } + exit(0); +} + +void +get_hostname_port_frm_str(char* arg, const char** hostname, + const char** port) +{ + /* parse -a src[@port] option */ + char* delim = strchr(arg, '@'); + + if (delim) { + *delim = '\0'; + *port = delim+1; + } + *hostname = arg; +} + + +int +check_matching_address_family(struct addrinfo *a0, struct addrinfo *b0) +{ + struct addrinfo *a; + struct addrinfo *b; + + for (a = a0; a; a = a->ai_next) { + for (b = b0; b; b = b->ai_next) { + if (a->ai_family == b->ai_family) { + return 1; + } + } + } + return 0; +} + +struct addrinfo * +find_by_address_family(struct addrinfo *addrs, int family) +{ + for (; addrs; addrs = addrs->ai_next) { + if (addrs->ai_family == family) { + return addrs; + } + } + return NULL; +} + diff --git a/usr.sbin/nsd/nsd-patch.8 b/usr.sbin/nsd/nsd-patch.8 new file mode 100644 index 00000000000..ffb7382abbb --- /dev/null +++ b/usr.sbin/nsd/nsd-patch.8 @@ -0,0 +1,69 @@ +.TH "nsd\-patch" "8" "Jan 6, 2010" "NLnet Labs" "nsd 3.2.4" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. +.\" See LICENSE for the license. +.SH "NAME" +.LP +.B nsd\-patch +\- NSD zone patcher version 3.2.4. +.SH "SYNOPSIS" +.B nsd\-patch +.RB [ \-c +.IR configfile ] +.RB [ \-f ] +.RB [ \-h ] +.RB [ \-l ] +.RB [ \-o +.IR dbfile ] +.RB [ \-s ] +.RB [ \-x +.IR difffile ] +.SH "DESCRIPTION" +.LP +.B Nsd\-patch +is the zone patcher for nsd(8). It reads in the nsd database +(nsd.db) and difffile (ixfr.db), and overwrites the zone text files +if they have been updated. Running this regularly ensures that the +difffile does not grow infinitely. +.SH "OPTIONS" +.TP +.B \-c\fI configfile +Read specified configfile instead of the default +.IR /etc/nsd/nsd.conf . +.TP +.B \-f +Forces writing zone files. Also zones that have not changed are written +back to their zone files. +.TP +.B \-h +Print usage help information and exit. +.TP +.B \-l +List the journal entries from the difffile. Does not write to zone files. +.TP +.B \-o\fI dbfile +Store the output directly to dbfile. +.TP +.B \-s +Skip writing zone files. No zones are written back to their zone files. +.TP +.B \-x\fI difffile +Read specified difffile. Overrides the config file setting. +.SH "FILES" +.TP +/var/db/nsd/nsd.db +default +.B NSD +database +.TP +/etc/nsd/nsd.conf +default +.B NSD +configuration file +.SH "SEE ALSO" +nsd(8), nsdc(8), nsd.conf(5), nsd-checkconf(8), nsd-notify(8), +nsd-xfer(8), zonec(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. diff --git a/usr.sbin/nsd/nsd-patch.c b/usr.sbin/nsd/nsd-patch.c new file mode 100644 index 00000000000..46d4afeebc6 --- /dev/null +++ b/usr.sbin/nsd/nsd-patch.c @@ -0,0 +1,429 @@ +/* + * nsd-patch - read database and ixfrs and patch up zone files. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include "options.h" +#include "difffile.h" +#include "namedb.h" +#include "util.h" + +extern char *optarg; +extern int optind; + +static void +usage(void) +{ + fprintf(stderr, "usage: nsd-patch [options]\n"); + fprintf(stderr, " Reads database and ixfrs and patches up zone files.\n"); + fprintf(stderr, " Version %s. Report bugs to <%s>.\n\n", PACKAGE_VERSION, PACKAGE_BUGREPORT); + fprintf(stderr, "-c configfile Specify config file to use, instead of %s\n", CONFIGFILE); + fprintf(stderr, "-f Force writing of zone files.\n"); + fprintf(stderr, "-h Print this help information.\n"); + fprintf(stderr, "-l List contents of transfer journal difffile, %s\n", DIFFFILE); + fprintf(stderr, "-o dbfile Specify dbfile to output the result " + "directly to dbfile, nsd.db.\n"); + fprintf(stderr, "-s Skip writing of zone files.\n"); + fprintf(stderr, "-x difffile Specify diff file to use, instead of diff file from config.\n"); + exit(1); +} + +static void +list_xfr(FILE *in) +{ + uint32_t timestamp[2]; + uint32_t skiplen, len, new_serial; + char zone_name[3072]; + uint16_t id; + uint32_t seq_nr, len2; + + if(!diff_read_32(in, ×tamp[0]) || + !diff_read_32(in, ×tamp[1]) || + !diff_read_32(in, &len) || + !diff_read_str(in, zone_name, sizeof(zone_name)) || + !diff_read_32(in, &new_serial) || + !diff_read_16(in, &id) || + !diff_read_32(in, &seq_nr)) { + fprintf(stderr, "incomplete zone transfer content packet\n"); + return; + } + skiplen = len - (sizeof(uint32_t)*3 + sizeof(uint16_t) + strlen(zone_name)); + fprintf(stdout, "zone %s transfer id %x serial %d timestamp %u.%u: " + "seq_nr %d of %d bytes\n", zone_name, id, new_serial, + timestamp[0], timestamp[1], seq_nr, skiplen); + + if(fseeko(in, skiplen, SEEK_CUR) == -1) + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + + if(!diff_read_32(in, &len2)) { + fprintf(stderr, "incomplete zone transfer content packet\n"); + return; + } + if(len != len2) { + fprintf(stderr, "packet seq %d had bad length check bytes!\n", + seq_nr); + } +} + +static const char* +get_date(const char* log) +{ + const char *entry = strstr(log, " time "); + time_t t = 0; + static char timep[30]; + if(!entry) return "<notime>"; + t = strtol(entry + 6, NULL, 10); + if(t == 0) return "0"; + timep[0]=0; + strlcpy(timep, ctime(&t), sizeof(timep)); + /* remove newline at end */ + if(strlen(timep) > 0 && timep[strlen(timep)-1]=='\n') + timep[strlen(timep)-1] = 0; + return timep; +} + +static void +list_commit(FILE *in) +{ + uint32_t timestamp[2]; + uint32_t len; + char zone_name[3072]; + uint32_t old_serial, new_serial; + uint16_t id; + uint32_t num; + uint8_t commit; + char log_msg[10240]; + uint32_t len2; + + if(!diff_read_32(in, ×tamp[0]) || + !diff_read_32(in, ×tamp[1]) || + !diff_read_32(in, &len) || + !diff_read_str(in, zone_name, sizeof(zone_name)) || + !diff_read_32(in, &old_serial) || + !diff_read_32(in, &new_serial) || + !diff_read_16(in, &id) || + !diff_read_32(in, &num) || + !diff_read_8(in, &commit) || + !diff_read_str(in, log_msg, sizeof(log_msg)) || + !diff_read_32(in, &len2)) { + fprintf(stderr, "incomplete commit/rollback packet\n"); + return; + } + fprintf(stdout, "zone %s transfer id %x serial %d: %s of %d packets\n", + zone_name, id, new_serial, commit?"commit":"rollback", num); + fprintf(stdout, " time %s, from serial %d, log message: %s\n", + get_date(log_msg), old_serial, log_msg); + if(len != len2) { + fprintf(stderr, " commit packet with bad length check \ + bytes!\n"); + } +} + +static void +debug_list(struct nsd_options* opt) +{ + const char* file = opt->difffile; + FILE *f; + uint32_t type; + fprintf(stdout, "debug listing of the contents of %s\n", file); + f = fopen(file, "r"); + if(!f) { + fprintf(stderr, "error opening %s: %s\n", file, + strerror(errno)); + return; + } + while(diff_read_32(f, &type)) { + switch(type) { + case DIFF_PART_IXFR: + list_xfr(f); + break; + case DIFF_PART_SURE: + list_commit(f); + break; + default: + fprintf(stderr, "bad part of type %x\n", type); + break; + } + } + + fclose(f); +} + +static int +exist_difffile(struct nsd_options* opt) +{ + /* see if diff file exists */ + const char* file = opt->difffile; + FILE *f; + + f = fopen(file, "r"); + if(!f) { + if(errno == ENOENT) + return 0; + fprintf(stderr, "could not open file %s: %s\n", + file, strerror(errno)); + return 0; + } + return 1; +} + +static void +print_rrs(FILE* out, struct zone* zone) +{ + rrset_type *rrset; + domain_type *domain = zone->apex; + region_type* region = region_create(xalloc, free); + struct state_pretty_rr* state = create_pretty_rr(region); + /* first print the SOA record for the zone */ + if(zone->soa_rrset) { + size_t i; + for(i=0; i < zone->soa_rrset->rr_count; i++) { + if(!print_rr(out, state, &zone->soa_rrset->rrs[i])){ + fprintf(stderr, "There was an error " + "printing SOARR to zone %s\n", + zone->opts->name); + } + } + } + /* go through entire tree below the zone apex (incl subzones) */ + while(domain && dname_is_subdomain( + domain_dname(domain), domain_dname(zone->apex))) + { + for(rrset = domain->rrsets; rrset; rrset=rrset->next) + { + size_t i; + if(rrset->zone != zone || rrset == zone->soa_rrset) + continue; + for(i=0; i < rrset->rr_count; i++) { + if(!print_rr(out, state, &rrset->rrs[i])){ + fprintf(stderr, "There was an error " + "printing RR to zone %s\n", + zone->opts->name); + } + } + } + domain = domain_next(domain); + } + region_destroy(region); +} + +static void +print_commit_log(FILE* out, const dname_type* zone, struct diff_log* commit_log) +{ + struct diff_log* p = commit_log; + region_type* region = region_create(xalloc, free); + while(p) + { + const dname_type* dname = dname_parse(region, p->zone_name); + if(dname_compare(dname, zone) == 0) + { + fprintf(out, "; commit"); + if(p->error) + fprintf(out, "(%s)", p->error); + fprintf(out, ": %s\n", p->comment); + } + p = p->next; + } + region_destroy(region); +} + +static void +write_to_zonefile(struct zone* zone, struct diff_log* commit_log) +{ + const char* filename = zone->opts->zonefile; + time_t now = time(0); + FILE *out; + + fprintf(stderr, "writing zone %s to file %s\n", zone->opts->name, + filename); + + if(!zone->apex) { + fprintf(stderr, "zone %s has no apex, no data.\n", filename); + return; + } + + out = fopen(filename, "w"); + if(!out) { + fprintf(stderr, "cannot open or create file %s for writing: %s\n", + filename, strerror(errno)); + return; + } + + /* print zone header */ + fprintf(out, "; NSD version %s\n", PACKAGE_VERSION); + fprintf(out, "; nsd-patch zone %s run at time %s", + zone->opts->name, ctime(&now)); + print_commit_log(out, domain_dname(zone->apex), commit_log); + + print_rrs(out, zone); + + fclose(out); +} + +int main(int argc, char* argv[]) +{ + int c; + const char* configfile = CONFIGFILE; + const char* difffile = NULL; + const char* dbfile = NULL; + nsd_options_t *options; + struct namedb* db = NULL; + struct namedb* dbout = NULL; + struct zone* zone; + struct diff_log* commit_log = 0; + size_t fake_child_count = 1; + int debug_list_diff = 0; + int force_write = 0; + int skip_write = 0; + int difffile_exists = 0; + + /* Parse the command line... */ + while ((c = getopt(argc, argv, "c:fhlo:sx:")) != -1) { + switch (c) { + case 'c': + configfile = optarg; + break; + case 'l': + debug_list_diff = 1; + break; + case 'f': + if (skip_write) + { + fprintf(stderr, "Cannot force and skip writing " + "zonefiles at the same time\n"); + exit(1); + } + else + force_write = 1; + break; + case 's': + if (force_write) + { + fprintf(stderr, "Cannot skip and force writing " + "zonefiles at the same time\n"); + exit(1); + } + else + skip_write = 1; + break; + case 'o': + dbfile = optarg; + break; + case 'x': + difffile = optarg; + break; + case 'h': + default: + usage(); + }; + } + argc -= optind; + argv += optind; + if (argc != 0) + usage(); + + /* read config file */ + log_init("nsd-patch"); + options = nsd_options_create(region_create(xalloc, free)); + if(!parse_options_file(options, configfile)) { + fprintf(stderr, "Could not read config: %s\n", configfile); + exit(1); + } + if(options->zonesdir && options->zonesdir[0]) { + if (chdir(options->zonesdir)) { + fprintf(stderr, "nsd-patch: cannot chdir to %s: %s\n", + options->zonesdir, strerror(errno)); + exit(1); + } + } + + /* override difffile if commandline option given */ + if(difffile) + options->difffile = difffile; + + /* see if necessary */ + if(!exist_difffile(options)) { + fprintf(stderr, "No diff file.\n"); + if (!force_write) + exit(0); + } + else difffile_exists = 1; + + if(debug_list_diff) { + debug_list(options); + exit(0); + } + + /* read database and diff file */ + fprintf(stdout, "reading database\n"); + db = namedb_open(options->database, options, fake_child_count); + if(!db) { + fprintf(stderr, "could not read database: %s\n", + options->database); + exit(1); + } + + /* set all updated to 0 so we know what has changed */ + for(zone = db->zones; zone; zone = zone->next) + { + zone->updated = 0; + } + + if (dbfile) + dbout = namedb_new(dbfile); + if (dbout) + { + db->fd = dbout->fd; + db->filename = (char*) dbfile; + } + + /* read ixfr diff file */ + if (difffile_exists) { + fprintf(stdout, "reading updates to database\n"); + if(!diff_read_file(db, options, &commit_log, fake_child_count)) + { + fprintf(stderr, "unable to load the diff file: %s\n", + options->difffile); + exit(1); + } + } + + if (skip_write) + fprintf(stderr, "skip patching up zonefiles.\n"); + else { + fprintf(stdout, "writing changed zones\n"); + for(zone = db->zones; zone; zone = zone->next) + { + if(!force_write && !zone->updated) { + fprintf(stdout, "zone %s had not changed.\n", + zone->opts->name); + continue; + } + /* write zone to its zone file */ + write_to_zonefile(zone, commit_log); + } + } + + /* output result directly to dbfile */ + if (dbout) + { + fprintf(stdout, "storing database to %s.\n", dbout->filename); + if (namedb_save(db) != 0) { + fprintf(stderr, "error writing the database (%s): %s\n", + dbfile, strerror(errno)); + exit(1); + } + } + fprintf(stdout, "done\n"); + + return 0; +} diff --git a/usr.sbin/nsd/nsd-xfer.8 b/usr.sbin/nsd/nsd-xfer.8 new file mode 100644 index 00000000000..28375b19418 --- /dev/null +++ b/usr.sbin/nsd/nsd-xfer.8 @@ -0,0 +1,82 @@ +.TH "nsd\-xfer" "8" "Jan 6, 2010" "NLnet Labs" "nsd 3.2.4" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. +.\" See LICENSE for the license. +.SH "NAME" +.LP +.B nsd\-xfer +\- AXFR client to transfer zones from a name server +.SH "SYNOPSIS" +.LP +.B nsd\-xfer +.RB [ \-4 ] +.RB [ \-6 ] +.RB [ \-a +.IR address[@port] ] +.RB [ \-p +.IR port ] +.RB [ \-s +.IR serial ] +.RB [ \-T +.IR tsiginfo ] +.RB [ \-v ] +.B \-z +.I zone +.B \-f +.I file +.I servers +.SH "DESCRIPTION" +.LP +.B Nsd\-xfer +is program to transfer zones from a name server using AXFR. +.B NSD +is a complete implementation of an authoritative DNS nameserver. +.SH "OPTIONS" +.LP +.TP +.B \-4 +Only send to IPv4 addresses. +.TP +.B \-6 +Only send to IPv6 addresses. +.TP +.B \-a\fI address[@port] +Specify the source address (and port) to send from. +.TP +.B \-f\fI file +The file to store the zone in. +.TP +.B \-p\fI port +Specify the port to send to. +.TP +.B \-s\fI serial +Specify the serial of the current zone. The zone is only transferred +if the master server has a zone with a greater serial number. +.TP +.B \-T\fI tsiginfo +Use TSIG to verify the zone transfer. The +.I tsiginfo +file must contain the TSIG key information. The file is removed +upon successful reading of the key. +.TP +.B \-v +Be more verbose. +.TP +.B \-z\fI zone +Specify the zone to receive. +.TP +.I servers +List of nameservers to try. +.SH "EXAMPLES" +.LP +To run this program the standard way type: +.LP +# nsd\-xfer \-z foobar.cz \-f foobar.cz.zone 1.2.3.4 +.SH "SEE ALSO" +.LP +nsd(8), nsdc(8), nsd.conf(5), nsd-checkconf(8), +nsd-notify(8), nsd-patch(8), zonec(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. diff --git a/usr.sbin/nsd/nsd-xfer.c b/usr.sbin/nsd/nsd-xfer.c new file mode 100644 index 00000000000..a0b8b2d87c9 --- /dev/null +++ b/usr.sbin/nsd/nsd-xfer.c @@ -0,0 +1,1078 @@ +/* + * nsd-xfer.c -- nsd-xfer(8). + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <limits.h> +#include <assert.h> +#include <errno.h> +#include <netdb.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include "dname.h" +#include "dns.h" +#include "packet.h" +#include "query.h" +#include "rdata.h" +#include "region-allocator.h" +#include "tsig.h" +#include "tsig-openssl.h" +#include "util.h" +#include "zonec.h" + + +/* + * Number of seconds to wait when recieving no data from the remote + * server. + */ +#define MAX_WAITING_TIME TCP_TIMEOUT + +/* + * Exit codes are based on named-xfer for now. See ns_defs.h in + * bind8. + */ +#define XFER_UPTODATE 0 +#define XFER_SUCCESS 1 +#define XFER_FAIL 3 + +struct axfr_state +{ + int verbose; + size_t packets_received; + size_t bytes_received; + + int s; /* AXFR socket. */ + query_type *q; /* Query buffer. */ + uint16_t query_id; /* AXFR query ID. */ + tsig_record_type *tsig; /* TSIG data. */ + + int first_transfer; /* First transfer of this zone. */ + uint32_t last_serial; /* Otherwise the last serial. */ + uint32_t zone_serial; /* And the new zone serial. */ + const dname_type *zone; /* Zone name. */ + + int done; /* AXFR is complete. */ + size_t rr_count; /* Number of RRs received so far. */ + + /* + * Region used to allocate data needed to process a single RR. + */ + region_type *rr_region; + + /* + * Region used to store owner and origin of previous RR (used + * for pretty printing of zone data). + */ + struct state_pretty_rr *pretty_rr; +}; +typedef struct axfr_state axfr_state_type; + +static sig_atomic_t timeout_flag = 0; +static void to_alarm(int sig); /* our alarm() signal handler */ + +extern char *optarg; +extern int optind; + +static uint16_t init_query(query_type *q, + const dname_type *dname, + uint16_t type, + uint16_t klass, + tsig_record_type *tsig); + + +/* + * Check if two getaddrinfo result lists have records with matching + * ai_family fields. + */ +int check_matching_address_family(struct addrinfo *a, struct addrinfo *b); + +/* + * Returns the first record with ai_family == FAMILY, or NULL if no + * such record is found. + */ +struct addrinfo *find_by_address_family(struct addrinfo *addrs, int family); + +/* + * Assigns pointers to hostname and port and wipes out the optional delimiter. + */ +void get_hostname_port_frm_str(char* arg, const char** hostname, + const char** port); + +/* + * Log an error message and exit. + */ +static void error(const char *format, ...) ATTR_FORMAT(printf, 1, 2); +static void +error(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_vmsg(LOG_ERR, format, args); + va_end(args); + exit(XFER_FAIL); +} + + +/* + * Log a warning message. + */ +static void warning(const char *format, ...) ATTR_FORMAT(printf, 1, 2); +static void +warning(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_vmsg(LOG_WARNING, format, args); + va_end(args); +} + + +/* + * Display usage information and exit. + */ +static void +usage (void) +{ + fprintf(stderr, + "Usage: nsd-xfer [OPTION]... -z zone -f file server...\n" + "NSD AXFR client.\n\nSupported options:\n" + " -4 Only use IPv4 connections.\n" + " -6 Only use IPv6 connections.\n" + " -a src[@port] Local hostname/ip-address for the \ +connection, including optional source port.\n" + " -f file Output zone file name.\n" + " -p port The port to connect to.\n" + " -s serial The current zone serial.\n" + " -T tsiginfo The TSIG key file name. The file is removed " + "after reading the\n key.\n" + " -v Verbose output.\n"); + fprintf(stderr, + " -z zone Specify the name of the zone to transfer.\n" + " server The name or IP address of the master server.\n" + "\nVersion %s. Report bugs to <%s>.\n", PACKAGE_VERSION, PACKAGE_BUGREPORT); + exit(XFER_FAIL); +} + + +/* + * Signal handler for timeouts (SIGALRM). This function is called when + * the alarm() value that was set counts down to zero. This indicates + * that we haven't received a response from the server. + * + * All we do is set a flag and return from the signal handler. The + * occurrence of the signal interrupts the read() system call (errno + * == EINTR) above, and we then check the timeout_flag flag. + */ +static void +to_alarm(int ATTR_UNUSED(sig)) +{ + timeout_flag = 1; +} + +#ifdef TSIG +/* + * Read a line from IN. If successful, the line is stripped of + * leading and trailing whitespace and non-zero is returned. + */ +static int +tsig_read_line(FILE *in, char *line, size_t size) +{ + if (!fgets(line, size, in)) { + return 0; + } else { + strip_string(line); + return 1; + } +} + +static tsig_key_type * +read_tsig_key_data(region_type *region, FILE *in, + int ATTR_UNUSED(default_family), tsig_algorithm_type** tsig_algo) +{ + char line[4000]; + tsig_key_type *key = (tsig_key_type *) region_alloc( + region, sizeof(tsig_key_type)); + int size; + uint8_t algo = 0; + uint8_t data[4000]; + + /* server address */ + if (!tsig_read_line(in, line, sizeof(line))) { + error("failed to read TSIG key server address: '%s'", + strerror(errno)); + return NULL; + } + /* server address unused */ + + /* tsig keyname */ + if (!tsig_read_line(in, line, sizeof(line))) { + error("failed to read TSIG key name: '%s'", strerror(errno)); + return NULL; + } + + key->name = dname_parse(region, line); + if (!key->name) { + error("failed to parse TSIG key name '%s'", line); + return NULL; + } + + /* tsig algorithm */ + if (!tsig_read_line(in, line, sizeof(line))) { + error("failed to read TSIG key algorithm: '%s'", strerror(errno)); + return NULL; + } + algo = (uint8_t) atoi((const char*) line); + *tsig_algo = tsig_get_algorithm_by_id(algo); + if (*tsig_algo == NULL) { + error("failed to parse TSIG key algorithm %i: '%s'\n", algo, strerror(errno)); + return NULL; + } + + /* tsig secret */ + if (!tsig_read_line(in, line, sizeof(line))) { + error("failed to read TSIG key data: '%s'\n", strerror(errno)); + return NULL; + } + + size = b64_pton(line, data, sizeof(data)); + if (size == -1) { + error("failed to parse TSIG key data"); + return NULL; + } + + key->size = size; + key->data = (uint8_t *) region_alloc_init(region, data, key->size); + + return key; +} + +/* + * Read the TSIG key from a .tsiginfo file and remove the file. + */ +static tsig_key_type * +read_tsig_key(region_type *region, + const char *tsiginfo_filename, + int default_family, tsig_algorithm_type** algo) +{ + FILE *in; + tsig_key_type *key; + + in = fopen(tsiginfo_filename, "r"); + if (!in) { + error("failed to open %s: %s", + tsiginfo_filename, + strerror(errno)); + return NULL; + } + + key = read_tsig_key_data(region, in, default_family, algo); + + fclose(in); + + if (unlink(tsiginfo_filename) == -1) { + warning("failed to remove %s: %s", + tsiginfo_filename, + strerror(errno)); + } + + return key; +} +#endif /* TSIG */ + +/* + * Read SIZE bytes from the socket into BUF. Keep reading unless an + * error occurs (except for EAGAIN) or EOF is reached. + */ +static int +read_socket(int s, void *buf, size_t size) +{ + char *data = (char *) buf; + size_t total_count = 0; + + while (total_count < size) { + ssize_t count = read(s, data + total_count, size - total_count); + if (count == -1) { + /* Error or interrupt. */ + if (errno != EAGAIN) { + error("network read failed: %s", + strerror(errno)); + return 0; + } else { + continue; + } + } else if (count == 0) { + /* End of file (connection closed?) */ + error("network read failed: Connection closed by peer"); + return 0; + } + total_count += count; + } + + return 1; +} + +static int +parse_response(FILE *out, axfr_state_type *state) +{ + size_t rr_count; + size_t qdcount = QDCOUNT(state->q->packet); + size_t ancount = ANCOUNT(state->q->packet); + + /* Skip question section. */ + for (rr_count = 0; rr_count < qdcount; ++rr_count) { + if (!packet_skip_rr(state->q->packet, 1)) { + error("bad RR in question section"); + return 0; + } + } + + /* Read RRs from answer section and print them. */ + for (rr_count = 0; rr_count < ancount; ++rr_count) { + domain_table_type *owners + = domain_table_create(state->rr_region); + rr_type *record = packet_read_rr( + state->rr_region, owners, state->q->packet, 0); + if (!record) { + error("bad RR in answer section"); + return 0; + } + + if (state->rr_count == 0 + && (record->type != TYPE_SOA || record->klass != CLASS_IN)) + { + error("First RR must be the SOA record, but is a %s record", + rrtype_to_string(record->type)); + return 0; + } else if (state->rr_count > 0 + && record->type == TYPE_SOA + && record->klass == CLASS_IN) + { + state->done = 1; + return 1; + } + + ++state->rr_count; + + if (!print_rr(out, state->pretty_rr, record)) { + return 0; + } + + region_free_all(state->rr_region); + } + + return 1; +} + +static int +send_query(int s, query_type *q) +{ + uint16_t size = htons(buffer_remaining(q->packet)); + + if (!write_socket(s, &size, sizeof(size))) { + error("network write failed: %s", strerror(errno)); + return 0; + } + if (!write_socket(s, buffer_begin(q->packet), buffer_limit(q->packet))) + { + error("network write failed: %s", strerror(errno)); + return 0; + } + return 1; +} + +static int +receive_response_no_timeout(axfr_state_type *state) +{ + uint16_t size; + + buffer_clear(state->q->packet); + if (!read_socket(state->s, &size, sizeof(size))) { + return 0; + } + size = ntohs(size); + if (size > state->q->maxlen) { + error("response size (%d) exceeds maximum (%d)", + (int) size, (int) state->q->maxlen); + return 0; + } + if (!read_socket(state->s, buffer_begin(state->q->packet), size)) { + return 0; + } + + buffer_set_position(state->q->packet, size); + + ++state->packets_received; + state->bytes_received += sizeof(size) + size; + + return 1; +} + +static int +receive_response(axfr_state_type *state) +{ + int result; + + timeout_flag = 0; + alarm(MAX_WAITING_TIME); + result = receive_response_no_timeout(state); + alarm(0); + if (!result && timeout_flag) { + error("timeout reading response, server unreachable?"); + } + + return result; +} + +static int +check_response_tsig(query_type *q, tsig_record_type *tsig) +{ + if (!tsig) + return 1; + + if (!tsig_find_rr(tsig, q->packet)) { + error("error parsing response"); + return 0; + } + if (tsig->status == TSIG_NOT_PRESENT) { + if (tsig->response_count == 0) { + error("required TSIG not present"); + return 0; + } + if (tsig->updates_since_last_prepare > 100) { + error("too many response packets without TSIG"); + return 0; + } + tsig_update(tsig, q->packet, buffer_limit(q->packet)); + return 1; + } + + ARCOUNT_SET(q->packet, ARCOUNT(q->packet) - 1); + + if (tsig->status == TSIG_ERROR) { + error("TSIG record is not correct"); + return 0; + } else if (tsig->error_code != TSIG_ERROR_NOERROR) { + error("TSIG error code: %s", + tsig_error(tsig->error_code)); + return 0; + } else { + tsig_update(tsig, q->packet, tsig->position); + if (!tsig_verify(tsig)) { + error("TSIG record did not authenticate"); + return 0; + } + tsig_prepare(tsig); + } + + return 1; +} + + +/* + * Query the server for the zone serial. Return 1 if the zone serial + * is higher than the current serial, 0 if the zone serial is lower or + * equal to the current serial, and -1 on error. + * + * On success, the zone serial is returned in ZONE_SERIAL. + */ +static int +check_serial(axfr_state_type *state) +{ + region_type *local; + uint16_t query_id; + uint16_t i; + domain_table_type *owners; + + query_id = init_query( + state->q, state->zone, TYPE_SOA, CLASS_IN, state->tsig); + + if (!send_query(state->s, state->q)) { + return -1; + } + + if (state->tsig) { + /* Prepare for checking responses. */ + tsig_prepare(state->tsig); + } + + if (!receive_response(state)) { + return -1; + } + buffer_flip(state->q->packet); + + if (buffer_limit(state->q->packet) <= QHEADERSZ) { + error("response size (%d) is too small", + (int) buffer_limit(state->q->packet)); + return -1; + } + + if (!QR(state->q->packet)) { + error("response is not a response"); + return -1; + } + + if (TC(state->q->packet)) { + error("response is truncated"); + return -1; + } + + if (ID(state->q->packet) != query_id) { + error("bad response id (%d), expected (%d)", + (int) ID(state->q->packet), (int) query_id); + return -1; + } + + if (RCODE(state->q->packet) != RCODE_OK) { + error("error response %d (%s)", (int) RCODE(state->q->packet), + rcode2str((int) RCODE(state->q->packet))); + return -1; + } + + if (QDCOUNT(state->q->packet) != 1) { + error("question section count not equal to 1"); + return -1; + } + + if (ANCOUNT(state->q->packet) == 0) { + error("answer section is empty"); + return -1; + } + + if (!check_response_tsig(state->q, state->tsig)) { + return -1; + } + + buffer_set_position(state->q->packet, QHEADERSZ); + + local = region_create(xalloc, free); + owners = domain_table_create(local); + + /* Skip question records. */ + for (i = 0; i < QDCOUNT(state->q->packet); ++i) { + rr_type *record + = packet_read_rr(local, owners, state->q->packet, 1); + if (!record) { + error("bad RR in question section"); + region_destroy(local); + return -1; + } + + if (dname_compare(state->zone, domain_dname(record->owner)) != 0 + || record->type != TYPE_SOA + || record->klass != CLASS_IN) + { + error("response does not match query"); + region_destroy(local); + return -1; + } + } + + /* Find the SOA record in the response. */ + for (i = 0; i < ANCOUNT(state->q->packet); ++i) { + rr_type *record + = packet_read_rr(local, owners, state->q->packet, 0); + if (!record) { + error("bad RR in answer section"); + region_destroy(local); + return -1; + } + + if (dname_compare(state->zone, domain_dname(record->owner)) == 0 + && record->type == TYPE_SOA + && record->klass == CLASS_IN) + { + assert(record->rdata_count == 7); + assert(rdata_atom_size(record->rdatas[2]) == 4); + state->zone_serial = read_uint32( + rdata_atom_data(record->rdatas[2])); + region_destroy(local); + return (state->first_transfer + || compare_serial(state->zone_serial, + state->last_serial) > 0); + } + } + + error("SOA not found in answer"); + region_destroy(local); + return -1; +} + +/* + * Receive and parse the AXFR response packets. + */ +static int +handle_axfr_response(FILE *out, axfr_state_type *axfr) +{ + while (!axfr->done) { + if (!receive_response(axfr)) { + return 0; + } + + buffer_flip(axfr->q->packet); + + if (buffer_limit(axfr->q->packet) <= QHEADERSZ) { + error("response size (%d) is too small", + (int) buffer_limit(axfr->q->packet)); + return 0; + } + + if (!QR(axfr->q->packet)) { + error("response is not a response"); + return 0; + } + + if (ID(axfr->q->packet) != axfr->query_id) { + error("bad response id (%d), expected (%d)", + (int) ID(axfr->q->packet), + (int) axfr->query_id); + return 0; + } + + if (RCODE(axfr->q->packet) != RCODE_OK) { + error("error response %d (%s)", (int) RCODE(axfr->q->packet), + rcode2str((int) RCODE(axfr->q->packet))); + return 0; + } + + if (QDCOUNT(axfr->q->packet) > 1) { + error("query section count greater than 1"); + return 0; + } + + if (ANCOUNT(axfr->q->packet) == 0) { + error("answer section is empty"); + return 0; + } + + if (!check_response_tsig(axfr->q, axfr->tsig)) { + return 0; + } + + buffer_set_position(axfr->q->packet, QHEADERSZ); + + if (!parse_response(out, axfr)) { + return 0; + } + } + return 1; +} + +static int +axfr(FILE *out, axfr_state_type *state, const char *server) +{ + state->query_id = init_query( + state->q, state->zone, TYPE_AXFR, CLASS_IN, state->tsig); + + log_msg(LOG_INFO, + "send AXFR query to %s for %s", + server, + dname_to_string(state->zone, NULL)); + + if (!send_query(state->s, state->q)) { + return 0; + } + + if (state->tsig) { + /* Prepare for checking responses. */ + tsig_prepare(state->tsig); + } + + return handle_axfr_response(out, state); +} + +static uint16_t +init_query(query_type *q, + const dname_type *dname, + uint16_t type, + uint16_t klass, + tsig_record_type *tsig) +{ + uint16_t query_id = (uint16_t) random(); + + buffer_clear(q->packet); + + /* Set up the header */ + ID_SET(q->packet, query_id); + FLAGS_SET(q->packet, 0); + OPCODE_SET(q->packet, OPCODE_QUERY); + AA_SET(q->packet); + QDCOUNT_SET(q->packet, 1); + ANCOUNT_SET(q->packet, 0); + NSCOUNT_SET(q->packet, 0); + ARCOUNT_SET(q->packet, 0); + buffer_skip(q->packet, QHEADERSZ); + + /* The question record. */ + buffer_write(q->packet, dname_name(dname), dname->name_size); + buffer_write_u16(q->packet, type); + buffer_write_u16(q->packet, klass); + + if (tsig) { + tsig_init_query(tsig, query_id); + tsig_prepare(tsig); + tsig_update(tsig, q->packet, buffer_position(q->packet)); + tsig_sign(tsig); + tsig_append_rr(tsig, q->packet); + ARCOUNT_SET(q->packet, 1); + } + + buffer_flip(q->packet); + + return ID(q->packet); +} + +static void +print_zone_header(FILE *out, axfr_state_type *state, const char *server) +{ + time_t now = time(NULL); + fprintf(out, "; NSD version %s\n", PACKAGE_VERSION); + fprintf(out, "; zone '%s'", dname_to_string(state->zone, NULL)); + if (state->first_transfer) { + fprintf(out, " first transfer\n"); + } else { + fprintf(out, + " last serial %lu\n", + (unsigned long) state->last_serial); + } + fprintf(out, "; from %s using AXFR at %s", server, ctime(&now)); + if (state->tsig) { + fprintf(out, "; TSIG verified with key '%s'\n", + dname_to_string(state->tsig->key->name, NULL)); + } else { + fprintf(out, "; NOT TSIG verified\n"); + } +} + +static void +print_stats(axfr_state_type *state) +{ + log_msg(LOG_INFO, + "received %lu RRs in %lu bytes (using %lu response packets)", + (unsigned long) state->rr_count, + (unsigned long) state->bytes_received, + (unsigned long) state->packets_received); +} + +int +main(int argc, char *argv[]) +{ + region_type *region = region_create(xalloc, free); + int c; + query_type q; + struct addrinfo hints, *res0, *res; + const char *zone_filename = NULL; + const char *local_hostname = NULL; + struct addrinfo *local_address, *local_addresses = NULL; + const char *port = TCP_PORT; + const char *local_port = NULL; + int default_family = DEFAULT_AI_FAMILY; + struct sigaction mysigaction; + FILE *zone_file; +#ifdef TSIG + const char *tsig_key_filename = NULL; + tsig_key_type *tsig_key = NULL; +#endif /* TSIG */ + axfr_state_type state; + + log_init("nsd-xfer"); + + /* Initialize the query. */ + memset(&q, 0, sizeof(query_type)); + q.region = region; + q.addrlen = sizeof(q.addr); + q.packet = buffer_create(region, QIOBUFSZ); + q.maxlen = TCP_MAX_MESSAGE_LEN; + + /* Initialize the state. */ + state.verbose = 0; + state.packets_received = 0; + state.bytes_received = 0; + state.q = &q; + state.tsig = NULL; + state.zone = NULL; + state.first_transfer = 1; + state.done = 0; + state.rr_count = 0; + state.rr_region = region_create(xalloc, free); + state.pretty_rr = create_pretty_rr(region); + + region_add_cleanup(region, cleanup_region, state.rr_region); + + srandom((unsigned long) getpid() * (unsigned long) time(NULL)); + + if (!tsig_init(region)) { + error("TSIG initialization failed"); + } + + /* Parse the command line... */ + while ((c = getopt(argc, argv, "46a:f:hp:s:T:vz:")) != -1) { + switch (c) { + case '4': + default_family = AF_INET; + break; + case '6': +#ifdef INET6 + default_family = AF_INET6; +#else /* !INET6 */ + error("IPv6 support not enabled."); +#endif /* !INET6 */ + break; + case 'a': + get_hostname_port_frm_str((char *) optarg, + &local_hostname, &local_port); + break; + case 'f': + zone_filename = optarg; + break; + case 'h': + usage(); + break; + case 'p': + port = optarg; + break; + case 's': { + uint32_t v; + const char *t; + state.first_transfer = 0; + v = strtoserial(optarg, &t); + if (optarg[0] == '\0' || *t != '\0') + { + error("bad serial '%s'", optarg); + exit(XFER_FAIL); + } + state.last_serial = v; + break; + } + case 'T': +#ifdef TSIG + tsig_key_filename = optarg; + break; +#else + log_msg(LOG_ERR, "option -T given but TSIG not enabled"); +#endif /* TSIG */ + case 'v': + ++state.verbose; + break; + case 'z': + state.zone = dname_parse(region, optarg); + if (!state.zone) { + error("incorrect domain name '%s'", optarg); + } + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 0 || !zone_filename || !state.zone) + usage(); + +#ifdef TSIG + if (tsig_key_filename) { + tsig_algorithm_type *tsig_algo = NULL; + tsig_key = read_tsig_key( + region, tsig_key_filename, default_family, &tsig_algo); + if (!tsig_key) { + error("cannot initialize TSIG: error in tsiginfo file"); + exit(XFER_FAIL); + } + + tsig_add_key(tsig_key); + + state.tsig = (tsig_record_type *) region_alloc( + region, sizeof(tsig_record_type)); + tsig_create_record(state.tsig, region); + tsig_init_record(state.tsig, tsig_algo, tsig_key); + } +#endif /* TSIG */ + + mysigaction.sa_handler = to_alarm; + sigfillset(&mysigaction.sa_mask); + mysigaction.sa_flags = 0; + if (sigaction(SIGALRM, &mysigaction, NULL) < 0) { + error("cannot set signal handler"); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = default_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (local_hostname) { + int rc = getaddrinfo(local_hostname, local_port, + &hints, &local_addresses); + if (rc) { + error("local hostname '%s' not found: %s", + local_hostname, gai_strerror(rc)); + } + } + + for (/*empty*/; *argv; ++argv) { + /* Try each server separately until one succeeds. */ + int rc; + + rc = getaddrinfo(*argv, port, &hints, &res0); + if (rc) { + warning("skipping bad address %s: %s\n", *argv, + gai_strerror(rc)); + continue; + } + + if (local_addresses + && !check_matching_address_family(res0, local_addresses)) + { + warning("no local address family matches remote " + "address family, skipping server '%s'", + *argv); + continue; + } + + for (res = res0; res; res = res->ai_next) { + if (res->ai_addrlen > sizeof(q.addr)) + continue; + + /* + * If a local address is specified, use an + * address with the same family as the remote + * address. + */ + local_address = find_by_address_family(local_addresses, + res->ai_family); + if (local_addresses && !local_address) { + /* Continue with next remote address. */ + continue; + } + + state.s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (state.s == -1) { + warning("cannot create socket: %s\n", + strerror(errno)); + continue; + } + + /* Bind socket to local address, if required. */ + if (local_address && bind(state.s, + local_address->ai_addr, + local_address->ai_addrlen) < 0) + { + warning("cannot bind to %s: %s\n", + local_hostname, strerror(errno)); + } + + if (connect(state.s, res->ai_addr, res->ai_addrlen) < 0) + { + warning("cannot connect to %s: %s\n", + *argv, + strerror(errno)); + close(state.s); + continue; + } + + memcpy(&q.addr, res->ai_addr, res->ai_addrlen); + + rc = check_serial(&state); + if (rc == -1) { + close(state.s); + continue; + } + if (rc == 0) { + /* Zone is up-to-date. */ + close(state.s); + exit(XFER_UPTODATE); + } else if (rc > 0) { + zone_file = fopen(zone_filename, "w"); + if (!zone_file) { + error("cannot open or create zone file '%s' for writing: %s", + zone_filename, strerror(errno)); + close(state.s); + exit(XFER_FAIL); + } + + print_zone_header(zone_file, &state, *argv); + + if (axfr(zone_file, &state, *argv)) { + /* AXFR succeeded, done. */ + fclose(zone_file); + close(state.s); + + if (state.verbose > 0) { + print_stats(&state); + } + + exit(XFER_SUCCESS); + } + + fclose(zone_file); + } + + close(state.s); + } + + freeaddrinfo(res0); + } + + log_msg(LOG_ERR, + "cannot contact an authoritative server, zone NOT transferred"); + exit(XFER_FAIL); +} + +void +get_hostname_port_frm_str(char* arg, const char** hostname, + const char** port) +{ + /* parse -a src[@port] option */ + char* delim = strchr(arg, '@'); + + if (delim) { + *delim = '\0'; + *port = delim+1; + } + *hostname = arg; +} + + +int +check_matching_address_family(struct addrinfo *a0, struct addrinfo *b0) +{ + struct addrinfo *a; + struct addrinfo *b; + + for (a = a0; a; a = a->ai_next) { + for (b = b0; b; b = b->ai_next) { + if (a->ai_family == b->ai_family) { + return 1; + } + } + } + return 0; +} + +struct addrinfo * +find_by_address_family(struct addrinfo *addrs, int family) +{ + for (; addrs; addrs = addrs->ai_next) { + if (addrs->ai_family == family) { + return addrs; + } + } + return NULL; +} diff --git a/usr.sbin/nsd/nsd.8 b/usr.sbin/nsd/nsd.8 new file mode 100644 index 00000000000..ba1488b9156 --- /dev/null +++ b/usr.sbin/nsd/nsd.8 @@ -0,0 +1,266 @@ +.TH "NSD" "8" "Jan 6, 2010" "NLnet Labs" "NSD 3.2.4" +.\" 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.4. +.SH "SYNOPSIS" +.LP +.B nsd +.RB [ \-4 ] +.RB [ \-6 ] +.RB [ \-a +.IR ip\-address ] +.RB [ \-c +.IR configfile ] +.RB [ \-d ] +.RB [ \-f +.IR database ] +.RB [ \-h ] +.RB [ \-i +.IR identity ] +.RB [ \-I +.IR nsid ] +.RB [ \-l +.IR logfile ] +.RB [ \-N +.IR server\-count ] +.RB [ \-n +.IR noncurrent\-tcp\-count ] +.RB [ \-P +.IR pidfile ] +.RB [ \-p +.IR port ] +.RB [ \-s +.IR seconds ] +.RB [ \-t +.IR chrootdir ] +.RB [ \-u +.IR username ] +.RB [ \-V +.IR level ] +.RB [ \-v ] +.SH "DESCRIPTION" +.LP +.B NSD +is a complete implementation of an authoritative DNS nameserver. +Upon startup, +.B NSD +will read the database specified with +.B \-f +.I database +argument and put itself into background and answers queries on port +53 or a different port specified with +.B \-p +.I port +option. The +.I database +must be generated beforehand with zonec(8). By default, +.B NSD +will bind to all local interfaces available. Use the +.B \-a +.I ip\-address +option to specify a single particular interface address to be +bound. If this option is given more than once, +.B NSD +will bind its UDP and TCP sockets to all the specified ip\-addresses +separately. If IPv6 is enabled when +.B NSD +is compiled an IPv6 address can also be specified. +.P +.SH "OPTIONS" +.LP +All the options can be specified in the configfile ( +.B \-c +argument), except for the +.B \-v +and +.B \-h +options. If options are specified on the commandline, the options +on the commandline take precedence over the options in the +configfile. +.P +Normally +.B NSD +should be started with the `nsdc(8) start` command invoked from a +.I /etc/rc.d/nsd.sh +script or similar at the operating system startup. +.TP +.B \-4 +Only listen to IPv4 connections. +.TP +.B \-6 +Only listen to IPv6 connections. +.TP +.B \-a\fI ip\-address +Listen to the specified +.IR ip\-address . +The +.I ip\-address +must be specified in numeric format (using the standard IPv4 or IPv6 +notation). This flag can be specified multiple times to listen to +multiple IP addresses. If this flag is not specified, +.B NSD +listens to all IP addresses. +.TP +.B \-c\fI configfile +Read specified +.I configfile instead of the default +.IR /etc/nsd/nsd.conf . +For format description see nsd.conf(5). +.TP +.B \-d +Turn on debugging mode, do not fork, stay in the foreground. +.TP +.B \-f\fI database +Use the specified +.I database +instead of the default of +.IR /var/db/nsd/nsd.db . +If a +.B zonesdir: +is specified in the config file this path can be relative to that +directory. +.TP +.B \-h +Print help information and exit. +.TP +.B \-i\fI identity +Return the specified +.I identity +when asked for +.I CH TXT ID.SERVER +(This option is used to determine which server is answering the queries +when they are multicast). The default is the name returned by +gethostname(3). +.TP +.B \-I\fI nsid +Add the specified +.I nsid +to the EDNS section of the answer when queried with an NSID EDNS +enabled packet. This is disabled until IANA has given the NSID +option an OPCODE. +.TP +.B \-l\fI logfile +Log messages to the specified +.IR logfile . +The default is to log to stderr and syslog. If a +.B zonesdir: +is specified in the config file this path can be relative to that +directory. +.TP +.B \-N\fI count +Start +.I count +.B NSD +servers. The default is 1. Starting more than a single server is +only useful on machines with multiple CPUs and/or network adapters. +.TP +.B \-n\fI number +The maximum +.I number +of concurrent TCP connection that can be handled by each server. The +default is 10. +.TP +.B \-P\fI pidfile +Use the specified +.I pidfile +instead of the platform specific default, which is mostly +.IR /var/run/nsd.pid . +If a +.B zonesdir: +is specified in the config file, this path can be relative to that +directory. +.TP +.B \-p\fI port +Answer the queries on the specified +.IR port . +Normally this is port 53. +.TP +.B \-s\fI seconds +.It Fl s Ar seconds +Produce statistics dump every +.I seconds +seconds. This is equal to sending +.I SIGUSR1 +to the daemon periodically. +.TP +.B \-t\fI chroot +Specifies a directory to +.I chroot +to upon startup. This option requires you to ensure that appropriate +syslogd(8) socket (e.g. +.I chrootdir +/dev/log) is available, otherwise +.B NSD +won't produce any log output. +.TP +.B \-u\fI username +Drop user and group privileges to those of +.I username +after binding the socket. +The +.I username +must be one of: username, id, or id.gid. For example: nsd, 80, or +80.80. +.TP +.B \-V\fI level +This value specifies the verbosity level for (non\-debug) logging. +Default is 0. +.TP +.B \-v +Print the version number of +.B NSD +to standard error and exit. +.LP +.B NSD +reacts to the following signals: +.TP +SIGTERM +Stop answering queries, shutdown, and exit normally. +.TP +SIGHUP +Reload the database. +.TP +SIGUSR1 +Dump BIND8\-style statistics into the log. Ignored otherwise. +.SH "FILES" +.TP +/var/db/nsd/nsd.db +default +.B NSD +database +.TP +/var/run/nsd.pid +the process id of the name server. +.TP +/etc/nsd/nsd.conf +default +.B NSD +configuration file +.SH "DIAGNOSTICS" +.LP +will log all the problems via the standard syslog(8) +.I daemon +facility, unless the +.B \-d +option is specified. +.SH "SEE ALSO" +.LP +nsdc(8), nsd.conf(5), nsd\-checkconf(8), nsd\-notify(8), +nsd\-patch(8), nsd\-xfer(8), zonec(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 zonec(8). Therefore problems with +misconfigured master zone files or 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 new file mode 100644 index 00000000000..708d897fcfd --- /dev/null +++ b/usr.sbin/nsd/nsd.c @@ -0,0 +1,1043 @@ +/* + * nsd.c -- nsd(8) + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#ifdef HAVE_GRP_H +#include <grp.h> +#endif /* HAVE_GRP_H */ +#ifdef HAVE_SETUSERCONTEXT +#include <login_cap.h> +#endif /* HAVE_SETUSERCONTEXT */ + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <netdb.h> +#include <pwd.h> +#include <signal.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "nsd.h" +#include "options.h" +#ifdef TSIG +#include "tsig.h" +#endif /* TSIG */ + +/* The server handler... */ +static struct nsd nsd; +static char hostname[MAXHOSTNAMELEN]; + +static void error(const char *format, ...) ATTR_FORMAT(printf, 1, 2); + +/* + * Print the help text. + * + */ +static void +usage (void) +{ + fprintf(stderr, "Usage: nsd [OPTION]...\n"); + fprintf(stderr, "Name Server Daemon.\n\n"); + fprintf(stderr, + "Supported options:\n" + " -4 Only listen to IPv4 connections.\n" + " -6 Only listen to IPv6 connections.\n" + " -a ip-address Listen to the specified incoming IP address (may be\n" + " 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" +#ifndef NDEBUG + " -F facilities Specify the debug facilities.\n" +#endif /* NDEBUG */ + " -f database Specify the database to load.\n" + " -h Print this help information.\n" + , CONFIGFILE); + fprintf(stderr, + " -i identity Specify the identity when queried for id.server CHAOS TXT.\n" +#ifdef NSID + " -I nsid Specify the NSID. This must be a hex string.\n" +#endif /* NSID */ +#ifndef NDEBUG + " -L level Specify the debug level.\n" +#endif /* NDEBUG */ + " -l filename Specify the log file.\n" + " -N server-count The number of servers to start.\n" + " -n tcp-count The maximum number of TCP connections per server.\n" + " -P pidfile Specify the PID file to write.\n" + " -p port Specify the port to listen to.\n" + " -s seconds Dump statistics every SECONDS seconds.\n" + " -t chrootdir Change root to specified directory on startup.\n" + ); + fprintf(stderr, + " -u user Change effective uid to the specified user.\n" + " -V level Specify verbosity level.\n" + " -v Print version information.\n" + ); + fprintf(stderr, "Version %s. Report bugs to <%s>.\n", + PACKAGE_VERSION, PACKAGE_BUGREPORT); +} + +/* + * Print the version exit. + * + */ +static void +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-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); +} + +/* + * Something went wrong, give error messages and exit. + * + */ +static void +error(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_vmsg(LOG_ERR, format, args); + va_end(args); + exit(1); +} + +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 */ +} + + +/* + * Fetch the nsd parent process id from the nsd pidfile + * + */ +pid_t +readpid(const char *file) +{ + int fd; + pid_t pid; + char pidbuf[16]; + char *t; + int l; + + if ((fd = open(file, O_RDONLY)) == -1) { + return -1; + } + + if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) { + close(fd); + return -1; + } + + close(fd); + + /* Empty pidfile means no pidfile... */ + if (l == 0) { + errno = ENOENT; + return -1; + } + + pid = strtol(pidbuf, &t, 10); + + if (*t && *t != '\n') { + return -1; + } + return pid; +} + +/* + * Store the nsd parent process id in the nsd pidfile + * + */ +int +writepid(struct nsd *nsd) +{ + FILE * fd; + char pidbuf[32]; + + snprintf(pidbuf, sizeof(pidbuf), "%lu\n", (unsigned long) nsd->pid); + + if ((fd = fopen(nsd->pidfile, "w")) == NULL ) { + log_msg(LOG_ERR, "cannot open pidfile %s: %s", + nsd->pidfile, strerror(errno)); + return -1; + } + + if (!write_data(fd, pidbuf, strlen(pidbuf))) { + log_msg(LOG_ERR, "cannot write pidfile %s: %s", + nsd->pidfile, strerror(errno)); + fclose(fd); + return -1; + } + fclose(fd); + + if (chown(nsd->pidfile, nsd->uid, nsd->gid) == -1) { + log_msg(LOG_ERR, "cannot chown %u.%u %s: %s", + (unsigned) nsd->uid, (unsigned) nsd->gid, + nsd->pidfile, strerror(errno)); + return -1; + } + + return 0; +} + +void +unlinkpid(const char* file) +{ + if (file && unlink(file) == -1) + log_msg(LOG_ERR, "failed to unlink pidfile %s: %s", + file, strerror(errno)); +} + +/* + * Incoming signals, set appropriate actions. + * + */ +void +sig_handler(int sig) +{ + /* To avoid race cond. We really don't want to use log_msg() in this handler */ + + /* Are we a child server? */ + if (nsd.server_kind != NSD_SERVER_MAIN) { + switch (sig) { + case SIGCHLD: + nsd.signal_hint_child = 1; + break; + case SIGALRM: + break; + case SIGINT: + case SIGTERM: + nsd.signal_hint_quit = 1; + break; + case SIGILL: + case SIGUSR1: /* Dump stats on SIGUSR1. */ + nsd.signal_hint_statsusr = 1; + break; + default: + break; + } + return; + } + + /* We are the main process */ + switch (sig) { + case SIGCHLD: + nsd.signal_hint_child = 1; + return; + case SIGHUP: + nsd.signal_hint_reload = 1; + return; + case SIGALRM: + nsd.signal_hint_stats = 1; + break; + case SIGILL: + /* + * For backwards compatibility with BIND 8 and older + * versions of NSD. + */ + nsd.signal_hint_statsusr = 1; + break; + case SIGUSR1: + /* Dump statistics. */ + nsd.signal_hint_statsusr = 1; + break; + case SIGINT: + /* Silent shutdown... */ + nsd.signal_hint_quit = 1; + break; + case SIGTERM: + default: + nsd.signal_hint_shutdown = 1; + break; + } +} + +/* + * Statistic output... + * + */ +#ifdef BIND8_STATS +void +bind8_stats (struct nsd *nsd) +{ + char buf[MAXSYSLOGMSGLEN]; + char *msg, *t; + int i, len; + + /* Current time... */ + time_t now; + time(&now); + + /* NSTATS */ + t = msg = buf + snprintf(buf, MAXSYSLOGMSGLEN, "NSTATS %lu %lu", + (unsigned long) now, (unsigned long) nsd->st.boot); + for (i = 0; i <= 255; i++) { + /* How much space left? */ + if ((len = buf + MAXSYSLOGMSGLEN - t) < 32) { + log_msg(LOG_INFO, "%s", buf); + t = msg; + len = buf + MAXSYSLOGMSGLEN - t; + } + + if (nsd->st.qtype[i] != 0) { + t += snprintf(t, len, " %s=%lu", rrtype_to_string(i), nsd->st.qtype[i]); + } + } + if (t > msg) + log_msg(LOG_INFO, "%s", buf); + + /* XSTATS */ + /* Only print it if we're in the main daemon or have anything to report... */ + if (nsd->server_kind == NSD_SERVER_MAIN + || nsd->st.dropped || nsd->st.raxfr || (nsd->st.qudp + nsd->st.qudp6 - nsd->st.dropped) + || nsd->st.txerr || nsd->st.opcode[OPCODE_QUERY] || nsd->st.opcode[OPCODE_IQUERY] + || nsd->st.wrongzone || nsd->st.ctcp + nsd->st.ctcp6 || nsd->st.rcode[RCODE_SERVFAIL] + || nsd->st.rcode[RCODE_FORMAT] || nsd->st.nona || nsd->st.rcode[RCODE_NXDOMAIN] + || nsd->st.opcode[OPCODE_UPDATE]) { + + log_msg(LOG_INFO, "XSTATS %lu %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", + (unsigned long) now, (unsigned long) nsd->st.boot, + nsd->st.dropped, (unsigned long)0, (unsigned long)0, (unsigned long)0, (unsigned long)0, + (unsigned long)0, (unsigned long)0, nsd->st.raxfr, (unsigned long)0, (unsigned long)0, + (unsigned long)0, nsd->st.qudp + nsd->st.qudp6 - nsd->st.dropped, (unsigned long)0, + (unsigned long)0, nsd->st.txerr, + nsd->st.opcode[OPCODE_QUERY], nsd->st.opcode[OPCODE_IQUERY], nsd->st.wrongzone, + (unsigned long)0, nsd->st.ctcp + nsd->st.ctcp6, + (unsigned long)0, nsd->st.rcode[RCODE_SERVFAIL], nsd->st.rcode[RCODE_FORMAT], + nsd->st.nona, nsd->st.rcode[RCODE_NXDOMAIN], + (unsigned long)0, (unsigned long)0, (unsigned long)0, nsd->st.opcode[OPCODE_UPDATE]); + } + +} +#endif /* BIND8_STATS */ + +extern char *optarg; +extern int optind; + +int +main(int argc, char *argv[]) +{ + /* Scratch variables... */ + int c; + pid_t oldpid; + size_t i; + struct sigaction action; + FILE* dbfd; +#ifdef HAVE_GETPWNAM + struct passwd *pwd; +#endif /* HAVE_GETPWNAM */ + + /* For initialising the address info structures */ + struct addrinfo hints[MAX_INTERFACES]; + const char *nodes[MAX_INTERFACES]; + const char *udp_port = 0; + const char *tcp_port = 0; + + const char *configfile = CONFIGFILE; + + char* argv0 = (argv0 = strrchr(argv[0], '/')) ? argv0 + 1 : argv[0]; + + log_init("nsd"); + + /* Initialize the server handler... */ + memset(&nsd, 0, sizeof(struct nsd)); + nsd.region = region_create(xalloc, free); + nsd.dbfile = 0; + nsd.pidfile = 0; + nsd.server_kind = NSD_SERVER_MAIN; + + for (i = 0; i < MAX_INTERFACES; i++) { + memset(&hints[i], 0, sizeof(hints[i])); + hints[i].ai_family = DEFAULT_AI_FAMILY; + hints[i].ai_flags = AI_PASSIVE; + nodes[i] = NULL; + } + + nsd.identity = 0; + nsd.version = VERSION; + nsd.username = 0; + nsd.chrootdir = 0; + nsd.nsid = NULL; + nsd.nsid_len = 0; + + nsd.child_count = 0; + nsd.maximum_tcp_count = 0; + nsd.current_tcp_count = 0; + nsd.grab_ip6_optional = 0; + nsd.file_rotation_ok = 0; + + /* Set up our default identity to gethostname(2) */ + if (gethostname(hostname, MAXHOSTNAMELEN) == 0) { + nsd.identity = hostname; + } else { + log_msg(LOG_ERR, + "failed to get the host name: %s - using default identity", + strerror(errno)); + nsd.identity = IDENTITY; + } + + + /* Parse the command line... */ + while ((c = getopt(argc, argv, "46a:c:df:hi:I:l:N:n:P:p:s:u:t:X:V:v" +#ifndef NDEBUG /* <mattthijs> only when configured with --enable-checking */ + "F:L:" +#endif /* NDEBUG */ + )) != -1) { + switch (c) { + case '4': + for (i = 0; i < MAX_INTERFACES; ++i) { + hints[i].ai_family = AF_INET; + } + break; + case '6': +#ifdef INET6 + for (i = 0; i < MAX_INTERFACES; ++i) { + hints[i].ai_family = AF_INET6; + } +#else /* !INET6 */ + error("IPv6 support not enabled."); +#endif /* INET6 */ + break; + case 'a': + if (nsd.ifs < MAX_INTERFACES) { + nodes[nsd.ifs] = optarg; + ++nsd.ifs; + } else { + error("too many interfaces ('-a') specified."); + } + break; + case 'c': + configfile = optarg; + break; + case 'd': + nsd.debug = 1; + break; + case 'f': + nsd.dbfile = optarg; + break; + case 'h': + usage(); + exit(0); + case 'i': + nsd.identity = optarg; + break; + case 'I': +#ifdef NSID + if (nsd.nsid_len != 0) { + /* can only be given once */ + break; + } + if (strlen(optarg) % 2 != 0) { + error("the NSID must be a hex string of an even length."); + } + nsd.nsid = xalloc(strlen(optarg) / 2); + nsd.nsid_len = strlen(optarg) / 2; + if (hex_pton(optarg, nsd.nsid, nsd.nsid_len) == -1) { + error("hex string cannot be parsed '%s' in NSID.", optarg); + } +#endif /* NSID */ + break; + case 'l': + nsd.log_filename = optarg; + break; + case 'N': + i = atoi(optarg); + if (i <= 0) { + error("number of child servers must be greather than zero."); + } else { + nsd.child_count = i; + } + break; + case 'n': + i = atoi(optarg); + if (i <= 0) { + error("number of concurrent TCP connections must greater than zero."); + } else { + nsd.maximum_tcp_count = i; + } + break; + case 'P': + nsd.pidfile = optarg; + break; + case 'p': + if (atoi(optarg) == 0) { + error("port argument must be numeric."); + } + tcp_port = optarg; + udp_port = optarg; + break; + case 's': +#ifdef BIND8_STATS + nsd.st.period = atoi(optarg); +#else /* !BIND8_STATS */ + error("BIND 8 statistics not enabled."); +#endif /* BIND8_STATS */ + break; + case 't': +#ifdef HAVE_CHROOT + nsd.chrootdir = optarg; +#else /* !HAVE_CHROOT */ + error("chroot not supported on this platform."); +#endif /* HAVE_CHROOT */ + break; + case 'u': + nsd.username = optarg; + break; + case 'V': + verbosity = atoi(optarg); + break; + case 'v': + version(); + /* version exits */ +#ifndef NDEBUG + case 'F': + sscanf(optarg, "%x", &nsd_debug_facilities); + break; + case 'L': + sscanf(optarg, "%d", &nsd_debug_level); + break; +#endif /* NDEBUG */ + case '?': + default: + usage(); + exit(1); + } + } + argc -= optind; + argv += optind; + + /* Commandline parse error */ + if (argc != 0) { + usage(); + exit(1); + } + + if (strlen(nsd.identity) > UCHAR_MAX) { + error("server identity too long (%u characters)", + (unsigned) strlen(nsd.identity)); + } + + /* Read options */ + nsd.options = nsd_options_create(region_create(xalloc, free)); + if(!parse_options_file(nsd.options, configfile)) { + error("could not read config: %s\n", configfile); + } + if(nsd.options->ip4_only) { + for (i = 0; i < MAX_INTERFACES; ++i) { + hints[i].ai_family = AF_INET; + } + } +#ifdef INET6 + if(nsd.options->ip6_only) { + for (i = 0; i < MAX_INTERFACES; ++i) { + hints[i].ai_family = AF_INET6; + } + } +#endif /* INET6 */ + if(nsd.options->ip_addresses) + { + ip_address_option_t* ip = nsd.options->ip_addresses; + while(ip) { + if (nsd.ifs < MAX_INTERFACES) { + nodes[nsd.ifs] = ip->address; + ++nsd.ifs; + } else { + error("too many interfaces ('-a' + " + "'ip-address:') specified."); + break; + } + ip = ip->next; + } + } + if (verbosity == 0) + verbosity = nsd.options->verbosity; +#ifndef NDEBUG + if (nsd_debug_level > 0 && verbosity == 0) + verbosity = nsd_debug_level; +#endif /* NDEBUG */ + if(nsd.options->debug_mode) nsd.debug=1; + if(!nsd.dbfile) + { + if(nsd.options->database) + nsd.dbfile = nsd.options->database; + else + nsd.dbfile = DBFILE; + } + if(!nsd.pidfile) + { + if(nsd.options->pidfile) + nsd.pidfile = nsd.options->pidfile; + else + nsd.pidfile = PIDFILE; + } + if(strcmp(nsd.identity, hostname)==0 || strcmp(nsd.identity,IDENTITY)==0) + { + if(nsd.options->identity) + nsd.identity = nsd.options->identity; + } + if (nsd.options->logfile && !nsd.log_filename) { + nsd.log_filename = nsd.options->logfile; + } + if(nsd.child_count == 0) { + nsd.child_count = nsd.options->server_count; + } + if(nsd.maximum_tcp_count == 0) { + nsd.maximum_tcp_count = nsd.options->tcp_count; + } + nsd.tcp_timeout = nsd.options->tcp_timeout; + nsd.tcp_query_count = nsd.options->tcp_query_count; + nsd.ipv4_edns_size = nsd.options->ipv4_edns_size; + nsd.ipv6_edns_size = nsd.options->ipv6_edns_size; + + if(udp_port == 0) + { + if(nsd.options->port != 0) { + udp_port = nsd.options->port; + tcp_port = nsd.options->port; + } else { + udp_port = UDP_PORT; + tcp_port = TCP_PORT; + } + } +#ifdef BIND8_STATS + if(nsd.st.period == 0) { + nsd.st.period = nsd.options->statistics; + } +#endif /* BIND8_STATS */ +#ifdef HAVE_CHROOT + if(nsd.chrootdir == 0) nsd.chrootdir = nsd.options->chroot; +#endif /* HAVE_CHROOT */ + if(nsd.username == 0) { + if(nsd.options->username) nsd.username = nsd.options->username; + else nsd.username = USER; + } + if(nsd.options->zonesdir && nsd.options->zonesdir[0]) { + if(chdir(nsd.options->zonesdir)) { + error("cannot chdir to '%s': %s", + nsd.options->zonesdir, strerror(errno)); + } + DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s", + nsd.options->zonesdir)); + } + + /* EDNS0 */ + edns_init_data(&nsd.edns_ipv4, nsd.options->ipv4_edns_size); +#if defined(INET6) +#if defined(IPV6_USE_MIN_MTU) || defined(IPV6_MTU) + edns_init_data(&nsd.edns_ipv6, nsd.options->ipv6_edns_size); +#else /* no way to set IPV6 MTU, send no bigger than that. */ + if (nsd.options->ipv6_edns_size < IPV6_MIN_MTU) + edns_init_data(&nsd.edns_ipv6, nsd.options->ipv6_edns_size); + else + edns_init_data(&nsd.edns_ipv6, IPV6_MIN_MTU); +#endif /* IPV6 MTU) */ +#endif /* defined(INET6) */ + + + +#ifdef NSID + edns_init_nsid(&nsd.edns_ipv4, nsd.nsid_len); +#if defined(INET6) + edns_init_nsid(&nsd.edns_ipv6, nsd.nsid_len); +#endif /* defined(INET6) */ +#endif /* NSID */ + /* Number of child servers to fork. */ + nsd.children = (struct nsd_child *) region_alloc( + nsd.region, nsd.child_count * sizeof(struct nsd_child)); + for (i = 0; i < nsd.child_count; ++i) { + nsd.children[i].kind = NSD_SERVER_BOTH; + nsd.children[i].pid = -1; + nsd.children[i].child_fd = -1; + nsd.children[i].parent_fd = -1; + nsd.children[i].handler = NULL; + nsd.children[i].need_to_send_STATS = 0; + 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; + + /* We need at least one active interface */ + if (nsd.ifs == 0) { + nsd.ifs = 1; + + /* + * With IPv6 we'd like to open two separate sockets, + * one for IPv4 and one for IPv6, both listening to + * the wildcard address (unless the -4 or -6 flags are + * specified). + * + * However, this is only supported on platforms where + * we can turn the socket option IPV6_V6ONLY _on_. + * Otherwise we just listen to a single IPv6 socket + * and any incoming IPv4 connections will be + * automatically mapped to our IPv6 socket. + */ +#ifdef INET6 + if (hints[0].ai_family == AF_UNSPEC) { +#ifdef IPV6_V6ONLY + hints[0].ai_family = AF_INET6; + hints[1].ai_family = AF_INET; + nsd.ifs = 2; + nsd.grab_ip6_optional = 1; +#else /* !IPV6_V6ONLY */ + hints[0].ai_family = AF_INET6; +#endif /* IPV6_V6ONLY */ + } +#endif /* INET6 */ + } + + /* Set up the address info structures with real interface/port data */ + for (i = 0; i < nsd.ifs; ++i) { + int r; + + /* We don't perform name-lookups */ + if (nodes[i] != NULL) + hints[i].ai_flags |= AI_NUMERICHOST; + + hints[i].ai_socktype = SOCK_DGRAM; + if ((r=getaddrinfo(nodes[i], udp_port, &hints[i], &nsd.udp[i].addr)) != 0) { +#ifdef INET6 + if(nsd.grab_ip6_optional && hints[0].ai_family == AF_INET6) { + log_msg(LOG_WARNING, "No IPv6, fallback to IPv4. getaddrinfo: %s", + r==EAI_SYSTEM?strerror(errno):gai_strerror(r)); + continue; + } +#endif + error("cannot parse address '%s': getaddrinfo: %s %s", + nodes[i]?nodes[i]:"(null)", + gai_strerror(r), + r==EAI_SYSTEM?strerror(errno):""); + } + + hints[i].ai_socktype = SOCK_STREAM; + if ((r=getaddrinfo(nodes[i], tcp_port, &hints[i], &nsd.tcp[i].addr)) != 0) { + error("cannot parse address '%s': getaddrinfo: %s %s", + nodes[i]?nodes[i]:"(null)", + gai_strerror(r), + r==EAI_SYSTEM?strerror(errno):""); + } + } + + /* Parse the username into uid and gid */ + nsd.gid = getgid(); + nsd.uid = getuid(); +#ifdef HAVE_GETPWNAM + /* Parse the username into uid and gid */ + if (*nsd.username) { + if (isdigit((int)*nsd.username)) { + char *t; + nsd.uid = strtol(nsd.username, &t, 10); + if (*t != 0) { + if (*t != '.' || !isdigit((int)*++t)) { + error("-u user or -u uid or -u uid.gid"); + } + nsd.gid = strtol(t, &t, 10); + } else { + /* Lookup the group id in /etc/passwd */ + if ((pwd = getpwuid(nsd.uid)) == NULL) { + error("user id %u does not exist.", (unsigned) nsd.uid); + } else { + nsd.gid = pwd->pw_gid; + } + } + } else { + /* Lookup the user id in /etc/passwd */ + if ((pwd = getpwnam(nsd.username)) == NULL) { + error("user '%s' does not exist.", nsd.username); + } else { + nsd.uid = pwd->pw_uid; + nsd.gid = pwd->pw_gid; + } + } + } + /* endpwent(); */ +#endif /* HAVE_GETPWNAM */ + +#ifdef TSIG + if(!tsig_init(nsd.region)) + error("init tsig failed"); + key_options_tsig_add(nsd.options); +#endif /* TSIG */ + + /* Set up the logging */ + log_open(LOG_PID, FACILITY, nsd.log_filename); + if (!nsd.log_filename) + log_set_log_function(log_syslog); + else if (nsd.uid && nsd.gid) + (void) chown(nsd.log_filename, nsd.uid, nsd.gid); + + /* Relativize the pathnames for chroot... */ + if (nsd.chrootdir) { + int l = strlen(nsd.chrootdir); + + /* 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)); + strncat(chroot_slash, "/", 1); + nsd.chrootdir = chroot_slash; + ++l; + } + + if (strncmp(nsd.chrootdir, nsd.pidfile, l) != 0) { + log_msg(LOG_ERR, "%s is not relative to %s: will not chroot", + nsd.pidfile, nsd.chrootdir); + nsd.chrootdir = NULL; + } else if (strncmp(nsd.chrootdir, nsd.dbfile, l) != 0) { + log_msg(LOG_ERR, "%s is not relative to %s: will not chroot", + nsd.dbfile, nsd.chrootdir); + nsd.chrootdir = NULL; + } else if (strncmp(nsd.chrootdir, nsd.options->xfrdfile, l) != 0) { + log_msg(LOG_ERR, "%s is not relative to %s: will not chroot", + nsd.options->xfrdfile, nsd.chrootdir); + nsd.chrootdir = NULL; + } else if (strncmp(nsd.chrootdir, nsd.options->difffile, l) != 0) { + log_msg(LOG_ERR, "%s is not relative to %s: will not chroot", + nsd.options->difffile, nsd.chrootdir); + nsd.chrootdir = NULL; + } + } + + /* Do we have a running nsd? */ + if ((oldpid = readpid(nsd.pidfile)) == -1) { + if (errno != ENOENT) { + log_msg(LOG_ERR, "can't read pidfile %s: %s", + nsd.pidfile, strerror(errno)); + } + } else { + if (kill(oldpid, 0) == 0 || errno == EPERM) { + log_msg(LOG_WARNING, + "%s is already running as %u, continuing", + argv0, (unsigned) oldpid); + } else { + log_msg(LOG_ERR, + "...stale pid file from process %u", + (unsigned) oldpid); + } + } + + /* Unless we're debugging, fork... */ + if (!nsd.debug) { + int fd; + + /* Take off... */ + switch ((nsd.pid = fork())) { + case 0: + /* Child */ + break; + case -1: + log_msg(LOG_ERR, "fork() failed: %s", strerror(errno)); + exit(1); + default: + /* Parent is done */ + exit(0); + } + + /* Detach ourselves... */ + if (setsid() == -1) { + log_msg(LOG_ERR, "setsid() failed: %s", strerror(errno)); + exit(1); + } + + if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close(fd); + } + } + + /* Setup the signal handling... */ + action.sa_handler = sig_handler; + sigfillset(&action.sa_mask); + action.sa_flags = 0; + sigaction(SIGTERM, &action, NULL); + sigaction(SIGHUP, &action, NULL); + sigaction(SIGINT, &action, NULL); + sigaction(SIGILL, &action, NULL); + sigaction(SIGUSR1, &action, NULL); + sigaction(SIGALRM, &action, NULL); + sigaction(SIGCHLD, &action, NULL); + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); + + /* Get our process id */ + nsd.pid = getpid(); + + /* Initialize... */ + nsd.mode = NSD_RUN; + nsd.signal_hint_child = 0; + nsd.signal_hint_reload = 0; + nsd.signal_hint_quit = 0; + nsd.signal_hint_shutdown = 0; + nsd.signal_hint_stats = 0; + nsd.signal_hint_statsusr = 0; + nsd.quit_sync_done = 0; + + /* Initialize the server... */ + if (server_init(&nsd) != 0) { + log_msg(LOG_ERR, "server initialization failed, %s could " + "not be started", argv0); + exit(1); + } + +#ifdef HAVE_CHROOT + /* Chroot */ + if (nsd.chrootdir && strlen(nsd.chrootdir)) { + int l = strlen(nsd.chrootdir); + int ret = 0; + + while (l>0 && nsd.chrootdir[l-1] == '/') + --l; + + /* filename after chroot */ + ret = file_inside_chroot(nsd.log_filename, nsd.chrootdir); + if (ret) { + nsd.file_rotation_ok = 1; + if (ret == 2) /* also strip chroot */ + nsd.log_filename += l; + } + nsd.dbfile += l; + nsd.pidfile += l; + nsd.options->xfrdfile += l; + nsd.options->difffile += l; + + if (chroot(nsd.chrootdir)) { + log_msg(LOG_ERR, "unable to chroot: %s", strerror(errno)); + exit(1); + } + DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed root directory to %s", + nsd.chrootdir)); + } + else +#endif /* HAVE_CHROOT */ + nsd.file_rotation_ok = 1; + + 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", + nsd.pidfile, strerror(errno)); + } + + /* Drop the permissions */ +#ifdef HAVE_GETPWNAM + if (*nsd.username) { +#ifdef HAVE_SETUSERCONTEXT + /* setusercontext does initgroups, setuid, setgid, and + * also resource limits from login config, but we + * still call setresuid, setresgid to be sure to set all uid */ + if (setusercontext(NULL, pwd, nsd.uid, LOGIN_SETALL) != 0) + log_msg(LOG_WARNING, "unable to setusercontext %s: %s", + nsd.username, strerror(errno)); +#else /* !HAVE_SETUSERCONTEXT */ + #ifdef HAVE_INITGROUPS + if(initgroups(nsd.username, nsd.gid) != 0) + log_msg(LOG_WARNING, "unable to initgroups %s: %s", + nsd.username, strerror(errno)); + #endif /* HAVE_INITGROUPS */ +#endif /* HAVE_SETUSERCONTEXT */ + endpwent(); + +#ifdef HAVE_SETRESGID + if(setresgid(nsd.gid,nsd.gid,nsd.gid) != 0) +#elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID) + if(setregid(nsd.gid,nsd.gid) != 0) +#else /* use setgid */ + if(setgid(nsd.gid) != 0) +#endif /* HAVE_SETRESGID */ + error("unable to set group id of %s: %s", + nsd.username, strerror(errno)); + +#ifdef HAVE_SETRESUID + if(setresuid(nsd.uid,nsd.uid,nsd.uid) != 0) +#elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID) + if(setreuid(nsd.uid,nsd.uid) != 0) +#else /* use setuid */ + if(setuid(nsd.uid) != 0) +#endif /* HAVE_SETRESUID */ + error("unable to set user id of %s: %s", + nsd.username, strerror(errno)); + + DEBUG(DEBUG_IPC,1, (LOG_INFO, "dropped user privileges, run as %s", + nsd.username)); + } +#endif /* HAVE_GETPWNAM */ + + if (server_prepare(&nsd) != 0) { + log_msg(LOG_ERR, "server preparation failed, %s could " + "not be started", argv0); + unlinkpid(nsd.pidfile); + exit(1); + } + + /* Really take off */ + log_msg(LOG_NOTICE, "%s started (%s), pid %d", + argv0, PACKAGE_STRING, (int) nsd.pid); + + if (nsd.server_kind == NSD_SERVER_MAIN) { + server_main(&nsd); + } else { + server_child(&nsd); + } + + /* NOTREACH */ + exit(0); +} diff --git a/usr.sbin/nsd/nsd.conf.5 b/usr.sbin/nsd/nsd.conf.5 new file mode 100644 index 00000000000..7b6a871102c --- /dev/null +++ b/usr.sbin/nsd/nsd.conf.5 @@ -0,0 +1,514 @@ +.TH "nsd.conf" "5" "Jan 6, 2010" "NLnet Labs" "nsd 3.2.4" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. +.\" See LICENSE for the license. +.SH "NAME" +.LP +.B nsd.conf +\- NSD configuration file +.SH "SYNOPSIS" +.LP +.B nsd.conf +.SH "DESCRIPTION" +.B Nsd.conf +is used to configure nsd(8). The file format has attributes and +values. Some attributes have attributes inside them. The notation +is: attribute: value. +.PP +Comments start with # and last to the end of line. Empty lines are +ignored as is whitespace at the beginning of a line. +.PP +.B Nsd.conf +specifies options for the nsd server, zone files, primaries and +secondaries. +.SH "EXAMPLE" +.LP +An example of a short nsd.conf file is below. +.LP +# Example.com nsd.conf file +.RS 0 +# This is a comment. +.RE +.TP +server: +.RS 5 +database: "/var/db/nsd/nsd.db" +.RE +.RS 5 +username: nsd +.RE +.RS 5 +logfile: "/var/log/nsd.log" +.RE +.RS 5 +pidfile: "/var/run/nsd.pid" +.RE +.RS 5 +difffile: "/var/db/nsd/ixfr.db" +.RE +.RS 5 +xfrdfile: "/var/db/nsd/xfrd.state" +.RE +.TP +zone: +.RS 5 +name: example.com +.RE +.RS 5 +# note that quotes are optional on the value +.RE +.RS 5 +zonefile: /etc/nsd/example.com.zone +.RE +.SH "FILE FORMAT" +There must be whitespace between keywords. Attribute keywords end +with a colon ':'. An attribute is followed by its containing +attributes, or a value. +.P +At the top level only +.B server: +or +.B zone: +or +.B key: +are allowed. These are followed by their attributes or the start of +a new +.B server: +or +.B zone: +or +.B key: +clause. The +.B zone: +attribute is followed by zone options. The +.B server: +attribute is followed by global options for the +.B NSD +server. A +.B key: +attribute is used to define keys for authentication. +.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. +.S "Server Options" +.LP +The global options (if not overridden from the NSD commandline) are +taken from the +.B server: +clause. There may only be one +.B server: +clause. +.TP +.B ip\-address:\fR <ip4 or ip6> +NSD will bind to the listed ip\-address. Can be give multiple times +to bind multiple ip\-addresses. If none are given NSD listens to all +IP addresses. Same as commandline option +.BR \-a. +.TP +.B debug\-mode:\fR <yes or no> +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. +.TP +.B ip6\-only:\fR <yes or no> +If yes, NSD only listens to IPv6 connections. Same as commandline +option +.BR \-6. +.TP +.B database:\fR <filename> +By default +.I /var/db/nsd/nsd.db +is used. The specified file is used to store the compiled +zone information. Same as commandline option +.BR \-f. +.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 +commandline option +.BR \-i . +.TP +.B logfile:\fR <filename> +Log messages to the logfile. The default is to log to stderr and +syslog. Same as commandline option +.BR \-l . +.TP +.B server\-count:\fR <number> +.It \fBserver\-count:\fR <number> +Start this many NSD servers. Default is 1. Same as commandline +option +.BR \-N . +.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 +.BR \-n . +.TP +.B tcp\-query\-count:\fR <number> +The maximum number of queries served on a single TCP connection. +Default is 0, meaning there is no maximum. +.TP +.B tcp\-timeout:\fR <number> +Overrides the default TCP timeout. This also affects zone transfers over TCP. +.TP +.B ipv4\-edns\-size:\fR <number> +Preferred EDNS buffer size for IPv4. +.TP +.B ipv6\-edns\-size:\fR <number> +Preferred EDNS buffer size for IPv6. +.TP +.B pidfile:\fR <filename> +Use the pid file instead of the platform specific default, usually +.IR /var/run/nsd.pid. +Same as commandline option +.BR \-P . +.TP +.B port:\fR <number> +Answer queries on the specified port. Default is 53. Same as +commandline option +.BR \-p . +.TP +.B statistics:\fR <number> +If not present no statistics are dumped. Statistics are produced +every number seconds. Same as commandline option +.BR \-s . +.TP +.B chroot:\fR <directory> +NSD will chroot on startup to the specified directory. Same as +commandline option +.BR \-t . +.TP +.B username:\fR <username> +After binding the socket, drop user privileges and assume the +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 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. +.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 /var/db/nsd/ixfr.db . +.TP +.B xfrdfile:\fR <filename> +The soa timeout and zone transfer daemon in NSD will save its state +to this file. State is read back after a restart. The state file can +be deleted without too much harm, but timestamps of zones will be +gone. For more details see the section on zone expiry behavior of +NSD. Default is +.IR /var/db/nsd/xfrd.state . +.TP +.B xrfd\-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. +.TP +.B verbosity:\fR <level> +This value specifies the verbosity level for (non\-debug) logging. +Default is 0. 1 gives more information about incoming notifies and +zone transfers. 2 lists soft warnings that are encountered. +.TP +.B hide\-version:\fR <yes or no> +Prevent NSD from replying with the version string on CHAOS class +queries. +.SS "Zone Options" +.LP +For every zone the options need to be specified in one +.B zone: +clause. The access control list elements can be given multiple +times to add multiple servers. +.TP +.B name:\fR <string> +The name of the zone. This is the domain name of the apex of the +zone. May end with a '.' (in FQDN notation). For example +"example.com", "sub.example.net.". This attribute must be present in +each zone. +.TP +.B zonefile:\fR <filename> +The file containing the zone information. This file is used by +zonec(8). This attribute must be present in each 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. +.P +.RS +The ip\-spec is either a plain IP address (IPv4 or IPv6), or can be +a subnet of the form 1.2.3.4/24, or masked like +1.2.3.4&255.255.255.0 or a range of the form 1.2.3.4\-1.2.3.25. +A port number can be added using a suffix of @number, for example +1.2.3.4@5300 or 1.2.3.4/24@5300 for port 5300. +Note the ip\-spec ranges do not use spaces around the /, &, @ and \- +symbols. +.RE +.TP +.B request\-xfr:\fR [AXFR|UDP] <ip\-address> <key\-name | NOKEY> +Access control list. The listed address (the master) is queried for +AXFR/IXFR on update. The specified key is used during AXFR/IXFR. +.P +.RS +If the AXFR option is given, the server will not be contacted with +IXFR queries but only AXFR requests will be made to the server. This +allows an NSD secondary to have a master server that runs NSD. If +the AXFR option is left out then both IXFR and AXFR requests are +made to the master server. +.P +If the UDP option is given, the secondary will use UDP to transmit the IXFR +requests. You should deploy TSIG when allowing UDP transport, to authenticate +notifies and zone transfers. Otherwise, NSD is more vulnerable for +Kaminsky-style attacks. If the UDP option is left out then IXFR will be +transmitted using TCP. +.RE +.TP +.B allow\-axfr\-fallback:\fR <yes or no> +This option should be accompanied by request-xfr. It (dis)allows NSD (as secondary) +to fallback to AXFR if the primary name server does not support IXFR. +.TP +.B notify:\fR <ip\-address> <key\-name | NOKEY> +Access control list. The listed address (a secondary) is notified +of updates to this zone. The specified key is used to sign the +notify. Only on secondary configurations will NSD be able to detect +zone updates (as it gets notified itself, or refreshes after a +time). +.TP +.B notify\-retry:\fR <number> +This option should be accompanied by notify. It sets the number of retries +when sending notifies. +.TP +.B provide\-xfr:\fR <ip\-spec> <key\-name | NOKEY | BLOCKED> +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. +.P +.RS +The ip\-spec is either a plain IP address (IPv4 or IPv6), or can be +a subnet of the form 1.2.3.4/24, or masked like +1.2.3.4&255.255.255.0 or a range of the form 1.2.3.4\-1.2.3.25. +A port number can be added using a suffix of @number, for example +1.2.3.4@5300 or 1.2.3.4/24@5300 for port 5300. Note the ip\-spec +ranges do not use spaces around the /, &, @ and \- symbols. +.RE +.TP +.B outgoing\-interface:\fR <ip\-address> +Access control list. The listed address is used to request AXFR|IXFR (in case of +a secondary) or used to send notifies (in case of a primary). +.P +.RS +The ip\-address is either a plain IP address (IPv4 or IPv6), or can be +a subnet of the form 1.2.3.4/24, or masked like +1.2.3.4&255.255.255.0 or a range of the form 1.2.3.4\-1.2.3.25. +.RE +.SS "Key Declarations" +The +.B key: +clause establishes a key for use in access control lists. It has +the following attributes. +.TP +.B name:\fR <string> +The key name. Used to refer to this key in the access control list. +.TP +.B algorithm:\fR <string> +Authentication algorithm for this key. +.TP +.B secret:\fR <base64 blob> +The base64 encoded shared secret. It is possible to put the +.B secret: +declaration (and base64 blob) into a different file, and then to +.B include: +that file. In this way the key secret and the rest of the configuration +file, which may have different security policies, can be split apart. +.SH "NSD CONFIGURATION FOR BIND9 HACKERS" +BIND9 is a name server implementation with its own configuration +file format, named.conf(5). BIND9 types zones as 'Master' or +'Slave'. +.SS "Slave zones" +For a slave zone, the master servers are listed. The master servers are +queried for zone data, and are listened to for update notifications. +In NSD these two properties need to be configured seperately, by listing +the master address in allow\-notify and request\-xfr statements. +.P +In BIND9 you only need to provide allow\-notify elements for +any extra sources of notifications (i.e. the operators), NSD needs to have +allow\-notify for both masters and operators. BIND9 allows +additional transfer sources, in NSD you list those as request\-xfr. +.P +Here is an example of a slave zone in BIND9 syntax. +.P +# Config file for example.org +options { +.RS 5 +dnssec\-enable yes; +.RE +.RS 0 +}; +.RE +.LP +key tsig.example.org. { +.RS 5 +algorithm hmac\-md5; +.RE +.RS 5 +secret "aaaaaabbbbbbccccccdddddd"; +.RE +}; +.LP +server 162.0.4.49 { +.RS 5 +keys { tsig.example.org. ; }; +.RE +}; +.LP +zone "example.org" { +.RS 5 +type slave; +.RE +.RS 5 +file "secondary/example.org.signed"; +.RE +.RS 5 +masters { 162.0.4.49; }; +.RE +}; +.P +For NSD, DNSSEC is enabled automatically for zones that are signed. The +dnssec\-enable statement in the options clause is not needed. In NSD +keys are associated with an IP address in the access control list +statement, therefore the server{} statement is not needed. Below is +the same example in an NSD config file. +.LP +# Config file for example.org +.RS 0 +key: +.RE +.RS 5 +name: tsig.example.org. +.RE +.RS 5 +algorithm: hmac\-md5 +.RE +.RS 5 +secret: "aaaaaabbbbbbccccccdddddd" +.RE +.LP +zone: +.RS 5 +name: "example.org" +.RE +.RS 5 +zonefile: "secondary/example.org.signed" +.RE +.RS 5 +# the master is allowed to notify and will provide zone data. +.RE +.RS 5 +allow\-notify: 162.0.4.49 NOKEY +.RE +.RS 5 +request\-xfr: 162.0.4.49 tsig.example.org. +.RE +.P +Notice that the master is listed twice, once to allow it to send notifies +to this slave server and once to tell the slave server where to look for +updates zone data. More allow\-notify and request\-xfr lines can be +added to specify more masters. +.P +It is possible to specify extra allow\-notify lines for addresses +that are also allowed to send notifications to this slave server. +.SS "Master zones" +For a master zone in BIND9, the slave servers are listed. These slave +servers are sent notifications of updated and are allowed to request +transfer of the zone data. In NSD these two properties need to be +configured seperately. +.P +Here is an example of a master zone in BIND9 syntax. +.LP +zone "example.nl" { +.RS 5 +type master; +.RE +.RS 5 +file "example.nl"; +.RE +}; +.LP +In NSD syntax this becomes: +.LP +zone: +.RS 5 +name: "example.nl" +.RE +.RS 5 +zonefile: "example.nl" +.RE +.RS 5 +# allow anybody to request xfr. +.RE +.RS 5 +provide\-xfr: 0.0.0.0/0 NOKEY +.RE +.RS 5 +provide\-xfr: ::0/0 NOKEY +.RE +.P +.RS 5 +# to list a slave server you would in general give +.RE +.RS 5 +# provide\-xfr: 1.2.3.4 tsig\-key.name. +.RE +.RS 5 +# notify: 1.2.3.4 NOKEY +.RE +.SS "Other" +NSD is an authoritative only DNS server. This means that it is +meant as a primary or secondary server for zones, providing DNS +data to DNS resolvers and caches. BIND9 can function as an +authoritative DNS server, the configuration options for that are +compared with those for NSD in this section. However, BIND9 can +also function as a resolver or cache. The configuration options that +BIND9 has for the resolver or caching thus have no equivalents for NSD. +.SH "FILES" +.TP +/var/db/nsd/nsd.db +default +.B NSD +database +.TP +/etc/nsd/nsd.conf +default +.B NSD +configuration file +.SH "SEE ALSO" +.LP +nsd(8), nsdc(8), nsd\-checkconf(8), nsd-notify(8), +nsd-patch(8), nsd-xfer(8), zonec(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.conf +is parsed by a primitive parser, error messages may not be to the +point. diff --git a/usr.sbin/nsd/nsd.conf.sample.in b/usr.sbin/nsd/nsd.conf.sample.in new file mode 100644 index 00000000000..442031b96fb --- /dev/null +++ b/usr.sbin/nsd/nsd.conf.sample.in @@ -0,0 +1,180 @@ +# +# nsd.conf -- the NSD(8) configuration file, nsd.conf(5). +# +# Copyright (c) 2001-2006, NLnet Labs. All rights reserved. +# +# See LICENSE for the license. +# + +# This is a comment. +# Sample configuration file + +# options for the nsd server +server: + # uncomment to specify specific interfaces to bind (default all). + # ip-address: 1.2.3.4 + # ip-address: 12fe::8ef0 + + # don't answer VERSION.BIND and VERSION.SERVER CHAOS class queries + # hide-version: no + + # enable debug mode, does not fork daemon process into the background. + # debug-mode: no + + # listen only on IPv4 connections + # ip4-only: no + + # listen only on IPv6 connections + # ip6-only: no + + # the database to use + # database: "@dbfile@" + + # identify the server (CH TXT ID.SERVER entry). + # identity: "unidentified server" + + # log messages to file. Default to stderr and syslog. + # logfile: "/var/log/nsd.log" + + # Number of NSD servers to fork. + # server-count: 1 + + # Maximum number of concurrent TCP connections per server. + # This option should have a value below 1000. + # tcp-count: 10 + + # Maximum number of queries served on a single TCP connection. + # By default 0, which means no maximum. + # tcp-query-count: 0 + + # Override the default (120 seconds) TCP timeout. + # tcp-timeout: 120 + + # Preferred EDNS buffer size for IPv4. + # ipv4-edns-size: 4096 + + # Preferred EDNS buffer size for IPv6. + # ipv6-edns-size: 4096 + + # File to store pid for nsd in. + # pidfile: "@pidfile@" + + # port to answer queries on. default is 53. + # port: 53 + + # statistics are produced every number of seconds. + # statistics: 3600 + + # Run NSD in a chroot-jail. + # make sure to have pidfile and database reachable from there. + # by default, no chroot-jail is used. + # chroot: "@configdir@" + + # After binding socket, drop user privileges. + # can be a username, id or id.gid. + # username: @user@ + + # The directory for zonefile: files. + # zonesdir: "@zonesdir@" + + # The file where incoming zone transfers are stored. + # run nsd-patch to update zone files, then you can safely delete it. + # difffile: "@difffile@" + + # The file where secondary zone refresh and expire timeouts are kept. + # If you delete this file, all secondary zones are forced to be + # 'refreshing' (as if nsd got a notify). + # xfrdfile: "@xfrdfile@" + + # Number of seconds between reloads triggered by xfrd. + # xfrd-reload-timeout: 10 + + # Verbosity level. + # verbosity: 0 + +# key for zone 1 +key: + name: mskey + algorithm: hmac-md5 + secret: "K2tf3TRjvQkVCmJF3/Z9vA==" + +# Sample zone 1 +zone: + name: "example.com" + zonefile: "example.com.zone" + + # This is a slave zone. Masters are listed below. + + # master 1 + allow-notify: 168.192.44.42 mskey + request-xfr: 168.192.44.42 mskey + + # set local interface for sending zone transfer requests. + outgoing-interface: 10.0.0.10 + + # master 2 + allow-notify: 10.0.0.11 NOKEY + request-xfr: 10.0.0.11 NOKEY + + # By default, a slave will request a zone transfer with IXFR/TCP. + # If you want to make use of IXFR/UDP use + allow-notify: 10.0.0.12 NOKEY + request-xfr: UDP 10.0.0.12 NOKEY + + # for a master that only speaks AXFR (like NSD) use + allow-notify: 10.0.0.13 NOKEY + request-xfr: AXFR 10.0.0.13 NOKEY + + # Attention: You cannot use UDP and AXFR together. AXFR is always over + # TCP. If you use UDP, we higly recommend you to deploy TSIG. + + # Allow AXFR fallback if the master does not support IXFR. Default + # is yes. + allow-axfr-fallback: "yes" + + # uncomment to provide AXFR to all the world + # provide-xfr: 0.0.0.0/0 NOKEY + # provide-xfr: ::0/0 NOKEY + +# Sample zone 2 +zone: + name: "example.net" + zonefile: "example.net.signed.zone" + + # This is a master zone. Slaves are listed below. + + # secondary 1. Uses port 5300. + notify: 10.0.0.14@5300 sec1_key + provide-xfr: 10.0.0.14@5300 sec1_key + + # set local interface for sending notifies + outgoing-interface: 10.0.0.15 + + # secondary 2. + notify: 10.11.12.14 sec2_key + provide-xfr: 10.11.12.14 sec2_key + + # also provide xfr to operator's network. + provide-xfr: 169.192.85.0/24 NOKEY + # uncomment to disable xfr for the address. + # provide-xfr: 169.192.85.66 BLOCKED + + # set the number of retries for notify. + notify-retry: 5 + +# keys for zone 2 +key: + name: "sec1_key" + algorithm: hmac-md5 + secret: "6KM6qiKfwfEpamEq72HQdA==" + +key: + name: sec2_key + algorithm: hmac-sha1 + secret: "m83H2x8R0zbDf3yRKhrqgw==" + +key: + name: sec3_key + algorithm: hmac-sha256 + secret: "m83H2x8R0zbDf3yRKhrqgw==" + diff --git a/usr.sbin/nsd/nsd.h b/usr.sbin/nsd/nsd.h new file mode 100644 index 00000000000..b9678e2e8de --- /dev/null +++ b/usr.sbin/nsd/nsd.h @@ -0,0 +1,231 @@ +/* + * nsd.h -- nsd(8) definitions and prototypes + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _NSD_H_ +#define _NSD_H_ + +/* disable NSID no matter what, there is no typecode yet */ +#undef NSID + +#include <signal.h> + +#include "dns.h" +#include "edns.h" +struct netio_handler; +struct nsd_options; + +/* The NSD runtime states and NSD ipc command values */ +#define NSD_RUN 0 +#define NSD_RELOAD 1 +#define NSD_SHUTDOWN 2 +#define NSD_STATS 3 +#define NSD_REAP_CHILDREN 4 +#define NSD_QUIT 5 +/* + * NSD_SOA_INFO is followed by u16(len in network byte order), dname, + * and then nothing (no info) or soa info. + */ +#define NSD_SOA_INFO 6 +/* + * PASS_TO_XFRD is followed by the u16(len in network order) and + * then network packet contents. packet is a notify(acl checked), or + * xfr reply from a master(acl checked). + * followed by u32(acl number that matched from notify/xfr acl). + */ +#define NSD_PASS_TO_XFRD 7 +/* + * NSD_ZONE_STATE is followed by u16(len in network byte order), + * octet 0: zone is expired, 1: zone ok. and dname of zone. + */ +#define NSD_ZONE_STATE 8 +/* + * SOA BEGIN is sent at the start of a reload SOA_INFO pass + * xfrd will not send to the parent (deadlock prevention). + */ +#define NSD_SOA_BEGIN 9 +/* + * SOA END is sent at the end of a reload SOA_INFO pass. + * xfrd then knows that reload phase is over. + */ +#define NSD_SOA_END 10 +/* + * QUIT_SYNC is sent to signify a synchronisation of ipc + * channel content during reload + */ +#define NSD_QUIT_SYNC 11 + +#define NSD_SERVER_MAIN 0x0U +#define NSD_SERVER_UDP 0x1U +#define NSD_SERVER_TCP 0x2U +#define NSD_SERVER_BOTH (NSD_SERVER_UDP | NSD_SERVER_TCP) + +#ifdef INET6 +#define DEFAULT_AI_FAMILY AF_UNSPEC +#else +#define DEFAULT_AI_FAMILY AF_INET +#endif + +#ifdef BIND8_STATS + +/* Counter for statistics */ +typedef unsigned long stc_t; + +#define LASTELEM(arr) (sizeof(arr) / sizeof(arr[0]) - 1) + +#define STATUP(nsd, stc) nsd->st.stc++ +/* #define STATUP2(nsd, stc, i) ((i) <= (LASTELEM(nsd->st.stc) - 1)) ? nsd->st.stc[(i)]++ : \ + nsd->st.stc[LASTELEM(nsd->st.stc)]++ */ + +#define STATUP2(nsd, stc, i) nsd->st.stc[(i) <= (LASTELEM(nsd->st.stc) - 1) ? i : LASTELEM(nsd->st.stc)]++ +#else /* BIND8_STATS */ + +#define STATUP(nsd, stc) /* Nothing */ +#define STATUP2(nsd, stc, i) /* Nothing */ + +#endif /* BIND8_STATS */ + +struct nsd_socket +{ + struct addrinfo * addr; + int s; +}; + +struct nsd_child +{ + /* The type of child process (UDP or TCP handler). */ + int kind; + + /* The child's process id. */ + pid_t pid; + + /* + * Socket used by the parent process to send commands and + * receive responses to/from this child process. + */ + int child_fd; + + /* + * Socket used by the child process to receive commands and + * send responses from/to the parent process. + */ + int parent_fd; + + /* + * IPC info, buffered for nonblocking writes to the child + */ + uint8_t need_to_send_STATS, need_to_send_QUIT; + uint8_t need_to_exit, has_exited; + stack_type* dirty_zones; /* stack of type zone_type* */ + + /* + * The handler for handling the commands from the child. + */ + struct netio_handler* handler; +}; + +/* NSD configuration and run-time variables */ +typedef struct nsd nsd_type; +struct nsd +{ + /* + * Global region that is not deallocated until NSD shuts down. + */ + region_type *region; + + /* Run-time variables */ + pid_t pid; + volatile sig_atomic_t mode; + volatile sig_atomic_t signal_hint_reload; + volatile sig_atomic_t signal_hint_child; + volatile sig_atomic_t signal_hint_quit; + volatile sig_atomic_t signal_hint_shutdown; + volatile sig_atomic_t signal_hint_stats; + volatile sig_atomic_t signal_hint_statsusr; + volatile sig_atomic_t quit_sync_done; + unsigned server_kind; + struct namedb *db; + int debug; + + size_t child_count; + struct nsd_child *children; + + /* NULL if this is the parent process. */ + struct nsd_child *this_child; + + /* Configuration */ + const char *dbfile; + const char *pidfile; + const char *log_filename; + const char *username; + uid_t uid; + gid_t gid; + const char *chrootdir; + const char *version; + const char *identity; + uint16_t nsid_len; + unsigned char *nsid; + uint8_t file_rotation_ok; + + /* number of interfaces, ifs < MAX_INTERFACES */ + size_t ifs; + uint8_t grab_ip6_optional; + + /* TCP specific configuration */ + struct nsd_socket tcp[MAX_INTERFACES]; + + /* UDP specific configuration */ + struct nsd_socket udp[MAX_INTERFACES]; + + edns_data_type edns_ipv4; +#if defined(INET6) + edns_data_type edns_ipv6; +#endif + + int maximum_tcp_count; + int current_tcp_count; + int tcp_query_count; + int tcp_timeout; + size_t ipv4_edns_size; + size_t ipv6_edns_size; + +#ifdef BIND8_STATS + + 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; + } st; +#endif /* BIND8_STATS */ + + struct nsd_options* options; +}; + +/* nsd.c */ +pid_t readpid(const char *file); +int writepid(struct nsd *nsd); +void unlinkpid(const char* file); +void sig_handler(int sig); +void bind8_stats(struct nsd *nsd); + +/* server.c */ +int server_init(struct nsd *nsd); +int server_prepare(struct nsd *nsd); +void server_main(struct nsd *nsd); +void server_child(struct nsd *nsd); +/* extra domain numbers for temporary domains */ +#define EXTRA_DOMAIN_NUMBERS 1024 + +#endif /* _NSD_H_ */ diff --git a/usr.sbin/nsd/nsdc.8 b/usr.sbin/nsd/nsdc.8 new file mode 100644 index 00000000000..37162dbcc15 --- /dev/null +++ b/usr.sbin/nsd/nsdc.8 @@ -0,0 +1,167 @@ +.TH "NSDC" "8" "Jan 6, 2010" "NLnet Labs" "NSDC 3.2.4" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. +.\" See LICENSE for the license. +.SH "NAME" +.LP +.B nsdc +\- Name Server Daemon (NSD) control script. +.SH "SYNOPSIS" +.LP +.B nsdc +.RB [ \-c +.IR configfile ] +.I start +| +.I stop +| +.I reload +| +.I rebuild +| +.I restart +| +.I running +| +.I update +| +.I notify +| +.I patch +.SH "DESCRIPTION" +.LP +.B Nsdc +is the shell script that used to control nsd(8) and zonec(8) from +.B NSD +distribution. +.B Nsdc +is also suitable to be linked into +.I /etc/rc.d +directory on +.I BSD +like systems for automatic startup of nsd(8) at boot time. +.P +At every invokation, +.B nsdc +will try to read the nsd.conf(5) configuration file. An example of +such configuration file is distributed with the +.B NSD +package as +.IR nsd.conf.sample . +The config file is checked for errors before it is used, see +nsd\-checkconf(8). +.P +Possible +.B nsdc +applications are: +.TP +.I start +Start nsd(8). +.TP +.I stop +Shut down nsd(8) by sending +.I SIGTERM +to it. +.TP +.I reload +Initiate nsd(8) name space database reload by sending +.IR SIGHUP. +.TP +.I rebuild +Rebuild the nsd(8) database by invoking zonec(8) with appropriate +arguments. +.TP +.I restart +Restart nsdc(8). This equals to nsdc stop && nsdc start. +.TP +.I running +Check whether nsd(8) is running. Returns error message and error +code if it is not running, and no message and zero error code +otherwise. +.TP +.I update +Updates all the slave zones which have +.I allow\-notify: +from localhost (127.0.0.1 or ::1) allowed. +If a TSIG key is specified for the allow\-notify statement in the +config file, it will be used to secure the notify. Note that NSD +keeps track of zone timeouts automatically, this is only needed if +you want to manually force updates by sending notify messages to the +localhost. +.P +.RS +Another method you can use is to stop nsd, delete the xfrd.state +file and then start nsd again. It will try to update all zones. +This method does not require allow\-notify: statements. +.RE +.TP +.I notify +Sends notify messages to all the slaves for all the zones that have the +.I notify: +keyword in the +.I nsd.conf +file. If a TSIG key is specified for a notify statement, it will be +used to secure the notification message to that slave server. +.TP +.I patch +Merge zone transfer changes back to zone files. It reads in the nsd +database (nsd.db) and difffile (ixfr.db), and overwrites the zone +text files if they have been updated. Running this regularly +ensures that the difffile does not grow infinitely. +.SH "OPTIONS" +.TP +.B \-c\fI configfile +Specify configfile to use instead of the default +.IR /etc/nsd/nsd.conf . +.SH "FILES" +.TP +/etc/nsd/nsd.conf +Configuration file for nsd to change default pathnames and +.B NSD +flags. The zone names, pathnames to zone files and access control +lists are also in nsd.conf(5). +.TP +/var/db/nsd/nsd.db +default +.B NSD +database +.TP +/var/db/nsd/nsd.db.lock +Lockfile for the +.B NSD +database access by operator tools. +.TP +/var/db/nsd/ixfr.db +Journal of zone transfers, the diff file containing the new zone +contents transferred. +.TP +/var/db/nsd/xfrd.state +State for the zone transfer process of +.BR NSD. +Contains timeouts for the zones and whether zones are expired. +.TP +/var/run/nsd.pid +the process id of the name server. +.SH "DIAGNOSTICS" +.LP +.B Nsdc +will return zero return code if operation was successful and +an error message to standard output plus a non\-zero return code +otherwise. +.SH "SEE ALSO" +.LP +nsd(8), nsd.conf(5), nsd\-checkconf(8), nsd\-notify(8), +nsd\-patch(8), nsd\-xfer(8), zonec(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" +Syntax checking of the config file is rudimentary and error +messages may be wrong. If you do a nsdc patch, whilst a (long) zone +transfer is busy, the zone transfer contents will be partially +lost. After a reload, this will be detected and the zone transfer +should be restarted. The reload that happens at the end of nsdc +patch also frees up memory churn in +.B NSD +caused by zone transfers. diff --git a/usr.sbin/nsd/nsdc.sh.in b/usr.sbin/nsd/nsdc.sh.in new file mode 100644 index 00000000000..d5287871aea --- /dev/null +++ b/usr.sbin/nsd/nsdc.sh.in @@ -0,0 +1,438 @@ +#!@shell@ +# +# nsdc.sh -- a shell script to manage the beast +# +# Copyright (c) 2001-2006, NLnet Labs. All rights reserved. +# +# See LICENSE for the license. +# +# + +# chkconfig: 2345 @spriority@ @kpriority@ +# description: NSD, authoritative only high performance name server. + +# configuration file default +configfile="@nsdconfigfile@" + +# The directory where NSD binaries reside +sbindir="@sbindir@" + +# how verbose is zonec run. Specify Nothing (empty string), -v or -vv. +ZONEC_VERBOSE=-v + +# how patch is done. Specify 1 (with use of textfiles, default) or 0 (without) +PATCH_STYLE=1 + +# +# You sure heard this many times before: NO USER SERVICEABLE PARTS BELOW +# + +# see if user selects a different config file, with -c <filename> +if test "x$1" = "x-c"; then + shift + if [ -e $1 ]; then + configfile=$1 + shift + else + echo "`basename $0`: Config file "$1" does not exist." + exit 1 + fi +fi + +# locate nsd-checkconf : in sbindir, PATH, nsdc_dir or . +nsd_checkconf="" +if [ -e ${sbindir}/nsd-checkconf ]; then + nsd_checkconf=${sbindir}/nsd-checkconf +else + if which nsd-checkconf >/dev/null 2>&1 ; then + if which nsd-checkconf 2>&1 | grep "^[Nn]o " >/dev/null; then + nsd_checkconf="" + else + nsd_checkconf=`which nsd-checkconf` + fi + fi + if [ -z "${nsd_checkconf}" -a -e `dirname $0`/nsd-checkconf ]; then + nsd_checkconf=`dirname $0`/nsd-checkconf + fi + if [ -z "${nsd_checkconf}" -a -e ./nsd-checkconf ]; then + nsd_checkconf=./nsd-checkconf + fi + if [ -z "${nsd_checkconf}" ]; then + echo "`basename $0`: Could not find nsd programs" \ + "in $sbindir, in PATH=$PATH, in cwd=`pwd`," \ + "or in dir of nsdc=`dirname $0`" + exit 1 + fi +fi + +usage() { + echo "Usage: `basename $0` [-c configfile] {start|stop|reload|rebuild|restart|" + echo " running|update|notify|patch}" + echo "options:" + echo " -c configfile Use specified configfile (default: @nsdconfigfile@)." + echo "commands:" + echo " start Start nsd server." + echo " stop Stop nsd server." + echo " reload Nsd server reloads database file." + echo " rebuild Compile database file from zone files." + echo " restart Stop the nsd server and start it again." + echo " running Prints message and exit nonzero if server not running." + echo " update Try to update all slave zones hosted on this server." + echo " notify Send notify messages to all secondary servers." + echo " patch Merge zone transfer changes back to zone files." +} + +# check the config syntax before using it +${nsd_checkconf} ${configfile} +if test $? -ne 0 ; then + usage + exit 1 +fi + +# Read some settings from the config file. +dbfile=`${nsd_checkconf} -o database ${configfile}` +pidfile=`${nsd_checkconf} -o pidfile ${configfile}` +difffile=`${nsd_checkconf} -o difffile ${configfile}` +zonesdir=`${nsd_checkconf} -o zonesdir ${configfile}` +lockfile="${dbfile}.lock" # still needed +sbindir=`dirname ${nsd_checkconf}` + +# move to zonesdir (if specified), and make absolute pathnames. +if test -n "${zonesdir}"; then + zonesdir=`dirname ${zonesdir}/.` + if echo "${zonesdir}" | grep "^[^/]" >/dev/null; then + zonesdir=`pwd`/${zonesdir} + fi + if echo "${dbfile}" | grep "^[^/]" >/dev/null; then + dbfile=${zonesdir}/${dbfile} + fi + if echo "${pidfile}" | grep "^[^/]" >/dev/null; then + pidfile=${zonesdir}/${pidfile} + fi + if echo "${lockfile}" | grep "^[^/]" >/dev/null; then + lockfile=${zonesdir}/${lockfile} + fi + if echo "${difffile}" | grep "^[^/]" >/dev/null; then + difffile=${zonesdir}/${difffile} + fi +fi + +# for bash: -C or noclobber. For tcsh: noclobber. For bourne: -C. +noclobber_set="set -C" +# ugly check for tcsh +if echo @shell@ | grep tcsh >/dev/null; then + noclobber_set="set noclobber" +fi + +# +# useful routines +# +signal() { + if [ -s ${pidfile} ] + then + kill -"$1" `cat ${pidfile}` && return 0 + else + echo "nsd is not running" + fi + return 1 +} + +lock_file() { + (umask 222; ${noclobber_set}; echo "$$" >${lockfile}) +} + +lock() { + lock_file + if [ $? = 1 ] + then + # check if the lockfile has not gone stale + LPID=`cat ${lockfile}` + echo database locked by PID: $LPID + if kill -0 $LPID 2>/dev/null; then + exit 1 + fi + + # locking process does not exist, consider lockfile stale + echo stale lockfile, removing... && rm -f ${lockfile} && lock_file + fi + + if [ $? = 1 ] + then + echo lock failed + exit 1 + fi + return 0 +} + +unlock() { + rm -f ${lockfile} +} + +do_start() { + if test -x ${sbindir}/nsd; then + ${sbindir}/nsd -c ${configfile} + test $? = 0 || (echo "nsd startup failed."; exit 1) + else + echo "${sbindir}/nsd not an executable file, nsd startup failed."; exit 1 + fi +} + +controlled_sleep() { + if [ $1 -ge 25 ]; then + sleep 1 + fi +} + +controlled_stop() { + pid=$1 + try=1 + + while [ $try -ne 0 ]; do + if [ ${try} -gt 50 ]; then + echo "nsdc stop failed" + return 1 + else + if [ $try -eq 1 ]; then + kill -TERM ${pid} + else + kill -TERM ${pid} >/dev/null 2>&1 + fi + + # really stopped? + kill -0 ${pid} >/dev/null 2>&1 + if [ $? -eq 0 ]; then + controlled_sleep ${try} + try=`expr ${try} + 1` + else + try=0 + fi + fi + done + + return 0 +} + +do_controlled_stop() { + if [ -s ${pidfile} ]; then + pid=`cat ${pidfile}` + controlled_stop ${pid} && return 0 + else + echo "nsd is not running, starting anyway" && return 0 + fi + return 1 +} + +do_stop() { + signal "TERM" +} + +do_reload() { + signal "HUP" +} + +# send_updates zone_name ifc_spec {ip_spec key_spec} +send_updates() { + local zonename=$1 + # set local interface + ifc_spec="" + if test I$2 = INOKEY; then + return + fi + if test I$2 != INOKEY -a I$2 != INOIFC; then + ifc_spec="-a $2" + fi + + shift 2 + + # extract port number (if any) + port=`${nsd_checkconf} -o port ${configfile}` + if test -n "${port}"; then + port="-p ${port}" + fi + update_sent="no" + + while test $# -gt 0; do + ip_spec=$1 + key_spec=$2 + shift 2 + # only localhost is allowed. + # see if zone has 127.0.0.1 or ::1 as allowed. + if test Z${ip_spec} = "Z127.0.0.1" -o Z${ip_spec} = "Z::1"; then + secret="" + if test K${key_spec} != KNOKEY -a K${key_spec} != KBLOCKED; then + secret=`${nsd_checkconf} -s ${key_spec} ${configfile}` + secret="-y ${key_spec}:${secret}" + fi + if test K${key_spec} != KBLOCKED; then + #echo "${sbindir}/nsd-notify ${ifc_spec} ${port} -z ${zonename} ${ip_spec} # with ${key_spec}" + ${sbindir}/nsd-notify ${ifc_spec} ${port} ${secret} \ + -z ${zonename} ${ip_spec} && update_sent="yes" + fi + fi + done + if test ${update_sent} = no; then + req_xfr=`${nsd_checkconf} -z "${zonename}" -o request-xfr ${configfile}` + if test -n "${req_xfr}"; then + # must be a slave zone (has request-xfr). + echo "`basename $0`: Could not send notify for slave zone ${zonename}: not configured (with allow-notify: 127.0.0.1 or ::1)" + fi + fi +} + +# send_notify zone_name {ifc_spec} {ip_spec key_spec} +send_notify() { + local zonename=$1 + # set local interface + ifc_spec="" + if test I$2 = INOKEY; then + return + fi + + if test I$2 != INOKEY -a I$2 != INOIFC; then + ifc_spec="-a $2" + fi + shift 2 + + while test $# -gt 0; do + ip_spec=$1 + key_spec=$2 + shift 2 + secret="" + + if test K${key_spec} != KNOKEY -a K${key_spec} != KBLOCKED; then + secret=`${nsd_checkconf} -s ${key_spec} ${configfile}` + secret="-y ${key_spec}:${secret}" + fi + if test K${key_spec} != KBLOCKED; then + port="" + ipaddr=${ip_spec} + if echo ${ip_spec} | grep @ >/dev/null; then + port="-p "`echo ${ip_spec} | sed -e 's/[^@]*@\([0-9]*\)/\1/'` + ipaddr=`echo ${ip_spec} | sed -e 's/\([^@]*\)@[0-9]*/\1/'` + fi + #echo "${sbindir}/nsd-notify ${ifc_spec} ${port} -z ${zonename} ${ip_spec} # with ${key_spec}" + ${sbindir}/nsd-notify ${ifc_spec} ${port} ${secret} \ + -z ${zonename} ${ipaddr} + fi + done +} + +# do_patch {with-textfile} +do_patch() { + if test I$1 = I1; then + lock && mv ${difffile} ${difffile}.$$ && \ + ${sbindir}/nsd-patch -c ${configfile} -x ${difffile}.$$ && \ + rm -f ${difffile}.$$ && unlock && do_rebuild + result=$? + else # without textfile + lock && mv ${difffile} ${difffile}.$$ && \ + ${sbindir}/nsd-patch -c ${configfile} -x ${difffile}.$$ -s -o ${dbfile}.$$ \ + && rm -f ${difffile}.$$ && unlock && \ + mv ${dbfile}.$$ ${dbfile} + result=$? + fi + + return ${result} +} + +do_rebuild() { + lock && \ + ${sbindir}/zonec ${ZONEC_VERBOSE} -c ${configfile} -f ${dbfile}.$$ && \ + mv ${dbfile}.$$ ${dbfile} + result=$? + unlock + [ $result != 0 ] && echo "${dbfile} is unmodified" + rm -f ${dbfile}.$$ + return ${result} +} + +case "$1" in +start) + if test -s ${pidfile} && kill -"0" `cat ${pidfile}` + then + (echo "process `cat ${pidfile}` exists, please use restart"; exit 1) + else + do_start + fi + ;; +stop) + do_stop + ;; +stats) + signal "USR1" + ;; +reload) + do_reload + ;; +running) + signal "0" + ;; +patch) + # patch queue clearen + if test -s ${difffile}; then + #${sbindir}/nsd-patch -c ${configfile} -x ${difffile} -l #debug + #echo ${sbindir}/nsd-patch -c ${configfile} -x ${difffile} + if do_patch ${PATCH_STYLE}; then + do_reload + else + unlock + # try to move back the transfer data + if [ -e ${difffile}.$$ -a ! -e ${difffile} ]; then + mv ${difffile}.$$ ${difffile} + fi + echo "`basename $0`: patch failed." + fi + else + echo "`basename $0`: no patch necessary." + fi + ;; +rebuild) + do_rebuild + ;; +update) + # send notifies to localhost for all zones that allow it + echo "Sending notify to localhost to update secondary zones..." + if [ -s ${pidfile} ]; then + zoneslist=`${nsd_checkconf} -o zones ${configfile}` + for zonename in ${zoneslist}; do + notify_allow=`${nsd_checkconf} -z "${zonename}" -o allow-notify ${configfile}` + local_ifc=`${nsd_checkconf} -z "${zonename}" -o outgoing-interface ${configfile}` + if test "" = "${local_ifc}"; then + local_ifc="NOIFC" + fi + if test "" != "${notify_allow}"; then + for ifc in ${local_ifc}; do + send_updates ${zonename} ${ifc} ${notify_allow} + done + fi + done + else + echo "nsd is not running" + fi + ;; +notify) + # send notifies to all slaves + echo "Sending notify to slave servers..." + zoneslist=`${nsd_checkconf} -o zones ${configfile}` + for zonename in ${zoneslist}; do + notify=`${nsd_checkconf} -z "${zonename}" -o notify ${configfile}` + local_ifc=`${nsd_checkconf} -z "${zonename}" -o outgoing-interface ${configfile}` + if test "" = "${local_ifc}"; then + local_ifc="NOIFC" + fi + if test "" != "${notify}"; then + for ifc in ${local_ifc}; do + send_notify ${zonename} ${ifc} ${notify} + done + fi + done + ;; +restart) + do_controlled_stop && do_start + ;; +*) + usage + ;; +esac + +exit $? diff --git a/usr.sbin/nsd/nsec3.c b/usr.sbin/nsd/nsec3.c new file mode 100644 index 00000000000..b285e9a28be --- /dev/null +++ b/usr.sbin/nsd/nsec3.c @@ -0,0 +1,624 @@ +/* + * nsec3.c -- nsec3 handling. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ +#include <config.h> +#ifdef NSEC3 +#include <stdio.h> +#include <stdlib.h> + +#include "nsec3.h" +#include "iterated_hash.h" +#include "namedb.h" +#include "nsd.h" +#include "answer.h" + +#define NSEC3_SHA1_HASH 1 /* same type code as DS hash */ + +/* detect is the latter rrset has the same hashalgo, iterations and salt + as the base. Does not compare optout bit, or other rdata. + base=NULL uses the zone soa_rr. */ +static int nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset); + +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]; + *salt = (unsigned char*)(rdata_atom_data(nsec3_apex->rdatas[3])+1); + *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) +{ + unsigned char hash[SHA_DIGEST_LENGTH]; + char b32[SHA_DIGEST_LENGTH*2+1]; + const unsigned char* nsec3_salt = NULL; + int nsec3_saltlength = 0; + int nsec3_iterations = 0; + + detect_nsec3_params(param_rr, &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; +} + +const dname_type * +nsec3_hash_dname(region_type *region, zone_type *zone, + const dname_type *dname) +{ + return nsec3_hash_dname_param(region, zone, + dname, zone->nsec3_soa_rr); +} + +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 */ + return 1; + return 0; +} + +static rr_type* +find_zone_nsec3(namedb_type* namedb, zone_type *zone) +{ + size_t i; + domain_type* domain; + 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 0; + 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; + + 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; + } + /* find SOA bit enabled nsec3, with the same settings */ + for(j=0; j<nsec3_rrset->rr_count; j++) + { + const unsigned char *salt1, *salt2; + int saltlen1, saltlen2, iter1, iter2; + if(!nsec3_has_soa(&nsec3_rrset->rrs[j])) + continue; + /* check params OK. Ignores the optout bit. */ + detect_nsec3_params(rr, &salt1, &saltlen1, &iter1); + 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]; + } + } + log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) no NSEC3 with SOAbit", + dname_to_string(domain_dname(zone->apex), NULL), (int)i); + } + region_destroy(tmpregion); + return 0; +} + +/* 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) +{ + rdata_atom_type* prd; + rdata_atom_type* rd; + 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) + { + 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; + } + } + return 0; +} + +int +nsec3_find_cover(namedb_type* db, zone_type* zone, + const dname_type* hashname, domain_type** result) +{ + rrset_type *rrset; + domain_type *walk; + domain_type *closest_match; + domain_type *closest_encloser; + int exact; + + 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; + } + + /* 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; + } + 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; + } + assert(*result != 0); + return 0; +} + +static void +prehash_domain(namedb_type* db, zone_type* zone, + domain_type* domain, region_type* region) +{ + /* find it */ + domain_type* result = 0; + const dname_type *wcard, *wcard_child, *hashname; + int exact; + + 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; + } + + hashname = nsec3_hash_dname(region, zone, domain_dname(domain)); + exact = nsec3_find_cover(db, zone, hashname, &result); + domain->nsec3_cover = result; + if(exact) + domain->nsec3_is_exact = 1; + else domain->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)); + } +} + +static void +prehash_ds(namedb_type* db, zone_type* zone, + domain_type* domain, region_type* region) +{ + 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; + 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; +} + +static void +prehash_zone(struct namedb* db, struct zone* zone) +{ + 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 = 0; + 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); + } + 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); + } + region_destroy(temp_region); +} + +void +prehash(struct namedb* db, int updated_only) +{ + 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++; + } + } + end = time(NULL); + if(count > 0) + VERBOSITY(1, (LOG_INFO, "nsec3-prepare took %d " + "seconds for %d zones.", (int)(end-start), count)); +} + +/* add the NSEC3 rrset to the query answer at the given domain */ +static void +nsec3_add_rrset(struct query *query, struct answer *answer, + rr_section_type section, struct domain* domain) +{ + if(domain) { + rrset_type* rrset = domain_find_rrset(domain, query->zone, TYPE_NSEC3); + if(rrset) + answer_add_rrset(answer, section, domain, rrset); + } +} + +/* 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) +{ + const dname_type *to_prove, *hashed; + 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 */ + hashed = nsec3_hash_dname(query->region, query->zone, to_prove); + if(nsec3_find_cover(db, query->zone, hashed, &cover)) + { + /* exact match, hash collision */ + /* the hashed name of the query corresponds to an existing name. */ + log_msg(LOG_ERR, "nsec3 hash collision for name=%s", + dname_to_string(to_prove, NULL)); + RCODE_SET(query->packet, RCODE_SERVFAIL); + return; + } + else + { + /* cover proves the qname does not exist */ + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, cover); + } +} + +static void +nsec3_add_closest_encloser_proof( + struct query *query, struct answer *answer, + struct domain *closest_encloser, struct namedb* db, + 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); + /* proof that closest encloser exists */ + if(closest_encloser->nsec3_is_exact) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + closest_encloser->nsec3_cover); +} + +void +nsec3_answer_wildcard(struct query *query, struct answer *answer, + struct domain *wildcard, struct namedb* db, const dname_type* qname) +{ + if(!wildcard) + return; + if(!query->zone->nsec3_soa_rr) + return; + nsec3_add_nonexist_proof(query, answer, wildcard, db, qname); +} + +static void +nsec3_add_ds_proof(struct query *query, struct answer *answer, + struct domain *domain) +{ + /* assert we are above the zone cut */ + assert(domain != query->zone->apex); + if(domain->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 { + /* prove closest provable encloser */ + domain_type* par = domain->parent; + domain_type* prev_par = 0; + while(par && !par->nsec3_is_exact) + { + prev_par = par; + par = par->parent; + } + assert(par); /* parent zone apex must be provable, thus this ends */ + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + par->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) { + assert(prev_par != domain && !prev_par->nsec3_is_exact); + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + prev_par->nsec3_cover); + } + /* add optout range from parent zone */ + /* note: no check of optout bit, resolver checks it */ + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + domain->nsec3_ds_parent_cover); + } +} + +void +nsec3_answer_nodata(struct query *query, struct answer *answer, + struct domain *original) +{ + if(!query->zone->nsec3_soa_rr) + 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 */ + if(original->nsec3_is_exact) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + original->nsec3_cover); + return; + } + /* query->zone must be the parent zone */ + nsec3_add_ds_proof(query, answer, original); + } + /* the nodata is result from a wildcard match */ + else if (original==original->wildcard_child_closest_match + && label_is_wildcard(dname_name(domain_dname(original)))) { + /* denial for wildcard is already there */ + /* add parent proof to have a closest encloser proof for wildcard parent */ + if(original->parent && original->parent->nsec3_is_exact) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + original->parent->nsec3_cover); + /* proof for wildcard itself */ + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + original->nsec3_cover); + } + else { /* add nsec3 to prove rrset does not exist */ + if(original->nsec3_is_exact) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + original->nsec3_cover); + } +} + +void +nsec3_answer_delegation(struct query *query, struct answer *answer) +{ + if(!query->zone->nsec3_soa_rr) + return; + nsec3_add_ds_proof(query, answer, query->delegation_domain); +} + +int +domain_has_only_NSEC3(struct domain* domain, struct zone* zone) +{ + /* check for only NSEC3/RRSIG */ + rrset_type* rrset = domain->rrsets; + int nsec3_seen = 0, rrsig_seen = 0; + while(rrset) + { + if(!zone || rrset->zone == zone) + { + if(rrset->rrs[0].type == TYPE_NSEC3) + nsec3_seen = 1; + else if(rrset->rrs[0].type == TYPE_RRSIG) + rrsig_seen = 1; + else + return 0; + } + rrset = rrset->next; + } + return nsec3_seen; +} + +void +nsec3_answer_authoritative(struct domain** match, struct query *query, + struct answer *answer, struct domain* closest_encloser, + struct namedb* db, const dname_type* qname) +{ + if(!query->zone->nsec3_soa_rr) + return; + assert(match); + /* there is a match, this has 1 RRset, which is NSEC3, but qtype is not. */ + if(*match && +#if 0 + query->qtype != TYPE_NSEC3 && +#endif + domain_has_only_NSEC3(*match, query->zone)) + { + /* act as if the NSEC3 domain did not exist, name error */ + *match = 0; + /* all nsec3s are directly below the apex, that is closest encloser */ + if(query->zone->apex->nsec3_is_exact) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + query->zone->apex->nsec3_cover); + /* disprove the nsec3 record. */ + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3_cover); + /* disprove a wildcard */ + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, query->zone->apex-> + nsec3_wcard_child_cover); + if (domain_wildcard_child(query->zone->apex)) { + /* wildcard exists below the domain */ + /* wildcard and nsec3 domain clash. server failure. */ + RCODE_SET(query->packet, RCODE_SERVFAIL); + } + return; + } + if(!*match) { + /* name error, domain does not exist */ + nsec3_add_closest_encloser_proof(query, answer, closest_encloser, + db, qname); + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + closest_encloser->nsec3_wcard_child_cover); + } +} + +#endif /* NSEC3 */ diff --git a/usr.sbin/nsd/nsec3.h b/usr.sbin/nsd/nsec3.h new file mode 100644 index 00000000000..af87113ed56 --- /dev/null +++ b/usr.sbin/nsd/nsec3.h @@ -0,0 +1,83 @@ +/* + * nsec3.h -- nsec3 handling. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ +#ifndef NSEC3_H +#define NSEC3_H + +#include <config.h> +#ifdef NSEC3 + +struct domain; +struct dname; +struct region; +struct zone; +struct namedb; +struct query; +struct answer; + +/* + * Create the hashed name of the nsec3 record + * for the given dname. + */ +const struct dname *nsec3_hash_dname(struct region *region, + struct zone *zone, const struct dname *dname); + +/* + * calculate prehash information for all zones, + * selects only updated=1 zones if bool set. + */ +void prehash(struct namedb* db, int updated_only); + +/* + * finds nsec3 that covers the given domain dname. + * returns true if the find is exact. + * hashname is the already hashed dname for the NSEC3. + */ +int nsec3_find_cover(struct namedb* db, struct zone* zone, + const struct dname* hashname, struct domain** result); + +/* + * _answer_ Routines used to add the correct nsec3 record to a query answer. + * cnames etc may have been followed, hence original name. + */ +/* + * add proof for wildcards that the name below the wildcard.parent + * does not exist + */ +void nsec3_answer_wildcard(struct query *query, struct answer *answer, + struct domain *wildcard, struct namedb* db, + const struct dname *qname); + +/* + * add NSEC3 to provide domain name but not rrset exists, + * this could be a query for a DS or NSEC3 type + */ +void nsec3_answer_nodata(struct query *query, struct answer *answer, + struct domain *original); + +/* + * add NSEC3 for a delegation (optout stuff) + */ +void nsec3_answer_delegation(struct query *query, struct answer *answer); + +/* + * add NSEC3 for authoritative answers. + * match==0 is an nxdomain. + */ +void nsec3_answer_authoritative(struct domain** match, struct query *query, + struct answer *answer, struct domain* closest_encloser, + struct namedb* db, const struct dname* qname); + +/* + * True if domain is a NSEC3 (+RRSIG) data only variety. + * pass nonNULL zone to filter for particular zone. + */ +int domain_has_only_NSEC3(struct domain* domain, struct zone* zone); + +#endif /* NSEC3 */ +#endif /* NSEC3_H*/ diff --git a/usr.sbin/nsd/options.c b/usr.sbin/nsd/options.c new file mode 100644 index 00000000000..9ce47f398bf --- /dev/null +++ b/usr.sbin/nsd/options.c @@ -0,0 +1,666 @@ +/* + * options.c -- options functions. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ +#include <config.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include "options.h" +#include "query.h" +#include "tsig.h" +#include "difffile.h" + +#include "configyyrename.h" +#include "configparser.h" +nsd_options_t* nsd_options = 0; +config_parser_state_t* cfg_parser = 0; +extern FILE* c_in, *c_out; +int c_parse(void); +int c_lex(void); +int c_wrap(void); +void c_error(const char *message); + +nsd_options_t* nsd_options_create(region_type* region) +{ + nsd_options_t* opt; + opt = (nsd_options_t*)region_alloc(region, sizeof(nsd_options_t)); + opt->region = region; + opt->zone_options = rbtree_create(region, + (int (*)(const void *, const void *)) dname_compare); + opt->keys = NULL; + opt->numkeys = 0; + opt->ip_addresses = NULL; + opt->debug_mode = 0; + opt->verbosity = 0; + opt->hide_version = 0; + opt->ip4_only = 0; + opt->ip6_only = 0; + opt->database = DBFILE; + opt->identity = 0; + opt->logfile = 0; + opt->server_count = 1; + opt->tcp_count = 10; + opt->tcp_query_count = 0; + opt->tcp_timeout = TCP_TIMEOUT; + opt->ipv4_edns_size = EDNS_MAX_MESSAGE_LEN; + opt->ipv6_edns_size = EDNS_MAX_MESSAGE_LEN; + opt->pidfile = PIDFILE; + opt->port = UDP_PORT; +/* deprecated? opt->port = TCP_PORT; */ + opt->statistics = 0; + opt->chroot = 0; + opt->username = USER; + opt->zonesdir = ZONESDIR; + opt->difffile = DIFFFILE; + opt->xfrdfile = XFRDFILE; + opt->xfrd_reload_timeout = 10; + nsd_options = opt; + return opt; +} + +int nsd_options_insert_zone(nsd_options_t* opt, zone_options_t* zone) +{ + /* create dname for lookup */ + const dname_type* dname = dname_parse(opt->region, zone->name); + if(!dname) + return 0; + zone->node.key = dname; + if(!rbtree_insert(opt->zone_options, (rbnode_t*)zone)) + return 0; + return 1; +} + +int parse_options_file(nsd_options_t* opt, const char* file) +{ + FILE *in = 0; + zone_options_t* zone; + acl_options_t* acl; + + if(!cfg_parser) + cfg_parser = (config_parser_state_t*)region_alloc( + opt->region, sizeof(config_parser_state_t)); + cfg_parser->filename = file; + cfg_parser->line = 1; + cfg_parser->errors = 0; + cfg_parser->opt = opt; + cfg_parser->current_zone = 0; + cfg_parser->current_key = opt->keys; + while(cfg_parser->current_key && cfg_parser->current_key->next) + cfg_parser->current_key = cfg_parser->current_key->next; + cfg_parser->current_ip_address_option = opt->ip_addresses; + while(cfg_parser->current_ip_address_option && cfg_parser->current_ip_address_option->next) + cfg_parser->current_ip_address_option = cfg_parser->current_ip_address_option->next; + cfg_parser->current_allow_notify = 0; + cfg_parser->current_request_xfr = 0; + cfg_parser->current_notify = 0; + cfg_parser->current_provide_xfr = 0; + + in = fopen(cfg_parser->filename, "r"); + if(!in) { + fprintf(stderr, "Could not open %s: %s\n", file, strerror(errno)); + return 0; + } + c_in = in; + c_parse(); + fclose(in); + + if(cfg_parser->current_zone) { + if(!cfg_parser->current_zone->name) + c_error("last zone has no name"); + else { + if(!nsd_options_insert_zone(opt, + cfg_parser->current_zone)) + c_error("duplicate zone"); + } + if(!cfg_parser->current_zone->zonefile) + c_error("last zone has no zonefile"); + } + if(opt->keys) + { + if(!opt->keys->name) + c_error("last key has no name"); + if(!opt->keys->algorithm) + c_error("last key has no algorithm"); + if(!opt->keys->secret) + c_error("last key has no secret blob"); + } + RBTREE_FOR(zone, zone_options_t*, opt->zone_options) + { + if(!zone->name) + continue; + if(!zone->zonefile) + continue; + /* lookup keys for acls */ + for(acl=zone->allow_notify; acl; acl=acl->next) + { + if(acl->nokey || acl->blocked) + continue; + acl->key_options = key_options_find(opt, acl->key_name); + if(!acl->key_options) + c_error_msg("key %s in zone %s could not be found", + acl->key_name, zone->name); + } + for(acl=zone->notify; acl; acl=acl->next) + { + if(acl->nokey || acl->blocked) + continue; + acl->key_options = key_options_find(opt, acl->key_name); + if(!acl->key_options) + c_error_msg("key %s in zone %s could not be found", + acl->key_name, zone->name); + } + for(acl=zone->request_xfr; acl; acl=acl->next) + { + if(acl->nokey || acl->blocked) + continue; + acl->key_options = key_options_find(opt, acl->key_name); + if(!acl->key_options) + c_error_msg("key %s in zone %s could not be found", + acl->key_name, zone->name); + } + for(acl=zone->provide_xfr; acl; acl=acl->next) + { + if(acl->nokey || acl->blocked) + continue; + acl->key_options = key_options_find(opt, acl->key_name); + if(!acl->key_options) + c_error_msg("key %s in zone %s could not be found", + acl->key_name, zone->name); + } + } + + if(cfg_parser->errors > 0) + { + fprintf(stderr, "read %s failed: %d errors in configuration file\n", + cfg_parser->filename, + cfg_parser->errors); + return 0; + } + return 1; +} + +void c_error_va_list(const char *fmt, va_list args) +{ + cfg_parser->errors++; + fprintf(stderr, "%s:%d: error: ", cfg_parser->filename, + cfg_parser->line); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} + +void c_error_msg(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + c_error_va_list(fmt, args); + va_end(args); +} + +void c_error(const char *str) +{ + cfg_parser->errors++; + fprintf(stderr, "%s:%d: error: %s\n", cfg_parser->filename, + cfg_parser->line, str); +} + +int c_wrap() +{ + return 1; +} + +zone_options_t* zone_options_create(region_type* region) +{ + zone_options_t* zone; + zone = (zone_options_t*)region_alloc(region, sizeof(zone_options_t)); + zone->node = *RBTREE_NULL; + zone->name = 0; + zone->zonefile = 0; + zone->allow_notify = 0; + zone->request_xfr = 0; + zone->notify = 0; + zone->notify_retry = 5; + zone->provide_xfr = 0; + zone->outgoing_interface = 0; + zone->allow_axfr_fallback = 1; + return zone; +} + +key_options_t* key_options_create(region_type* region) +{ + key_options_t* key; + key = (key_options_t*)region_alloc(region, sizeof(key_options_t)); + key->name = 0; + key->next = 0; + key->algorithm = 0; + key->secret = 0; +#ifdef TSIG + key->tsig_key = 0; +#endif + return key; +} + +key_options_t* key_options_find(nsd_options_t* opt, const char* name) +{ + key_options_t* key = opt->keys; + while(key) { + if(strcmp(key->name, name)==0) + return key; + key = key->next; + } + return 0; +} + +int acl_check_incoming(acl_options_t* acl, struct query* q, + acl_options_t** reason) +{ + /* check each acl element. + if 1 blocked element matches - return -1. + if any element matches - return number. + else return -1. */ + int found_match = -1; + int number = 0; + acl_options_t* match = 0; + + if(reason) + *reason = NULL; + + while(acl) + { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "testing acl %s %s", + acl->ip_address_spec, acl->nokey?"NOKEY": + (acl->blocked?"BLOCKED":acl->key_name))); + if(acl_addr_matches(acl, q) && acl_key_matches(acl, q)) { + if(!match) + { + match = acl; /* remember first match */ + found_match=number; + } + if(acl->blocked) { + if(reason) + *reason = acl; + return -1; + } + } + number++; + acl = acl->next; + } + + if(reason) + *reason = match; + return found_match; +} + +int acl_addr_matches(acl_options_t* acl, struct query* q) +{ + if(acl->is_ipv6) + { +#ifdef INET6 + struct sockaddr_storage* addr_storage = (struct sockaddr_storage*)&q->addr; + struct sockaddr_in6* addr = (struct sockaddr_in6*)&q->addr; + if(addr_storage->ss_family != AF_INET6) + return 0; + if(acl->port != 0 && acl->port != ntohs(addr->sin6_port)) + return 0; + switch(acl->rangetype) { + case acl_range_mask: + case acl_range_subnet: + if(!acl_addr_match_mask((uint32_t*)&acl->addr.addr6, (uint32_t*)&addr->sin6_addr, + (uint32_t*)&acl->range_mask.addr6, sizeof(struct in6_addr))) + return 0; + break; + case acl_range_minmax: + if(!acl_addr_match_range((uint32_t*)&acl->addr.addr6, (uint32_t*)&addr->sin6_addr, + (uint32_t*)&acl->range_mask.addr6, sizeof(struct in6_addr))) + return 0; + break; + case acl_range_single: + default: + if(memcmp(&addr->sin6_addr, &acl->addr.addr6, + sizeof(struct in6_addr)) != 0) + return 0; + break; + } + return 1; +#else + return 0; /* no inet6, no match */ +#endif + } + else + { + struct sockaddr_in* addr = (struct sockaddr_in*)&q->addr; + if(addr->sin_family != AF_INET) + return 0; + if(acl->port != 0 && acl->port != ntohs(addr->sin_port)) + return 0; + switch(acl->rangetype) { + case acl_range_mask: + case acl_range_subnet: + if(!acl_addr_match_mask((uint32_t*)&acl->addr.addr, (uint32_t*)&addr->sin_addr, + (uint32_t*)&acl->range_mask.addr, sizeof(struct in_addr))) + return 0; + break; + case acl_range_minmax: + if(!acl_addr_match_range((uint32_t*)&acl->addr.addr, (uint32_t*)&addr->sin_addr, + (uint32_t*)&acl->range_mask.addr, sizeof(struct in_addr))) + return 0; + break; + case acl_range_single: + default: + if(memcmp(&addr->sin_addr, &acl->addr.addr, + sizeof(struct in_addr)) != 0) + return 0; + break; + } + return 1; + } + /* ENOTREACH */ + return 0; +} + +int acl_addr_match_mask(uint32_t* a, uint32_t* b, uint32_t* mask, size_t sz) +{ + size_t i; +#ifndef NDEBUG + assert(sz % 4 == 0); +#endif + sz /= 4; + for(i=0; i<sz; ++i) + { + if(((*a++)&*mask) != ((*b++)&*mask)) + return 0; + ++mask; + } + return 1; +} + +int acl_addr_match_range(uint32_t* minval, uint32_t* x, uint32_t* maxval, size_t sz) +{ + size_t i; + uint8_t checkmin = 1, checkmax = 1; +#ifndef NDEBUG + assert(sz % 4 == 0); +#endif + /* check treats x as one huge number */ + sz /= 4; + for(i=0; i<sz; ++i) + { + /* if outside bounds, we are done */ + if(checkmin) + if(minval[i] > x[i]) + return 0; + if(checkmax) + if(maxval[i] < x[i]) + return 0; + /* if x is equal to a bound, that bound needs further checks */ + if(checkmin && minval[i]!=x[i]) + checkmin = 0; + if(checkmax && maxval[i]!=x[i]) + checkmax = 0; + if(!checkmin && !checkmax) + return 1; /* will always match */ + } + return 1; +} + +int acl_key_matches(acl_options_t* acl, struct query* q) +{ + if(acl->blocked) + return 1; +#ifdef TSIG + if(acl->nokey) { + if(q->tsig.status == TSIG_NOT_PRESENT) + return 1; + return 0; + } + /* check name of tsig key */ + if(q->tsig.status != TSIG_OK) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "keymatch fail query has no TSIG")); + return 0; /* query has no TSIG */ + } + if(q->tsig.error_code != TSIG_ERROR_NOERROR) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "keymatch fail, tsig has error")); + return 0; /* some tsig error */ + } + if(!acl->key_options->tsig_key) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "keymatch fail no config")); + return 0; /* key not properly configged */ + } + if(dname_compare(q->tsig.key_name, + acl->key_options->tsig_key->name) != 0) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "keymatch fail wrong key name")); + return 0; /* wrong key name */ + } + if(tsig_strlowercmp(q->tsig.algorithm->short_name, + acl->key_options->algorithm) != 0) { + DEBUG(DEBUG_XFRD,2, (LOG_ERR, "query tsig wrong algorithm")); + return 0; /* no such algo */ + } + return 1; +#else + if(acl->nokey) + return 1; + return 0; +#endif +} + +int +acl_same_host(acl_options_t* a, acl_options_t* b) +{ + if(a->is_ipv6 && !b->is_ipv6) + return 0; + if(!a->is_ipv6 && b->is_ipv6) + return 0; + if(a->port != b->port) + return 0; + if(a->rangetype != b->rangetype) + return 0; + if(!a->is_ipv6) { + if(memcmp(&a->addr.addr, &b->addr.addr, + sizeof(struct in_addr)) != 0) + return 0; + if(a->rangetype != acl_range_single && + memcmp(&a->range_mask.addr, &b->range_mask.addr, + sizeof(struct in_addr)) != 0) + return 0; + } else { +#ifdef INET6 + if(memcmp(&a->addr.addr6, &b->addr.addr6, + sizeof(struct in6_addr)) != 0) + return 0; + if(a->rangetype != acl_range_single && + memcmp(&a->range_mask.addr6, &b->range_mask.addr6, + sizeof(struct in6_addr)) != 0) + return 0; +#else + return 0; +#endif + } + return 1; +} + +void key_options_tsig_add(nsd_options_t* opt) +{ +#if defined(TSIG) && defined(HAVE_SSL) + key_options_t* optkey; + uint8_t data[4000]; + tsig_key_type* tsigkey; + const dname_type* dname; + int size; + + for(optkey = opt->keys; optkey; optkey = optkey->next) + { + dname = dname_parse(opt->region, optkey->name); + if(!dname) { + log_msg(LOG_ERR, "Failed to parse tsig key name %s", optkey->name); + continue; + } + size = b64_pton(optkey->secret, data, sizeof(data)); + if(size == -1) { + log_msg(LOG_ERR, "Failed to parse tsig key data %s", optkey->name); + continue; + } + tsigkey = (tsig_key_type *) region_alloc(opt->region, sizeof(tsig_key_type)); + tsigkey->name = dname; + tsigkey->size = size; + tsigkey->data = (uint8_t *) region_alloc_init(opt->region, data, tsigkey->size); + tsig_add_key(tsigkey); + optkey->tsig_key = tsigkey; + } +#endif +} + +int zone_is_slave(zone_options_t* opt) +{ + return opt->request_xfr != 0; +} + +zone_options_t* zone_options_find(nsd_options_t* opt, const struct dname* apex) +{ + return (zone_options_t*) rbtree_search(opt->zone_options, apex); +} + +acl_options_t* +acl_find_num(acl_options_t* acl, int num) +{ + int count = num; + if(num < 0) + return 0; + while(acl && count > 0) { + acl = acl->next; + count--; + } + if(count == 0) + return acl; + return 0; +} + +/* true if ipv6 address, false if ipv4 */ +int parse_acl_is_ipv6(const char* p) +{ + /* see if addr is ipv6 or ipv4 -- by : and . */ + while(*p) { + if(*p == '.') return 0; + if(*p == ':') return 1; + ++p; + } + return 0; +} + +/* returns range type. mask is the 2nd part of the range */ +int parse_acl_range_type(char* ip, char** mask) +{ + char *p; + if((p=strchr(ip, '&'))!=0) { + *p = 0; + *mask = p+1; + return acl_range_mask; + } + if((p=strchr(ip, '/'))!=0) { + *p = 0; + *mask = p+1; + return acl_range_subnet; + } + if((p=strchr(ip, '-'))!=0) { + *p = 0; + *mask = p+1; + return acl_range_minmax; + } + *mask = 0; + return acl_range_single; +} + +/* parses subnet mask, fills 0 mask as well */ +void parse_acl_range_subnet(char* p, void* addr, int maxbits) +{ + int subnet_bits = atoi(p); + uint8_t* addr_bytes = (uint8_t*)addr; + if(subnet_bits == 0 && strcmp(p, "0")!=0) { + c_error_msg("bad subnet range '%s'", p); + return; + } + if(subnet_bits < 0 || subnet_bits > maxbits) { + c_error_msg("subnet of %d bits out of range [0..%d]", subnet_bits, maxbits); + return; + } + /* fill addr with n bits of 1s (struct has been zeroed) */ + while(subnet_bits >= 8) { + *addr_bytes++ = 0xff; + subnet_bits -= 8; + } + if(subnet_bits > 0) { + uint8_t shifts[] = {0x0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; + *addr_bytes = shifts[subnet_bits]; + } +} + +acl_options_t* parse_acl_info(region_type* region, char* ip, const char* key) +{ + char* p; + acl_options_t* acl = (acl_options_t*)region_alloc(region, sizeof(acl_options_t)); + acl->next = 0; + /* ip */ + acl->ip_address_spec = region_strdup(region, ip); + acl->use_axfr_only = 0; + acl->allow_udp = 0; + acl->ixfr_disabled = 0; + acl->key_options = 0; + acl->is_ipv6 = 0; + acl->port = 0; + memset(&acl->addr, 0, sizeof(union acl_addr_storage)); + memset(&acl->range_mask, 0, sizeof(union acl_addr_storage)); + if((p=strrchr(ip, '@'))!=0) { + if(atoi(p+1) == 0) c_error("expected port number after '@'"); + else acl->port = atoi(p+1); + *p=0; + } + acl->rangetype = parse_acl_range_type(ip, &p); + if(parse_acl_is_ipv6(ip)) { + acl->is_ipv6 = 1; +#ifdef INET6 + if(inet_pton(AF_INET6, ip, &acl->addr.addr6) != 1) + c_error_msg("Bad ip6 address '%s'", ip); + if(acl->rangetype==acl_range_mask || acl->rangetype==acl_range_minmax) + if(inet_pton(AF_INET6, p, &acl->range_mask.addr6) != 1) + c_error_msg("Bad ip6 address mask '%s'", p); + if(acl->rangetype==acl_range_subnet) + parse_acl_range_subnet(p, &acl->range_mask.addr6, 128); +#else + c_error_msg("encountered IPv6 address '%s'.", ip); +#endif /* INET6 */ + } else { + acl->is_ipv6 = 0; + if(inet_pton(AF_INET, ip, &acl->addr.addr) != 1) + c_error_msg("Bad ip4 address '%s'", ip); + if(acl->rangetype==acl_range_mask || acl->rangetype==acl_range_minmax) + if(inet_pton(AF_INET, p, &acl->range_mask.addr) != 1) + c_error_msg("Bad ip4 address mask '%s'", p); + if(acl->rangetype==acl_range_subnet) + parse_acl_range_subnet(p, &acl->range_mask.addr, 32); + } + + /* key */ + if(strcmp(key, "NOKEY")==0) { + acl->nokey = 1; + acl->blocked = 0; + acl->key_name = 0; + } else if(strcmp(key, "BLOCKED")==0) { + acl->nokey = 0; + acl->blocked = 1; + acl->key_name = 0; + } else { + acl->nokey = 0; + acl->blocked = 0; + acl->key_name = region_strdup(region, key); + } + return acl; +} + +void nsd_options_destroy(nsd_options_t* opt) +{ + region_destroy(opt->region); +} diff --git a/usr.sbin/nsd/options.h b/usr.sbin/nsd/options.h new file mode 100644 index 00000000000..b676cd8ce9b --- /dev/null +++ b/usr.sbin/nsd/options.h @@ -0,0 +1,214 @@ +/* + * options.h -- nsd.conf options definitions and prototypes + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +#include <config.h> +#include <stdarg.h> +#include "region-allocator.h" +#include "rbtree.h" +struct query; +struct dname; +struct tsig_key; + +typedef struct nsd_options nsd_options_t; +typedef struct zone_options zone_options_t; +typedef struct ipaddress_option ip_address_option_t; +typedef struct acl_options acl_options_t; +typedef struct key_options key_options_t; +typedef struct config_parser_state config_parser_state_t; +/* + * Options global for nsd. + */ +struct nsd_options { + /* options for zones, by apex, contains zone_options_t */ + rbtree_t* zone_options; + + /* list of keys defined */ + key_options_t* keys; + size_t numkeys; + + /* list of ip adresses to bind to (or NULL for all) */ + ip_address_option_t* ip_addresses; + + int debug_mode; + int verbosity; + int hide_version; + int ip4_only; + int ip6_only; + const char* database; + const char* identity; + const char* logfile; + int server_count; + int tcp_count; + int tcp_query_count; + int tcp_timeout; + size_t ipv4_edns_size; + size_t ipv6_edns_size; + const char* pidfile; + const char* port; + int statistics; + const char* chroot; + const char* username; + const char* zonesdir; + const char* difffile; + const char* xfrdfile; + int xfrd_reload_timeout; + + region_type* region; +}; + +struct ipaddress_option { + ip_address_option_t* next; + char* address; +}; + +/* + * Options for a zone + */ +struct zone_options { + /* key is dname of apex */ + rbnode_t node; + + /* is apex of the zone */ + const char* name; + const char* zonefile; + acl_options_t* allow_notify; + acl_options_t* request_xfr; + acl_options_t* notify; + acl_options_t* provide_xfr; + acl_options_t* outgoing_interface; + uint8_t allow_axfr_fallback; + uint8_t notify_retry; +}; + +union acl_addr_storage { +#ifdef INET6 + struct in_addr addr; + struct in6_addr addr6; +#else + struct in_addr addr; +#endif +}; + +/* + * Access control list element + */ +struct acl_options { + acl_options_t* next; + + /* options */ + uint8_t use_axfr_only; + uint8_t allow_udp; + time_t ixfr_disabled; + + /* ip address range */ + const char* ip_address_spec; + uint8_t is_ipv6; + unsigned int port; /* is 0(no port) or suffix @port value */ + union acl_addr_storage addr; + union acl_addr_storage range_mask; + enum { + acl_range_single = 0, /* single adress */ + acl_range_mask = 1, /* 10.20.30.40&255.255.255.0 */ + acl_range_subnet = 2, /* 10.20.30.40/28 */ + acl_range_minmax = 3 /* 10.20.30.40-10.20.30.60 (mask=max) */ + } rangetype; + + /* key */ + uint8_t nokey; + uint8_t blocked; + const char* key_name; + key_options_t* key_options; +}; + +/* + * Key definition + */ +struct key_options { + key_options_t* next; + const char* name; + const char* algorithm; + const char* secret; +#ifdef TSIG + struct tsig_key* tsig_key; +#endif +}; + +/* + * Used during options parsing + */ +struct config_parser_state { + const char* filename; + int line; + int errors; + nsd_options_t* opt; + zone_options_t* current_zone; + key_options_t* current_key; + ip_address_option_t* current_ip_address_option; + acl_options_t* current_allow_notify; + acl_options_t* current_request_xfr; + acl_options_t* current_notify; + acl_options_t* current_provide_xfr; + acl_options_t* current_outgoing_interface; +}; + +extern config_parser_state_t* cfg_parser; + +/* region will be put in nsd_options struct. Returns empty options struct. */ +nsd_options_t* nsd_options_create(region_type* region); +/* the number of zones that are configured */ +static inline size_t nsd_options_num_zones(nsd_options_t* opt) +{ return opt->zone_options->count; } +/* insert a zone into the main options tree, returns 0 on error */ +int nsd_options_insert_zone(nsd_options_t* opt, zone_options_t* zone); + +/* parses options file. Returns false on failure */ +int parse_options_file(nsd_options_t* opt, const char* file); +zone_options_t* zone_options_create(region_type* region); +/* find a zone by apex domain name, or NULL if not found. */ +zone_options_t* zone_options_find(nsd_options_t* opt, const struct dname* apex); +key_options_t* key_options_create(region_type* region); +key_options_t* key_options_find(nsd_options_t* opt, const char* name); +/* tsig must be inited, adds all keys in options to tsig. */ +void key_options_tsig_add(nsd_options_t* opt); + +/* check acl list, acl number that matches if passed(0..), + * or failure (-1) if dropped */ +/* the reason why (the acl) is returned too (or NULL) */ +int acl_check_incoming(acl_options_t* acl, struct query* q, + acl_options_t** reason); +int acl_addr_matches(acl_options_t* acl, struct query* q); +int acl_key_matches(acl_options_t* acl, struct query* q); +int acl_addr_match_mask(uint32_t* a, uint32_t* b, uint32_t* mask, size_t sz); +int acl_addr_match_range(uint32_t* minval, uint32_t* x, uint32_t* maxval, size_t sz); + +/* returns true if acls are both from the same host */ +int acl_same_host(acl_options_t* a, acl_options_t* b); +/* find acl by number in the list */ +acl_options_t* acl_find_num(acl_options_t* acl, int num); + +/* see if a zone is a slave or a master zone */ +int zone_is_slave(zone_options_t* opt); + +/* parsing helpers */ +void c_error(const char* msg); +void c_error_msg(const char* fmt, ...) ATTR_FORMAT(printf, 1, 2); +acl_options_t* parse_acl_info(region_type* region, char* ip, const char* key); +/* true if ipv6 address, false if ipv4 */ +int parse_acl_is_ipv6(const char* p); +/* returns range type. mask is the 2nd part of the range */ +int parse_acl_range_type(char* ip, char** mask); +/* parses subnet mask, fills 0 mask as well */ +void parse_acl_range_subnet(char* p, void* addr, int maxbits); +/* clean up options */ +void nsd_options_destroy(nsd_options_t* opt); + +#endif /* OPTIONS_H */ diff --git a/usr.sbin/nsd/packet.c b/usr.sbin/nsd/packet.c new file mode 100644 index 00000000000..c3c9a8a1aad --- /dev/null +++ b/usr.sbin/nsd/packet.c @@ -0,0 +1,298 @@ +/* + * packet.c -- low-level DNS packet encoding and decoding functions. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <string.h> + +#include "packet.h" +#include "query.h" +#include "rdata.h" + +static void +encode_dname(query_type *q, domain_type *domain) +{ + while (domain->parent && query_get_dname_offset(q, domain) == 0) { + query_put_dname_offset(q, domain, buffer_position(q->packet)); + DEBUG(DEBUG_NAME_COMPRESSION, 2, + (LOG_INFO, "dname: %s, number: %lu, offset: %u\n", + dname_to_string(domain_dname(domain), NULL), + (unsigned long) domain->number, + query_get_dname_offset(q, domain))); + buffer_write(q->packet, dname_name(domain_dname(domain)), + label_length(dname_name(domain_dname(domain))) + 1U); + domain = domain->parent; + } + if (domain->parent) { + DEBUG(DEBUG_NAME_COMPRESSION, 2, + (LOG_INFO, "dname: %s, number: %lu, pointer: %u\n", + dname_to_string(domain_dname(domain), NULL), + (unsigned long) domain->number, + query_get_dname_offset(q, domain))); + assert(query_get_dname_offset(q, domain) <= MAX_COMPRESSION_OFFSET); + buffer_write_u16(q->packet, + 0xc000 | query_get_dname_offset(q, domain)); + } else { + buffer_write_u8(q->packet, 0); + } +} + +int +packet_encode_rr(query_type *q, domain_type *owner, rr_type *rr) +{ + size_t truncation_mark; + uint16_t rdlength = 0; + size_t rdlength_pos; + uint16_t j; + + assert(q); + assert(owner); + assert(rr); + + /* + * If the record does not in fit in the packet the packet size + * will be restored to the mark. + */ + truncation_mark = buffer_position(q->packet); + + encode_dname(q, owner); + buffer_write_u16(q->packet, rr->type); + buffer_write_u16(q->packet, rr->klass); + buffer_write_u32(q->packet, rr->ttl); + + /* Reserve space for rdlength. */ + rdlength_pos = buffer_position(q->packet); + buffer_skip(q->packet, sizeof(rdlength)); + + for (j = 0; j < rr->rdata_count; ++j) { + switch (rdata_atom_wireformat_type(rr->type, j)) { + case RDATA_WF_COMPRESSED_DNAME: + encode_dname(q, rdata_atom_domain(rr->rdatas[j])); + break; + case RDATA_WF_UNCOMPRESSED_DNAME: + { + const dname_type *dname = domain_dname( + rdata_atom_domain(rr->rdatas[j])); + buffer_write(q->packet, + dname_name(dname), dname->name_size); + break; + } + default: + buffer_write(q->packet, + rdata_atom_data(rr->rdatas[j]), + rdata_atom_size(rr->rdatas[j])); + break; + } + } + + if (!query_overflow(q)) { + rdlength = (buffer_position(q->packet) - rdlength_pos + - sizeof(rdlength)); + buffer_write_u16_at(q->packet, rdlength_pos, rdlength); + return 1; + } else { + buffer_set_position(q->packet, truncation_mark); + query_clear_dname_offsets(q, truncation_mark); + assert(!query_overflow(q)); + return 0; + } +} + +int +packet_encode_rrset(query_type *query, + domain_type *owner, + rrset_type *rrset, + int section) +{ + uint16_t i; + size_t truncation_mark; + uint16_t added = 0; + int all_added = 1; + int truncate_rrset = (section == ANSWER_SECTION || + section == AUTHORITY_SECTION); + rrset_type *rrsig; + + assert(rrset->rr_count > 0); + + truncation_mark = buffer_position(query->packet); + + for (i = 0; i < rrset->rr_count; ++i) { + if (packet_encode_rr(query, owner, &rrset->rrs[i])) { + ++added; + } else { + all_added = 0; + break; + } + } + + if (all_added && + query->edns.dnssec_ok && + zone_is_secure(rrset->zone) && + rrset_rrtype(rrset) != TYPE_RRSIG && + (rrsig = domain_find_rrset(owner, rrset->zone, TYPE_RRSIG))) + { + for (i = 0; i < rrsig->rr_count; ++i) { + if (rr_rrsig_type_covered(&rrsig->rrs[i]) + == rrset_rrtype(rrset)) + { + if (packet_encode_rr(query, owner, + &rrsig->rrs[i])) + { + ++added; + } else { + all_added = 0; + break; + } + } + } + } + + if (!all_added && truncate_rrset) { + /* Truncate entire RRset and set truncate flag. */ + buffer_set_position(query->packet, truncation_mark); + query_clear_dname_offsets(query, truncation_mark); + TC_SET(query->packet); + added = 0; + } + + return added; +} + +int +packet_skip_dname(buffer_type *packet) +{ + while (1) { + uint8_t label_size; + if (!buffer_available(packet, 1)) + return 0; + + label_size = buffer_read_u8(packet); + if (label_size == 0) { + return 1; + } else if ((label_size & 0xc0) != 0) { + if (!buffer_available(packet, 1)) + return 0; + buffer_skip(packet, 1); + return 1; + } else if (!buffer_available(packet, label_size)) { + return 0; + } else { + buffer_skip(packet, label_size); + } + } +} + +int +packet_skip_rr(buffer_type *packet, int question_section) +{ + if (!packet_skip_dname(packet)) + return 0; + + if (question_section) { + if (!buffer_available(packet, 4)) + return 0; + buffer_skip(packet, 4); + } else { + uint16_t rdata_size; + if (!buffer_available(packet, 10)) + return 0; + buffer_skip(packet, 8); + rdata_size = buffer_read_u16(packet); + if (!buffer_available(packet, rdata_size)) + return 0; + buffer_skip(packet, rdata_size); + } + + return 1; +} + +rr_type * +packet_read_rr(region_type *region, domain_table_type *owners, + buffer_type *packet, int question_section) +{ + const dname_type *owner; + uint16_t rdlength; + ssize_t rdata_count; + rdata_atom_type *rdatas; + rr_type *result = (rr_type *) region_alloc(region, sizeof(rr_type)); + + owner = dname_make_from_packet(region, packet, 1, 1); + if (!owner || !buffer_available(packet, 2*sizeof(uint16_t))) { + return NULL; + } + + result->owner = domain_table_insert(owners, owner); + result->type = buffer_read_u16(packet); + result->klass = buffer_read_u16(packet); + + if (question_section) { + result->ttl = 0; + result->rdata_count = 0; + result->rdatas = NULL; + return result; + } else if (!buffer_available(packet, sizeof(uint32_t) + sizeof(uint16_t))) { + return NULL; + } + + result->ttl = buffer_read_u32(packet); + rdlength = buffer_read_u16(packet); + + if (!buffer_available(packet, rdlength)) { + return NULL; + } + + rdata_count = rdata_wireformat_to_rdata_atoms( + region, owners, result->type, rdlength, packet, &rdatas); + if (rdata_count == -1) { + return NULL; + } + result->rdata_count = rdata_count; + result->rdatas = rdatas; + + return result; +} + +int packet_read_query_section(buffer_type *packet, + uint8_t* dst, uint16_t* qtype, uint16_t* qclass) +{ + uint8_t *query_name = buffer_current(packet); + uint8_t *src = query_name; + size_t len; + + while (*src) { + /* + * If we are out of buffer limits or we have a pointer + * in question dname or the domain name is longer than + * MAXDOMAINLEN ... + */ + if ((*src & 0xc0) || + (src + *src + 2 > buffer_end(packet)) || + (src + *src + 2 > query_name + MAXDOMAINLEN)) + { + return 0; + } + memcpy(dst, src, *src + 1); + dst += *src + 1; + src += *src + 1; + } + *dst++ = *src++; + + /* Make sure name is not too long or we have stripped packet... */ + len = src - query_name; + if (len > MAXDOMAINLEN || + (src + 2*sizeof(uint16_t) > buffer_end(packet))) + { + return 0; + } + buffer_set_position(packet, src - buffer_begin(packet)); + + *qtype = buffer_read_u16(packet); + *qclass = buffer_read_u16(packet); + return 1; +} diff --git a/usr.sbin/nsd/packet.h b/usr.sbin/nsd/packet.h new file mode 100644 index 00000000000..fe5dedb606f --- /dev/null +++ b/usr.sbin/nsd/packet.h @@ -0,0 +1,189 @@ +/* + * packet.h -- low-level DNS packet encoding and decoding functions. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _PACKET_H_ +#define _PACKET_H_ + +#include <sys/types.h> + +#include "dns.h" +#include "namedb.h" + +struct query; + +/* + * Set of macro's to deal with the dns message header as specified + * in RFC1035 in portable way. + * + */ + +/* + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ID | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QDCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ANCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | NSCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ARCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + */ + +/* The length of the header */ +#define QHEADERSZ 12 + +/* First octet of flags */ +#define RD_MASK 0x01U +#define RD_SHIFT 0 +#define RD(packet) (*buffer_at((packet), 2) & RD_MASK) +#define RD_SET(packet) (*buffer_at((packet), 2) |= RD_MASK) +#define RD_CLR(packet) (*buffer_at((packet), 2) &= ~RD_MASK) + +#define TC_MASK 0x02U +#define TC_SHIFT 1 +#define TC(packet) (*buffer_at((packet), 2) & TC_MASK) +#define TC_SET(packet) (*buffer_at((packet), 2) |= TC_MASK) +#define TC_CLR(packet) (*buffer_at((packet), 2) &= ~TC_MASK) + +#define AA_MASK 0x04U +#define AA_SHIFT 2 +#define AA(packet) (*buffer_at((packet), 2) & AA_MASK) +#define AA_SET(packet) (*buffer_at((packet), 2) |= AA_MASK) +#define AA_CLR(packet) (*buffer_at((packet), 2) &= ~AA_MASK) + +#define OPCODE_MASK 0x78U +#define OPCODE_SHIFT 3 +#define OPCODE(packet) ((*buffer_at((packet), 2) & OPCODE_MASK) >> OPCODE_SHIFT) +#define OPCODE_SET(packet, opcode) \ + (*buffer_at((packet), 2) = (*buffer_at((packet), 2) & ~OPCODE_MASK) | ((opcode) << OPCODE_SHIFT)) + +#define QR_MASK 0x80U +#define QR_SHIFT 7 +#define QR(packet) (*buffer_at((packet), 2) & QR_MASK) +#define QR_SET(packet) (*buffer_at((packet), 2) |= QR_MASK) +#define QR_CLR(packet) (*buffer_at((packet), 2) &= ~QR_MASK) + +/* Second octet of flags */ +#define RCODE_MASK 0x0fU +#define RCODE_SHIFT 0 +#define RCODE(packet) (*buffer_at((packet), 3) & RCODE_MASK) +#define RCODE_SET(packet, rcode) \ + (*buffer_at((packet), 3) = (*buffer_at((packet), 3) & ~RCODE_MASK) | (rcode)) + +#define CD_MASK 0x10U +#define CD_SHIFT 4 +#define CD(packet) (*buffer_at((packet), 3) & CD_MASK) +#define CD_SET(packet) (*buffer_at((packet), 3) |= CD_MASK) +#define CD_CLR(packet) (*buffer_at((packet), 3) &= ~CD_MASK) + +#define AD_MASK 0x20U +#define AD_SHIFT 5 +#define AD(packet) (*buffer_at((packet), 3) & AD_MASK) +#define AD_SET(packet) (*buffer_at((packet), 3) |= AD_MASK) +#define AD_CLR(packet) (*buffer_at((packet), 3) &= ~AD_MASK) + +#define Z_MASK 0x40U +#define Z_SHIFT 6 +#define Z(packet) (*buffer_at((packet), 3) & Z_MASK) +#define Z_SET(packet) (*buffer_at((packet), 3) |= Z_MASK) +#define Z_CLR(packet) (*buffer_at((packet), 3) &= ~Z_MASK) + +#define RA_MASK 0x80U +#define RA_SHIFT 7 +#define RA(packet) (*buffer_at((packet), 3) & RA_MASK) +#define RA_SET(packet) (*buffer_at((packet), 3) |= RA_MASK) +#define RA_CLR(packet) (*buffer_at((packet), 3) &= ~RA_MASK) + +/* Query ID */ +#define ID(packet) (buffer_read_u16_at((packet), 0)) +#define ID_SET(packet, id) (buffer_write_u16_at((packet), 0, (id))) + +/* Flags, RCODE, and OPCODE. */ +#define FLAGS(packet) (buffer_read_u16_at((packet), 2)) +#define FLAGS_SET(packet, f) (buffer_write_u16_at((packet), 2, (f))) + +/* Counter of the question section */ +#define QDCOUNT(packet) (buffer_read_u16_at((packet), 4)) +#define QDCOUNT_SET(packet, c) (buffer_write_u16_at((packet), 4, (c))) + +/* Counter of the answer section */ +#define ANCOUNT(packet) (buffer_read_u16_at((packet), 6)) +#define ANCOUNT_SET(packet, c) (buffer_write_u16_at((packet), 6, (c))) + +/* Counter of the authority section */ +#define NSCOUNT(packet) (buffer_read_u16_at((packet), 8)) +#define NSCOUNT_SET(packet, c) (buffer_write_u16_at((packet), 8, (c))) + +/* Counter of the additional section */ +#define ARCOUNT(packet) (buffer_read_u16_at((packet), 10)) +#define ARCOUNT_SET(packet, c) (buffer_write_u16_at((packet), 10, (c))) + +/* Miscelaneous limits */ +#define MAX_PACKET_SIZE 65535 /* Maximum supported size of DNS packets. */ + +#define QIOBUFSZ (MAX_PACKET_SIZE + MAX_RR_SIZE) + +#define MAXRRSPP 10240 /* Maximum number of rr's per packet */ +#define MAX_COMPRESSED_DNAMES MAXRRSPP /* Maximum number of compressed domains. */ +#define MAX_COMPRESSION_OFFSET 16383 /* Compression pointers are 14 bit. */ + +/* + * Encode RR with OWNER as owner name into QUERY. Returns the number + * of RRs successfully encoded. + */ +int packet_encode_rr(struct query *query, domain_type *owner, rr_type *rr); + +/* + * Encode RRSET with OWNER as the owner name into QUERY. Returns the + * number of RRs successfully encoded. If TRUNCATE_RRSET the entire + * RRset is truncated in case an RR (or the RRsets signature) does not + * fit. + */ +int packet_encode_rrset(struct query *query, + domain_type *owner, + rrset_type *rrset, + int truncate_rrset); + +/* + * Skip the RR at the current position in PACKET. + */ +int packet_skip_rr(buffer_type *packet, int question_section); + +/* + * Skip the dname at the current position in PACKET. + */ +int packet_skip_dname(buffer_type *packet); + +/* + * Read the RR at the current position in PACKET. + */ +rr_type *packet_read_rr(region_type *region, + domain_table_type *owners, + buffer_type *packet, + int question_section); + +/* + * read a query entry from network packet given in buffer. + * does not follow compression ptrs, checks for errors (returns 0). + * Dest must be at least MAXDOMAINLEN long. + */ +int packet_read_query_section(buffer_type *packet, + uint8_t* dest, + uint16_t* qtype, + uint16_t* qclass); + +#endif /* _PACKET_H_ */ diff --git a/usr.sbin/nsd/query.c b/usr.sbin/nsd/query.c new file mode 100644 index 00000000000..758d6e0877e --- /dev/null +++ b/usr.sbin/nsd/query.c @@ -0,0 +1,1432 @@ +/* + * query.c -- nsd(8) the resolver. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <netdb.h> + +#include "answer.h" +#include "axfr.h" +#include "dns.h" +#include "dname.h" +#include "nsd.h" +#include "namedb.h" +#include "query.h" +#include "util.h" +#include "options.h" +#include "nsec3.h" +#include "tsig.h" + +/* [Bug #253] Adding unnecessary NS RRset may lead to undesired truncation. + * This function determines if the final response packet needs the NS RRset included. + * Currently, it will only return negative if QTYPE == DNSKEY. This way, resolvers + * won't fallback to TCP unnecessarily when priming DNSKEYs. + */ +static int answer_needs_ns(struct query *query); + +static int add_rrset(struct query *query, + answer_type *answer, + rr_section_type section, + domain_type *owner, + rrset_type *rrset); + +static void answer_authoritative(struct nsd *nsd, + struct query *q, + answer_type *answer, + uint32_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, + int exact, domain_type *closest_match, + domain_type *closest_encloser, + const dname_type *qname); + +void +query_put_dname_offset(struct query *q, domain_type *domain, uint16_t offset) +{ + assert(q); + assert(domain); + assert(domain->number > 0); + + if (offset > MAX_COMPRESSION_OFFSET) + return; + if (q->compressed_dname_count >= MAX_COMPRESSED_DNAMES) + return; + + q->compressed_dname_offsets[domain->number] = offset; + q->compressed_dnames[q->compressed_dname_count] = domain; + ++q->compressed_dname_count; +} + +void +query_clear_dname_offsets(struct query *q, size_t max_offset) +{ + while (q->compressed_dname_count > 0 + && (q->compressed_dname_offsets[q->compressed_dnames[q->compressed_dname_count - 1]->number] + >= max_offset)) + { + q->compressed_dname_offsets[q->compressed_dnames[q->compressed_dname_count - 1]->number] = 0; + --q->compressed_dname_count; + } +} + +void +query_clear_compression_tables(struct query *q) +{ + uint16_t i; + + for (i = 0; i < q->compressed_dname_count; ++i) { + assert(q->compressed_dnames); + q->compressed_dname_offsets[q->compressed_dnames[i]->number] = 0; + } + q->compressed_dname_count = 0; +} + +void +query_add_compression_domain(struct query *q, domain_type *domain, uint16_t offset) +{ + 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), + (unsigned long) domain->number, + offset)); + query_put_dname_offset(q, domain, offset); + offset += label_length(dname_name(domain_dname(domain))) + 1; + domain = domain->parent; + } +} + +/* + * Generate an error response with the specified RCODE. + */ +query_state_type +query_error (struct query *q, nsd_rc_type rcode) +{ + if (rcode == NSD_RC_DISCARD) { + return QUERY_DISCARDED; + } + + buffer_clear(q->packet); + + QR_SET(q->packet); /* This is an answer. */ + RCODE_SET(q->packet, (int) rcode); /* Error code. */ + + /* Truncate the question as well... */ + QDCOUNT_SET(q->packet, 0); + ANCOUNT_SET(q->packet, 0); + NSCOUNT_SET(q->packet, 0); + ARCOUNT_SET(q->packet, 0); + buffer_set_position(q->packet, QHEADERSZ); + return QUERY_PROCESSED; +} + +static query_state_type +query_formerr (struct query *query) +{ + int opcode = OPCODE(query->packet); + FLAGS_SET(query->packet, FLAGS(query->packet) & 0x0100U); + /* Preserve the RD flag. Clear the rest. */ + OPCODE_SET(query->packet, opcode); + return query_error(query, NSD_RC_FORMAT); +} + +static void +query_cleanup(void *data) +{ + query_type *query = (query_type *) data; + region_destroy(query->region); +} + +query_type * +query_create(region_type *region, uint16_t *compressed_dname_offsets, + uint32_t compressed_dname_size) +{ + query_type *query + = (query_type *) region_alloc_zero(region, sizeof(query_type)); + /* create region with large block size, because the initial chunk + saves many mallocs in the server */ + query->region = region_create_custom(xalloc, free, 16384, 16384/8, 32, 0); + query->compressed_dname_offsets = compressed_dname_offsets; + query->packet = buffer_create(region, QIOBUFSZ); + region_add_cleanup(region, query_cleanup, query); + query->compressed_dname_offsets_size = compressed_dname_size; +#ifdef TSIG + tsig_create_record(&query->tsig, region); + query->tsig_prepare_it = 1; + query->tsig_update_it = 1; + query->tsig_sign_it = 1; +#endif /* TSIG */ + return query; +} + +void +query_reset(query_type *q, size_t maxlen, int is_tcp) +{ + /* + * As long as less than 4Kb (region block size) has been used, + * this call to free_all is free, the block is saved for re-use, + * so no malloc() or free() calls are done. + * at present use of the region is for: + * o query qname dname_type (255 max). + * o wildcard expansion domain_type (7*ptr+u32+2bytes)+(5*ptr nsec3) + * o wildcard expansion for additional section domain_type. + * o nsec3 hashed name(s) (3 dnames for a nonexist_proof, + * one proof per wildcard and for nx domain). + */ + region_free_all(q->region); + q->addrlen = sizeof(q->addr); + q->maxlen = maxlen; + q->reserved_space = 0; + buffer_clear(q->packet); + edns_init_record(&q->edns); +#ifdef TSIG + tsig_init_record(&q->tsig, NULL, NULL); + q->tsig_prepare_it = 1; + q->tsig_update_it = 1; + q->tsig_sign_it = 1; +#endif /* TSIG */ + q->tcp = is_tcp; + q->qname = NULL; + q->qtype = 0; + q->qclass = 0; + q->zone = NULL; + q->domain = NULL; + q->opcode = 0; + q->cname_count = 0; + q->delegation_domain = NULL; + q->delegation_rrset = NULL; + q->compressed_dname_count = 0; + q->number_temporary_domains = 0; + + q->axfr_is_done = 0; + q->axfr_zone = NULL; + q->axfr_current_domain = NULL; + q->axfr_current_rrset = NULL; + q->axfr_current_rr = 0; +} + +/* get a temporary domain number (or 0=failure) */ +static domain_type* +query_get_tempdomain(struct query *q) +{ + static domain_type d[EXTRA_DOMAIN_NUMBERS]; + if(q->number_temporary_domains >= EXTRA_DOMAIN_NUMBERS) + return 0; + q->number_temporary_domains ++; + memset(&d[q->number_temporary_domains-1], 0, sizeof(domain_type)); + d[q->number_temporary_domains-1].number = q->compressed_dname_offsets_size + + q->number_temporary_domains - 1; + return &d[q->number_temporary_domains-1]; +} + +static void +query_addtxt(struct query *q, + const uint8_t *dname, + uint16_t klass, + uint32_t ttl, + const char *txt) +{ + size_t txt_length = strlen(txt); + uint8_t len = (uint8_t) txt_length; + + assert(txt_length <= UCHAR_MAX); + + /* Add the dname */ + if (dname >= buffer_begin(q->packet) + && dname <= buffer_current(q->packet)) + { + buffer_write_u16(q->packet, + 0xc000 | (dname - buffer_begin(q->packet))); + } else { + buffer_write(q->packet, dname + 1, *dname); + } + + buffer_write_u16(q->packet, TYPE_TXT); + buffer_write_u16(q->packet, klass); + buffer_write_u32(q->packet, ttl); + buffer_write_u16(q->packet, len + 1); + buffer_write_u8(q->packet, len); + buffer_write(q->packet, txt, len); +} + +/* + * Parse the question section of a query. The normalized query name + * is stored in QUERY->name, the class in QUERY->klass, and the type + * in QUERY->type. + */ +static int +process_query_section(query_type *query) +{ + uint8_t qnamebuf[MAXDOMAINLEN]; + + buffer_set_position(query->packet, QHEADERSZ); + /* Lets parse the query name and convert it to lower case. */ + if(!packet_read_query_section(query->packet, qnamebuf, + &query->qtype, &query->qclass)) + return 0; + query->qname = dname_make(query->region, qnamebuf, 1); + query->opcode = OPCODE(query->packet); + return 1; +} + + +/* + * Process an optional EDNS OPT record. Sets QUERY->EDNS to 0 if + * there was no EDNS record, to -1 if there was an invalid or + * unsupported EDNS record, and to 1 otherwise. Updates QUERY->MAXLEN + * if the EDNS record specifies a maximum supported response length. + * + * Return NSD_RC_FORMAT on failure, NSD_RC_OK on success. + */ +static nsd_rc_type +process_edns(nsd_type* nsd, struct query *q) +{ + if (q->edns.status == EDNS_ERROR) { + return NSD_RC_FORMAT; + } + + if (q->edns.status == EDNS_OK) { + /* Only care about UDP size larger than normal... */ + if (!q->tcp && q->edns.maxlen > UDP_MAX_MESSAGE_LEN) { + size_t edns_size; +#if defined(INET6) + if (q->addr.ss_family == AF_INET6) { + edns_size = nsd->ipv6_edns_size; + } else +#endif + edns_size = nsd->ipv4_edns_size; + + if (q->edns.maxlen < edns_size) { + q->maxlen = q->edns.maxlen; + } else { + q->maxlen = edns_size; + } + +#if defined(INET6) && !defined(IPV6_USE_MIN_MTU) && !defined(IPV6_MTU) + /* + * Use IPv6 minimum MTU to avoid sending + * packets that are too large for some links. + * IPv6 will not automatically fragment in + * this case (unlike IPv4). + */ + if (q->addr.ss_family == AF_INET6 + && q->maxlen > IPV6_MIN_MTU) + { + q->maxlen = IPV6_MIN_MTU; + } +#endif + } + + /* Strip the OPT resource record off... */ + buffer_set_position(q->packet, q->edns.position); + buffer_set_limit(q->packet, q->edns.position); + ARCOUNT_SET(q->packet, ARCOUNT(q->packet) - 1); + } + return NSD_RC_OK; +} + +/* + * Processes TSIG. + * Sets error when tsig does not verify on the query. + */ +#ifdef TSIG +static nsd_rc_type +process_tsig(struct query* q) +{ + if(q->tsig.status == TSIG_ERROR) + return NSD_RC_FORMAT; + if(q->tsig.status == TSIG_OK) { + if(!tsig_from_query(&q->tsig)) { + log_msg(LOG_ERR, "query tsig unknown key/algorithm"); + return NSD_RC_REFUSE; + } + buffer_set_limit(q->packet, q->tsig.position); + ARCOUNT_SET(q->packet, ARCOUNT(q->packet) - 1); + 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)); + return NSD_RC_REFUSE; + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "query good tsig signature for %s", + dname_to_string(q->tsig.key->name, NULL))); + } + return NSD_RC_OK; +} +#endif /* TSIG */ + +/* + * Check notify acl and forward to xfrd (or return an error). + */ +static query_state_type +answer_notify(struct nsd* nsd, struct query *query) +{ + int acl_num; + acl_options_t *why; + nsd_rc_type rc; + + zone_options_t* zone_opt; + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "got notify %s processing acl", + dname_to_string(query->qname, NULL))); + + zone_opt = zone_options_find(nsd->options, query->qname); + if(!zone_opt) + return query_error(query, NSD_RC_NXDOMAIN); + + if(!nsd->this_child) /* we are in debug mode or something */ + return query_error(query, NSD_RC_SERVFAIL); + +#ifdef TSIG + if(!tsig_find_rr(&query->tsig, query->packet)) { + DEBUG(DEBUG_XFRD,2, (LOG_ERR, "bad tsig RR format")); + return query_error(query, NSD_RC_FORMAT); + } + rc = process_tsig(query); + if(rc != NSD_RC_OK) + return query_error(query, rc); +#endif /* TSIG */ + + /* check if it passes acl */ + if((acl_num = acl_check_incoming(zone_opt->allow_notify, query, + &why)) != -1) + { + sig_atomic_t mode = NSD_PASS_TO_XFRD; + int s = nsd->this_child->parent_fd; + uint16_t sz; + uint32_t acl_send = htonl(acl_num); + size_t pos; + 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, + why->nokey?"NOKEY": + (why->blocked?"BLOCKED":why->key_name))); + sz = buffer_limit(query->packet); + if(buffer_limit(query->packet) > MAX_PACKET_SIZE) + return query_error(query, NSD_RC_SERVFAIL); + /* forward to xfrd for processing + Note. Blocking IPC I/O, but acl is OK. */ + sz = htons(sz); + if(!write_socket(s, &mode, sizeof(mode)) || + !write_socket(s, &sz, sizeof(sz)) || + !write_socket(s, buffer_begin(query->packet), + buffer_limit(query->packet)) || + !write_socket(s, &acl_send, sizeof(acl_send))) { + log_msg(LOG_ERR, "error in IPC notify server2main, %s", + strerror(errno)); + return query_error(query, NSD_RC_SERVFAIL); + } + + /* create notify reply - keep same query contents */ + QR_SET(query->packet); /* This is an answer. */ + AA_SET(query->packet); /* we are authoritative. */ + ANCOUNT_SET(query->packet, 0); + NSCOUNT_SET(query->packet, 0); + ARCOUNT_SET(query->packet, 0); + RCODE_SET(query->packet, RCODE_OK); /* Error code. */ + /* position is right after the 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")); + /* tsig is added in add_additional later (if needed) */ + return QUERY_PROCESSED; + } + VERBOSITY(1, (LOG_INFO, "got notify for zone: %s; Refused by acl: %s %s", + dname_to_string(query->qname, NULL), + why?why->key_name:"no acl matches", + why?why->ip_address_spec:".")); + return query_error(query, NSD_RC_REFUSE); +} + + +/* + * Answer a query in the CHAOS class. + */ +static query_state_type +answer_chaos(struct nsd *nsd, query_type *q) +{ + AA_CLR(q->packet); + switch (q->qtype) { + case TYPE_ANY: + case TYPE_TXT: + if ((q->qname->name_size == 11 + && memcmp(dname_name(q->qname), "\002id\006server", 11) == 0) || + (q->qname->name_size == 15 + && memcmp(dname_name(q->qname), "\010hostname\004bind", 15) == 0)) + { + /* Add ID */ + query_addtxt(q, + buffer_begin(q->packet) + QHEADERSZ, + CLASS_CH, + 0, + nsd->identity); + ANCOUNT_SET(q->packet, ANCOUNT(q->packet) + 1); + } else if ((q->qname->name_size == 16 + && memcmp(dname_name(q->qname), "\007version\006server", 16) == 0) || + (q->qname->name_size == 14 + && memcmp(dname_name(q->qname), "\007version\004bind", 14) == 0)) + { + if(!nsd->options->hide_version) { + /* Add version */ + query_addtxt(q, + buffer_begin(q->packet) + QHEADERSZ, + CLASS_CH, + 0, + nsd->version); + ANCOUNT_SET(q->packet, ANCOUNT(q->packet) + 1); + } else { + RCODE_SET(q->packet, RCODE_REFUSE); + } + } + break; + default: + RCODE_SET(q->packet, RCODE_REFUSE); + break; + } + + return QUERY_PROCESSED; +} + + +/* + * Find the covering NSEC for a non-existent domain name. Normally + * the NSEC will be located at CLOSEST_MATCH, except when it is an + * empty non-terminal. In this case the NSEC may be located at the + * previous domain name (in canonical ordering). + */ +static domain_type * +find_covering_nsec(domain_type *closest_match, + zone_type *zone, + rrset_type **nsec_rrset) +{ + assert(closest_match); + assert(nsec_rrset); + + /* loop away temporary created domains. For real ones it is &RBTREE_NULL */ + while (closest_match->node.parent == NULL) + closest_match = closest_match->parent; + while (closest_match) { + *nsec_rrset = domain_find_rrset(closest_match, zone, TYPE_NSEC); + if (*nsec_rrset) { + return closest_match; + } + if (closest_match == zone->apex) { + /* Don't look outside the current zone. */ + return NULL; + } + closest_match = domain_previous(closest_match); + } + return NULL; +} + + +struct additional_rr_types +{ + uint16_t rr_type; + rr_section_type rr_section; +}; + +struct additional_rr_types default_additional_rr_types[] = { + { TYPE_A, ADDITIONAL_A_SECTION }, + { TYPE_AAAA, ADDITIONAL_AAAA_SECTION }, + { 0, (rr_section_type) 0 } +}; + +struct additional_rr_types rt_additional_rr_types[] = { + { TYPE_A, ADDITIONAL_A_SECTION }, + { TYPE_AAAA, ADDITIONAL_AAAA_SECTION }, + { TYPE_X25, ADDITIONAL_OTHER_SECTION }, + { TYPE_ISDN, ADDITIONAL_OTHER_SECTION }, + { 0, (rr_section_type) 0 } +}; + +static void +add_additional_rrsets(struct query *query, answer_type *answer, + rrset_type *master_rrset, size_t rdata_index, + int allow_glue, struct additional_rr_types types[]) +{ + size_t i; + + assert(query); + assert(answer); + assert(master_rrset); + assert(rdata_atom_is_domain(rrset_rrtype(master_rrset), rdata_index)); + + for (i = 0; i < master_rrset->rr_count; ++i) { + int j; + domain_type *additional = rdata_atom_domain(master_rrset->rrs[i].rdatas[rdata_index]); + domain_type *match = additional; + + assert(additional); + + if (!allow_glue && domain_is_glue(match, query->zone)) + continue; + + /* + * Check to see if we need to generate the dependent + * based on a wildcard domain. + */ + while (!match->is_existing) { + match = match->parent; + } + if (additional != match && domain_wildcard_child(match)) { + 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->number = additional->number; + temp->parent = match; + temp->wildcard_child_closest_match = temp; + temp->rrsets = wildcard_child->rrsets; + temp->is_existing = wildcard_child->is_existing; + additional = temp; + } + + for (j = 0; types[j].rr_type != 0; ++j) { + rrset_type *rrset = domain_find_rrset( + additional, query->zone, types[j].rr_type); + if (rrset) { + answer_add_rrset(answer, types[j].rr_section, + additional, rrset); + } + } + } +} + +static int +answer_needs_ns(struct query* query) +{ + assert(query); + /* Currently, only troublesome for DNSKEYs, cuz their RRSETs are quite large. */ + return (query->qtype != TYPE_DNSKEY); +} + +static int +add_rrset(struct query *query, + answer_type *answer, + rr_section_type section, + domain_type *owner, + rrset_type *rrset) +{ + int result; + + assert(query); + assert(answer); + assert(owner); + assert(rrset); + assert(rrset_rrclass(rrset) == CLASS_IN); + + result = answer_add_rrset(answer, section, owner, rrset); + switch (rrset_rrtype(rrset)) { + case TYPE_NS: + add_additional_rrsets(query, answer, rrset, 0, 1, + default_additional_rr_types); + break; + case TYPE_MB: + add_additional_rrsets(query, answer, rrset, 0, 0, + default_additional_rr_types); + break; + case TYPE_MX: + case TYPE_KX: + add_additional_rrsets(query, answer, rrset, 1, 0, + default_additional_rr_types); + break; + case TYPE_RT: + add_additional_rrsets(query, answer, rrset, 1, 0, + rt_additional_rr_types); + break; + default: + break; + } + + return result; +} + + +/* returns 0 on error, or the domain number for to_name. + 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 +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) +{ + /* add temporary domains for from_name and to_name and all + their (not allocated yet) parents */ + /* any domains below src are not_existing (because of DNAME at src) */ + int i; + domain_type* cname_domain; + domain_type* cname_dest; + rrset_type* rrset; + + /* allocate source part */ + domain_type* lastparent = src; + assert(q && answer && from_name && to_name && src && to_closest_encloser); + assert(to_closest_match); + for(i=0; i < from_name->label_count - domain_dname(src)->label_count; i++) + { + domain_type* newdom = query_get_tempdomain(q); + if(!newdom) + return 0; + newdom->is_existing = 1; + newdom->parent = lastparent; + newdom->node.key = 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)); + lastparent = newdom; + } + cname_domain = lastparent; + + /* allocate dest part */ + lastparent = to_closest_encloser; + for(i=0; i < to_name->label_count - domain_dname(to_closest_encloser)->label_count; + i++) + { + domain_type* newdom = query_get_tempdomain(q); + if(!newdom) + return 0; + newdom->is_existing = 0; + newdom->parent = lastparent; + newdom->node.key = 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)); + lastparent = newdom; + } + cname_dest = lastparent; + *to_closest_match = cname_dest; + + /* allocate the CNAME RR */ + rrset = (rrset_type*) region_alloc(q->region, sizeof(rrset_type)); + memset(rrset, 0, sizeof(rrset_type)); + rrset->zone = q->zone; + rrset->rr_count = 1; + rrset->rrs = (rr_type*) region_alloc(q->region, sizeof(rr_type)); + memset(rrset->rrs, 0, sizeof(rr_type)); + rrset->rrs->owner = cname_domain; + rrset->rrs->ttl = 0; + rrset->rrs->type = TYPE_CNAME; + rrset->rrs->klass = CLASS_IN; + rrset->rrs->rdata_count = 1; + rrset->rrs->rdatas = (rdata_atom_type*)region_alloc(q->region, + sizeof(rdata_atom_type)); + rrset->rrs->rdatas->domain = cname_dest; + + if(!add_rrset(q, answer, ANSWER_SECTION, cname_domain, rrset)) { + log_msg(LOG_ERR, "could not add synthesized CNAME rrset to packet"); + } + + return cname_dest->number; +} + +/* + * Answer delegation information. + * + * DNSSEC: Include the DS RRset if present. Otherwise include an NSEC + * record proving the DS RRset does not exist. + */ +static void +answer_delegation(query_type *query, answer_type *answer) +{ + assert(answer); + assert(query->delegation_domain); + assert(query->delegation_rrset); + + AA_CLR(query->packet); + add_rrset(query, + answer, + AUTHORITY_SECTION, + query->delegation_domain, + query->delegation_rrset); + if (query->edns.dnssec_ok && zone_is_secure(query->zone)) { + rrset_type *rrset; + if ((rrset = domain_find_rrset(query->delegation_domain, query->zone, TYPE_DS))) { + add_rrset(query, answer, AUTHORITY_SECTION, + query->delegation_domain, rrset); +#ifdef NSEC3 + } else if (query->zone->nsec3_soa_rr) { + nsec3_answer_delegation(query, answer); +#endif + } else if ((rrset = domain_find_rrset(query->delegation_domain, query->zone, TYPE_NSEC))) { + add_rrset(query, answer, AUTHORITY_SECTION, + query->delegation_domain, rrset); + } + } + query->domain = query->delegation_domain; +} + + +/* + * Answer SOA information. + */ +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, + query->zone->apex, + query->zone->soa_nx_rrset); + } +} + + +/* + * Answer that the domain name exists but there is no RRset with the + * requested type. + * + * DNSSEC: Include the correct NSEC record proving that the type does + * not exist. In the wildcard no data (3.1.3.4) case the wildcard IS + * NOT expanded, so the ORIGINAL parameter must point to the original + * wildcard entry, not to the generated entry. + */ +static void +answer_nodata(struct query *query, answer_type *answer, domain_type *original) +{ + if (query->cname_count == 0) { + answer_soa(query, answer); + } + +#ifdef NSEC3 + if (query->edns.dnssec_ok && query->zone->nsec3_soa_rr) { + nsec3_answer_nodata(query, answer, original); + } else +#endif + if (query->edns.dnssec_ok && zone_is_secure(query->zone)) { + domain_type *nsec_domain; + rrset_type *nsec_rrset; + + nsec_domain = find_covering_nsec(original, query->zone, &nsec_rrset); + if (nsec_domain) { + add_rrset(query, answer, AUTHORITY_SECTION, nsec_domain, nsec_rrset); + } + } +} + +static void +answer_nxdomain(query_type *query, answer_type *answer) +{ + if (query->cname_count == 0) { + RCODE_SET(query->packet, RCODE_NXDOMAIN); + answer_soa(query, answer); + } +} + + +/* + * Answer domain information (or SOA if we do not have an RRset for + * the type specified by the query). + */ +static void +answer_domain(struct nsd* nsd, struct query *q, answer_type *answer, + domain_type *domain, domain_type *original) +{ + rrset_type *rrset; + + if (q->qtype == TYPE_ANY) { + int added = 0; + for (rrset = domain_find_any_rrset(domain, q->zone); rrset; rrset = rrset->next) { + if (rrset->zone == q->zone +#ifdef NSEC3 + && rrset_rrtype(rrset) != TYPE_NSEC3 +#endif + /* + * Don't include the RRSIG RRset when + * DNSSEC is used, because it is added + * automatically on an per-RRset basis. + */ + && !(q->edns.dnssec_ok + && zone_is_secure(q->zone) + && rrset_rrtype(rrset) == TYPE_RRSIG)) + { + add_rrset(q, answer, ANSWER_SECTION, domain, rrset); + ++added; + } + } + if (added == 0) { + answer_nodata(q, answer, original); + return; + } +#ifdef NSEC3 + } else if (q->qtype == TYPE_NSEC3) { + answer_nodata(q, answer, original); + return; +#endif + } else if ((rrset = domain_find_rrset(domain, q->zone, q->qtype))) { + add_rrset(q, answer, ANSWER_SECTION, domain, rrset); + } else if ((rrset = domain_find_rrset(domain, q->zone, TYPE_CNAME))) { + int added; + + /* + * If the CNAME is not added it is already in the + * answer, so we have a CNAME loop. Don't follow the + * CNAME target in this case. + */ + added = add_rrset(q, answer, ANSWER_SECTION, domain, rrset); + assert(rrset->rr_count > 0); + if (added) { + /* only process first CNAME record */ + domain_type *closest_match = rdata_atom_domain(rrset->rrs[0].rdatas[0]); + domain_type *closest_encloser = closest_match; + zone_type* origzone = q->zone; + ++q->cname_count; + + while (!closest_encloser->is_existing) + closest_encloser = closest_encloser->parent; + + answer_lookup_zone(nsd, q, answer, closest_match->number, + closest_match == closest_encloser, + closest_match, closest_encloser, + domain_dname(closest_match)); + q->zone = origzone; + } + } 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, AUTHORITY_SECTION, q->zone->apex, + q->zone->ns_rrset); + } +} + + +/* + * Answer with authoritative data. If a wildcard is matched the owner + * name will be expanded to the domain name specified by + * DOMAIN_NUMBER. DOMAIN_NUMBER 0 (zero) is reserved for the original + * query name. + * + * DNSSEC: Include the necessary NSEC records in case the request + * domain name does not exist and/or a wildcard match does not exist. + */ +static void +answer_authoritative(struct nsd *nsd, + struct query *q, + answer_type *answer, + uint32_t domain_number, + int exact, + domain_type *closest_match, + domain_type *closest_encloser, + const dname_type *qname) +{ + domain_type *match; + domain_type *original = closest_match; + rrset_type *rrset; + +#ifdef NSEC3 + if(exact && domain_has_only_NSEC3(closest_match, q->zone)) { + exact = 0; /* pretend it does not exist */ + if(closest_encloser->parent) + closest_encloser = closest_encloser->parent; + } +#endif /* NSEC3 */ + + if (exact) { + match = closest_match; + } else if ((rrset=domain_find_rrset(closest_encloser, q->zone, TYPE_DNAME))) { + /* process DNAME */ + const dname_type* name = qname; + domain_type *dest = rdata_atom_domain(rrset->rrs[0].rdatas[0]); + int added; + assert(rrset->rr_count > 0); + if(domain_number != 0) /* we followed CNAMEs or DNAMEs */ + 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))); + DEBUG(DEBUG_QUERY,2, (LOG_INFO, "->dest is %s", + dname_to_string(domain_dname(dest), NULL))); + /* 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; + zone_type* origzone = q->zone; + ++q->cname_count; + if(!newname) { /* newname too long */ + RCODE_SET(q->packet, RCODE_YXDOMAIN); + return; + } + DEBUG(DEBUG_QUERY,2, (LOG_INFO, "->result is %s", dname_to_string(newname, NULL))); + /* follow the DNAME */ + exact = namedb_lookup(nsd->db, newname, &closest_match, &closest_encloser); + /* synthesize CNAME record */ + newnum = query_synthesize_cname(q, answer, name, newname, + src, closest_encloser, &closest_match); + if(!newnum) { + /* could not synthesize the CNAME. */ + /* return previous CNAMEs to make resolver recurse for us */ + return; + } + + while (closest_encloser && !closest_encloser->is_existing) + closest_encloser = closest_encloser->parent; + answer_lookup_zone(nsd, q, answer, newnum, + closest_match == closest_encloser, + closest_match, closest_encloser, newname); + q->zone = origzone; + } + if(!added) /* log the error so operator can find looping recursors */ + log_msg(LOG_INFO, "DNAME processing stopped due to loop, qname %s", + dname_to_string(q->qname, NULL)); + return; + } else if (domain_wildcard_child(closest_encloser)) { + /* Generate the domain from the wildcard. */ + domain_type *wildcard_child = domain_wildcard_child(closest_encloser); + + match = (domain_type *) region_alloc(q->region, + sizeof(domain_type)); + memcpy(&match->node, &wildcard_child->node, sizeof(rbnode_t)); + 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_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; + + if (q->edns.dnssec_ok && q->zone->nsec3_soa_rr) { + /* Only add nsec3 wildcard data when do bit is set */ + nsec3_answer_wildcard(q, answer, wildcard_child, nsd->db, qname); + } +#endif + + /* + * Remember the original domain in case a Wildcard No + * Data (3.1.3.4) response needs to be generated. In + * this particular case the wildcard IS NOT + * expanded. + */ + original = wildcard_child; + } else { + match = NULL; + } + + /* Authorative zone. */ +#ifdef NSEC3 + if (q->edns.dnssec_ok && q->zone->nsec3_soa_rr) { + nsec3_answer_authoritative(&match, q, answer, + closest_encloser, nsd->db, qname); + } else +#endif + if (q->edns.dnssec_ok && zone_is_secure(q->zone)) { + if (match != closest_encloser) { + domain_type *nsec_domain; + rrset_type *nsec_rrset; + + /* + * No match found or generated from wildcard, + * include NSEC record. + */ + nsec_domain = find_covering_nsec(closest_match, q->zone, &nsec_rrset); + if (nsec_domain) { + add_rrset(q, answer, AUTHORITY_SECTION, nsec_domain, nsec_rrset); + } + } + if (!match) { + domain_type *nsec_domain; + rrset_type *nsec_rrset; + + /* + * No match and no wildcard. Include NSEC + * proving there is no wildcard. + */ + nsec_domain = find_covering_nsec(closest_encloser->wildcard_child_closest_match, q->zone, &nsec_rrset); + if (nsec_domain) { + add_rrset(q, answer, AUTHORITY_SECTION, nsec_domain, nsec_rrset); + } + } + } + +#ifdef NSEC3 + if (RCODE(q->packet)!=RCODE_OK) { + return; /* nsec3 collision failure */ + } +#endif + if (match) { + answer_domain(nsd, q, answer, match, original); + } else { + answer_nxdomain(q, answer); + } +} + +/* + * qname may be different after CNAMEs have been followed from query->qname. + */ +static void +answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer, + uint32_t domain_number, int exact, domain_type *closest_match, + domain_type *closest_encloser, const dname_type *qname) +{ + q->zone = domain_find_zone(closest_encloser); + if (!q->zone) { + if(q->cname_count == 0) + RCODE_SET(q->packet, RCODE_SERVFAIL); + return; + } + + /* + * See RFC 4035 (DNSSEC protocol) section 3.1.4.1 Responding + * to Queries for DS RRs. + */ + if (exact && q->qtype == TYPE_DS && closest_encloser == q->zone->apex) { + /* + * Type DS query at a zone cut, use the responsible + * parent zone to generate the answer if we are + * authoritative for the parent zone. + */ + zone_type *zone = domain_find_parent_zone(q->zone); + if (zone) + q->zone = zone; + } + + /* 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->cname_count == 0) + RCODE_SET(q->packet, RCODE_SERVFAIL); + return; + } + + if (exact && q->qtype == TYPE_DS && closest_encloser == q->zone->apex) { + /* + * Type DS query at the zone apex (and the server is + * not authoratitive for the parent zone). + */ + if (q->qclass == CLASS_ANY) { + AA_CLR(q->packet); + } else { + AA_SET(q->packet); + } + answer_nodata(q, answer, closest_encloser); + } else { + q->delegation_domain = domain_find_ns_rrsets( + closest_encloser, q->zone, &q->delegation_rrset); + + if (!q->delegation_domain + || (exact && q->qtype == TYPE_DS && closest_encloser == q->delegation_domain)) + { + if (q->qclass == CLASS_ANY) { + AA_CLR(q->packet); + } else { + AA_SET(q->packet); + } + answer_authoritative(nsd, q, answer, domain_number, exact, + closest_match, closest_encloser, qname); + } + else { + answer_delegation(q, answer); + } + } +} + +static void +answer_query(struct nsd *nsd, struct query *q) +{ + domain_type *closest_match; + domain_type *closest_encloser; + int exact; + uint16_t offset; + answer_type answer; + + answer_init(&answer); + + exact = namedb_lookup(nsd->db, q->qname, &closest_match, &closest_encloser); + if (!closest_encloser->is_existing) { + exact = 0; + while (closest_encloser != NULL && !closest_encloser->is_existing) + closest_encloser = closest_encloser->parent; + } + if(!closest_encloser) { + RCODE_SET(q->packet, RCODE_SERVFAIL); + return; + } + + q->domain = closest_encloser; + answer_lookup_zone(nsd, q, &answer, 0, exact, closest_match, + closest_encloser, q->qname); + + encode_answer(q, &answer); + if (ANCOUNT(q->packet) + NSCOUNT(q->packet) + ARCOUNT(q->packet) == 0) + { + /* no answers, no need for compression */ + return; + } + offset = dname_label_offsets(q->qname)[domain_dname(closest_encloser)->label_count - 1] + QHEADERSZ; + query_add_compression_domain(q, closest_encloser, offset); + query_clear_compression_tables(q); +} + +void +query_prepare_response(query_type *q) +{ + uint16_t flags; + + /* + * Preserve the data up-to the current packet's limit. + */ + buffer_set_position(q->packet, buffer_limit(q->packet)); + buffer_set_limit(q->packet, buffer_capacity(q->packet)); + + /* + * Reserve space for the EDNS records if required. + */ + q->reserved_space = edns_reserved_space(&q->edns); +#ifdef TSIG + q->reserved_space += tsig_reserved_space(&q->tsig); +#endif /* TSIG */ + + /* Update the flags. */ + flags = FLAGS(q->packet); + flags &= 0x0100U; /* Preserve the RD flag. */ + /* CD flag must be cleared for auth answers */ + flags |= 0x8000U; /* Set the QR flag. */ + FLAGS_SET(q->packet, flags); +} + +/* + * Processes the query. + * + */ +query_state_type +query_process(query_type *q, nsd_type *nsd) +{ + /* The query... */ + nsd_rc_type rc; + query_state_type query_state; + uint16_t arcount; + + /* Sanity checks */ + if (buffer_limit(q->packet) < QHEADERSZ) { + /* packet too small to contain DNS header. + Now packet investigation macros will work without problems. */ + return QUERY_DISCARDED; + } + if (QR(q->packet)) { + /* Not a query? Drop it on the floor. */ + return QUERY_DISCARDED; + } + + if(!process_query_section(q)) { + return query_formerr(q); + } + + /* Update statistics. */ + STATUP2(nsd, opcode, q->opcode); + STATUP2(nsd, qtype, q->qtype); + STATUP2(nsd, qclass, q->qclass); + + if (q->opcode != OPCODE_QUERY) { + if (q->opcode == OPCODE_NOTIFY) { + return answer_notify(nsd, q); + } else { + return query_error(q, NSD_RC_IMPL); + } + } + + /* Dont bother to answer more than one question at once... */ + if (QDCOUNT(q->packet) != 1 || TC(q->packet)) { + FLAGS_SET(q->packet, 0); + return query_formerr(q); + } + + /* Dont allow any records in the answer or authority section... + except for IXFR queries. */ + if (ANCOUNT(q->packet) != 0 || + (q->qtype!=TYPE_IXFR && NSCOUNT(q->packet) != 0)) { + return query_formerr(q); + } + if(q->qtype==TYPE_IXFR && NSCOUNT(q->packet) > 0) { + int i; /* skip ixfr soa information data here */ + for(i=0; i< NSCOUNT(q->packet); i++) + if(!packet_skip_rr(q->packet, 0)) + return query_formerr(q); + } + + arcount = ARCOUNT(q->packet); +#ifdef TSIG + if (arcount > 0) { + /* see if tsig is before edns record */ + if (!tsig_parse_rr(&q->tsig, q->packet)) + return query_formerr(q); + if(q->tsig.status != TSIG_NOT_PRESENT) + --arcount; + } +#endif /* TSIG */ + if (arcount > 0) { + if (edns_parse_record(&q->edns, q->packet)) + --arcount; + } +#ifdef TSIG + if (arcount > 0 && q->tsig.status == TSIG_NOT_PRESENT) { + /* see if tsig is after the edns record */ + if (!tsig_parse_rr(&q->tsig, q->packet)) + return query_formerr(q); + if(q->tsig.status != TSIG_NOT_PRESENT) + --arcount; + } +#endif /* TSIG */ + if (arcount > 0) { + return query_formerr(q); + } + + /* Do we have any trailing garbage? */ +#ifdef STRICT_MESSAGE_PARSE + if (buffer_remaining(q->packet) > 0) { + /* If we're strict.... */ + return query_formerr(q); + } +#endif + /* Remove trailing garbage. */ + buffer_set_limit(q->packet, buffer_position(q->packet)); + +#ifdef TSIG + rc = process_tsig(q); + if (rc != NSD_RC_OK) { + return query_error(q, rc); + } +#endif /* TSIG */ + rc = process_edns(nsd, q); + if (rc != NSD_RC_OK) { + /* We should not return FORMERR, but BADVERS (=16). + * BADVERS is created with Ext. RCODE, followed by RCODE. + * Ext. RCODE is set to 1, RCODE must be 0 (getting 0x10 = 16). + * Thus RCODE = NOERROR = NSD_RC_OK. */ + return query_error(q, NSD_RC_OK); + } + + query_prepare_response(q); + + if (q->qclass != CLASS_IN && q->qclass != CLASS_ANY) { + if (q->qclass == CLASS_CH) { + return answer_chaos(nsd, q); + } else { + return query_error(q, NSD_RC_REFUSE); + } + } + + query_state = answer_axfr_ixfr(nsd, q); + if (query_state == QUERY_PROCESSED || query_state == QUERY_IN_AXFR) { + return query_state; + } + + answer_query(nsd, q); + + return QUERY_PROCESSED; +} + +void +query_add_optional(query_type *q, nsd_type *nsd) +{ + struct edns_data *edns = &nsd->edns_ipv4; +#if defined(INET6) + if (q->addr.ss_family == AF_INET6) { + edns = &nsd->edns_ipv6; + } +#endif + switch (q->edns.status) { + case EDNS_NOT_PRESENT: + break; + case EDNS_OK: + buffer_write(q->packet, edns->ok, OPT_LEN); + /* check if nsid data should be written */ +#ifdef NSID + if (nsd->nsid_len > 0 && q->edns.nsid == 1 && + !query_overflow_nsid(q, nsd->nsid_len)) { + /* rdata length */ + buffer_write(q->packet, edns->rdata_nsid, OPT_RDATA); + /* nsid opt header */ + buffer_write(q->packet, edns->nsid, OPT_HDR); + /* nsid payload */ + buffer_write(q->packet, nsd->nsid, nsd->nsid_len); + } else { + /* fill with NULLs */ + buffer_write(q->packet, edns->rdata_none, OPT_RDATA); + } +#else + buffer_write(q->packet, edns->rdata_none, OPT_RDATA); +#endif /* NSID */ + + ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); + + STATUP(nsd, edns); + break; + case EDNS_ERROR: + buffer_write(q->packet, edns->error, OPT_LEN); + buffer_write(q->packet, edns->rdata_none, OPT_RDATA); + ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); + STATUP(nsd, ednserr); + break; + } + +#ifdef TSIG + if (q->tsig.status != TSIG_NOT_PRESENT) { + if (q->tsig.status == TSIG_ERROR || + q->tsig.error_code != TSIG_ERROR_NOERROR) { + tsig_error_reply(&q->tsig); + tsig_append_rr(&q->tsig, q->packet); + ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); + } else if(q->tsig.status == TSIG_OK && + q->tsig.error_code == TSIG_ERROR_NOERROR) + { + if(q->tsig_prepare_it) + tsig_prepare(&q->tsig); + if(q->tsig_update_it) + tsig_update(&q->tsig, q->packet, buffer_position(q->packet)); + if(q->tsig_sign_it) { + tsig_sign(&q->tsig); + tsig_append_rr(&q->tsig, q->packet); + ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); + } + } + } +#endif /* TSIG */ +} diff --git a/usr.sbin/nsd/query.h b/usr.sbin/nsd/query.h new file mode 100644 index 00000000000..89ea960570e --- /dev/null +++ b/usr.sbin/nsd/query.h @@ -0,0 +1,217 @@ +/* + * query.h -- manipulation with the queries + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _QUERY_H_ +#define _QUERY_H_ + +#include <assert.h> +#include <string.h> + +#include "namedb.h" +#include "nsd.h" +#include "packet.h" +#include "tsig.h" + +enum query_state { + QUERY_PROCESSED, + QUERY_DISCARDED, + QUERY_IN_AXFR +}; +typedef enum query_state query_state_type; + +/* Query as we pass it around */ +typedef struct query query_type; +struct query { + /* + * Memory region freed whenever the query is reset. + */ + region_type *region; + + /* + * The address the query was received from. + */ +#ifdef INET6 + struct sockaddr_storage addr; +#else + struct sockaddr_in addr; +#endif + socklen_t addrlen; + + /* + * Maximum supported query size. + */ + size_t maxlen; + + /* + * Space reserved for optional records like EDNS. + */ + size_t reserved_space; + + /* EDNS information provided by the client. */ + edns_record_type edns; + +#ifdef TSIG + /* TSIG record information and running hash for query-response */ + tsig_record_type tsig; + /* tsig actions can be overridden, for axfr transfer. */ + int tsig_prepare_it, tsig_update_it, tsig_sign_it; +#endif /* TSIG */ + + int tcp; + uint16_t tcplen; + + buffer_type *packet; + + /* Normalized query domain name. */ + const dname_type *qname; + + /* Query type and class in host byte order. */ + uint16_t qtype; + uint16_t qclass; + + /* The zone used to answer the query. */ + zone_type *zone; + + /* The domain used to answer the query. */ + domain_type *domain; + + /* The delegation domain, if any. */ + domain_type *delegation_domain; + + /* The delegation NS rrset, if any. */ + rrset_type *delegation_rrset; + + /* Original opcode. */ + uint8_t opcode; + + /* + * The number of CNAMES followed. After a CNAME is followed + * we no longer change the RCODE to NXDOMAIN and no longer add + * SOA records to the authority section in case of NXDOMAIN + * and NODATA. + * Also includes number of DNAMES followed. + */ + int cname_count; + + /* Used for dname compression. */ + uint16_t compressed_dname_count; + domain_type *compressed_dnames[MAXRRSPP]; + + /* + * Indexed by domain->number, index 0 is reserved for the + * query name when generated from a wildcard record. + */ + uint16_t *compressed_dname_offsets; + uint32_t compressed_dname_offsets_size; + + /* number of temporary domains used for the query */ + uint32_t number_temporary_domains; + + /* + * Used for AXFR processing. + */ + int axfr_is_done; + zone_type *axfr_zone; + domain_type *axfr_current_domain; + rrset_type *axfr_current_rrset; + uint16_t axfr_current_rr; +}; + + +/* Check if the last write resulted in an overflow. */ +static inline int query_overflow(struct query *q); + +/* + * Store the offset of the specified domain in the dname compression + * table. + */ +void query_put_dname_offset(struct query *query, + domain_type *domain, + uint16_t offset); +/* + * Lookup the offset of the specified domain in the dname compression + * table. Offset 0 is used to indicate the domain is not yet in the + * compression table. + */ +static inline +uint16_t query_get_dname_offset(struct query *query, domain_type *domain) +{ + return query->compressed_dname_offsets[domain->number]; +} + +/* + * Remove all compressed dnames that have an offset that points beyond + * the end of the current answer. This must be done after some RRs + * are truncated and before adding new RRs. Otherwise dnames may be + * compressed using truncated data! + */ +void query_clear_dname_offsets(struct query *query, size_t max_offset); + +/* + * Clear the compression tables. + */ +void query_clear_compression_tables(struct query *query); + +/* + * Enter the specified domain into the compression table starting at + * the specified offset. + */ +void query_add_compression_domain(struct query *query, + domain_type *domain, + uint16_t offset); + + +/* + * Create a new query structure. + */ +query_type *query_create(region_type *region, + uint16_t *compressed_dname_offsets, + uint32_t compressed_dname_size); + +/* + * Reset a query structure so it is ready for receiving and processing + * a new query. + */ +void query_reset(query_type *query, size_t maxlen, int is_tcp); + +/* + * Process a query and write the response in the query I/O buffer. + */ +query_state_type query_process(query_type *q, nsd_type *nsd); + +/* + * Prepare the query structure for writing the response. The packet + * data up-to the current packet limit is preserved. This usually + * includes the packet header and question section. Space is reserved + * for the optional EDNS record, if required. + */ +void query_prepare_response(query_type *q); + +/* + * Add EDNS0 information to the response if required. + */ +void query_add_optional(query_type *q, nsd_type *nsd); + +/* + * Write an error response into the query structure with the indicated + * RCODE. + */ +query_state_type query_error(query_type *q, nsd_rc_type rcode); + +static inline int +query_overflow(query_type *q) +{ + return buffer_position(q->packet) > (q->maxlen - q->reserved_space); +} +static inline int +query_overflow_nsid(query_type *q, uint16_t nsid_len) +{ + return buffer_position(q->packet) > (q->maxlen - q->reserved_space - nsid_len); +} +#endif /* _QUERY_H_ */ diff --git a/usr.sbin/nsd/rbtree.c b/usr.sbin/nsd/rbtree.c new file mode 100644 index 00000000000..d683fe10c62 --- /dev/null +++ b/usr.sbin/nsd/rbtree.c @@ -0,0 +1,553 @@ +/* + * rbtree.c -- generic red black tree + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <assert.h> +#include <stdlib.h> + +#include "rbtree.h" + +#define BLACK 0 +#define RED 1 + +rbnode_t rbtree_null_node = { + RBTREE_NULL, /* Parent. */ + RBTREE_NULL, /* Left. */ + RBTREE_NULL, /* Right. */ + NULL, /* Key. */ + BLACK /* Color. */ +}; + +static void rbtree_rotate_left(rbtree_t *rbtree, rbnode_t *node); +static void rbtree_rotate_right(rbtree_t *rbtree, rbnode_t *node); +static void rbtree_insert_fixup(rbtree_t *rbtree, rbnode_t *node); +static void rbtree_delete_fixup(rbtree_t* rbtree, rbnode_t* child, rbnode_t* child_parent); + +/* + * Creates a new red black tree, intializes and returns a pointer to it. + * + * Return NULL on failure. + * + */ +rbtree_t * +rbtree_create (region_type *region, int (*cmpf)(const void *, const void *)) +{ + rbtree_t *rbtree; + + /* Allocate memory for it */ + rbtree = (rbtree_t *) region_alloc(region, sizeof(rbtree_t)); + if (!rbtree) { + return NULL; + } + + /* Initialize it */ + rbtree->root = RBTREE_NULL; + rbtree->count = 0; + rbtree->region = region; + rbtree->cmp = cmpf; + + return rbtree; +} + +/* + * Rotates the node to the left. + * + */ +static void +rbtree_rotate_left(rbtree_t *rbtree, rbnode_t *node) +{ + rbnode_t *right = node->right; + node->right = right->left; + if (right->left != RBTREE_NULL) + right->left->parent = node; + + right->parent = node->parent; + + if (node->parent != RBTREE_NULL) { + if (node == node->parent->left) { + node->parent->left = right; + } else { + node->parent->right = right; + } + } else { + rbtree->root = right; + } + right->left = node; + node->parent = right; +} + +/* + * Rotates the node to the right. + * + */ +static void +rbtree_rotate_right(rbtree_t *rbtree, rbnode_t *node) +{ + rbnode_t *left = node->left; + node->left = left->right; + if (left->right != RBTREE_NULL) + left->right->parent = node; + + left->parent = node->parent; + + if (node->parent != RBTREE_NULL) { + if (node == node->parent->right) { + node->parent->right = left; + } else { + node->parent->left = left; + } + } else { + rbtree->root = left; + } + left->right = node; + node->parent = left; +} + +static void +rbtree_insert_fixup(rbtree_t *rbtree, rbnode_t *node) +{ + rbnode_t *uncle; + + /* While not at the root and need fixing... */ + while (node != rbtree->root && node->parent->color == RED) { + /* If our parent is left child of our grandparent... */ + if (node->parent == node->parent->parent->left) { + uncle = node->parent->parent->right; + + /* If our uncle is red... */ + if (uncle->color == RED) { + /* Paint the parent and the uncle black... */ + node->parent->color = BLACK; + uncle->color = BLACK; + + /* And the grandparent red... */ + node->parent->parent->color = RED; + + /* And continue fixing the grandparent */ + node = node->parent->parent; + } else { /* Our uncle is black... */ + /* Are we the right child? */ + if (node == node->parent->right) { + node = node->parent; + rbtree_rotate_left(rbtree, node); + } + /* Now we're the left child, repaint and rotate... */ + node->parent->color = BLACK; + node->parent->parent->color = RED; + rbtree_rotate_right(rbtree, node->parent->parent); + } + } else { + uncle = node->parent->parent->left; + + /* If our uncle is red... */ + if (uncle->color == RED) { + /* Paint the parent and the uncle black... */ + node->parent->color = BLACK; + uncle->color = BLACK; + + /* And the grandparent red... */ + node->parent->parent->color = RED; + + /* And continue fixing the grandparent */ + node = node->parent->parent; + } else { /* Our uncle is black... */ + /* Are we the right child? */ + if (node == node->parent->left) { + node = node->parent; + rbtree_rotate_right(rbtree, node); + } + /* Now we're the right child, repaint and rotate... */ + node->parent->color = BLACK; + node->parent->parent->color = RED; + rbtree_rotate_left(rbtree, node->parent->parent); + } + } + } + rbtree->root->color = BLACK; +} + + +/* + * Inserts a node into a red black tree. + * + * Returns NULL on failure or the pointer to the newly added node + * otherwise. + */ +rbnode_t * +rbtree_insert (rbtree_t *rbtree, rbnode_t *data) +{ + /* XXX Not necessary, but keeps compiler quiet... */ + int r = 0; + + /* We start at the root of the tree */ + rbnode_t *node = rbtree->root; + rbnode_t *parent = RBTREE_NULL; + + /* Lets find the new parent... */ + while (node != RBTREE_NULL) { + /* Compare two keys, do we have a duplicate? */ + if ((r = rbtree->cmp(data->key, node->key)) == 0) { + return NULL; + } + parent = node; + + if (r < 0) { + node = node->left; + } else { + node = node->right; + } + } + + /* Initialize the new node */ + data->parent = parent; + data->left = data->right = RBTREE_NULL; + data->color = RED; + rbtree->count++; + + /* Insert it into the tree... */ + if (parent != RBTREE_NULL) { + if (r < 0) { + parent->left = data; + } else { + parent->right = data; + } + } else { + rbtree->root = data; + } + + /* Fix up the red-black properties... */ + rbtree_insert_fixup(rbtree, data); + + return data; +} + +/* + * Searches the red black tree, returns the data if key is found or NULL otherwise. + * + */ +rbnode_t * +rbtree_search (rbtree_t *rbtree, const void *key) +{ + rbnode_t *node; + + if (rbtree_find_less_equal(rbtree, key, &node)) { + return node; + } else { + return NULL; + } +} + +/* helpers for delete */ +static void swap_int8(uint8_t* x, uint8_t* y) +{ + uint8_t t = *x; *x = *y; *y = t; +} + +static void swap_np(rbnode_t** x, rbnode_t** y) +{ + rbnode_t* t = *x; *x = *y; *y = t; +} + +static void change_parent_ptr(rbtree_t* rbtree, rbnode_t* parent, rbnode_t* old, rbnode_t* new) +{ + if(parent == RBTREE_NULL) + { + assert(rbtree->root == old); + if(rbtree->root == old) rbtree->root = new; + return; + } + assert(parent->left == old || parent->right == old + || parent->left == new || parent->right == new); + if(parent->left == old) parent->left = new; + if(parent->right == old) parent->right = new; +} +static void change_child_ptr(rbnode_t* child, rbnode_t* old, rbnode_t* new) +{ + if(child == RBTREE_NULL) return; + assert(child->parent == old || child->parent == new); + if(child->parent == old) child->parent = new; +} + +rbnode_t* +rbtree_delete(rbtree_t *rbtree, const void *key) +{ + rbnode_t *to_delete; + rbnode_t *child; + if((to_delete = rbtree_search(rbtree, key)) == 0) return 0; + rbtree->count--; + + /* make sure we have at most one non-leaf child */ + if(to_delete->left != RBTREE_NULL && to_delete->right != RBTREE_NULL) + { + /* swap with smallest from right subtree (or largest from left) */ + rbnode_t *smright = to_delete->right; + while(smright->left != RBTREE_NULL) + smright = smright->left; + /* swap the smright and to_delete elements in the tree, + * but the rbnode_t is first part of user data struct + * so cannot just swap the keys and data pointers. Instead + * readjust the pointers left,right,parent */ + + /* swap colors - colors are tied to the position in the tree */ + swap_int8(&to_delete->color, &smright->color); + + /* swap child pointers in parents of smright/to_delete */ + change_parent_ptr(rbtree, to_delete->parent, to_delete, smright); + if(to_delete->right != smright) + change_parent_ptr(rbtree, smright->parent, smright, to_delete); + + /* swap parent pointers in children of smright/to_delete */ + change_child_ptr(smright->left, smright, to_delete); + change_child_ptr(smright->left, smright, to_delete); + change_child_ptr(smright->right, smright, to_delete); + change_child_ptr(smright->right, smright, to_delete); + change_child_ptr(to_delete->left, to_delete, smright); + if(to_delete->right != smright) + change_child_ptr(to_delete->right, to_delete, smright); + if(to_delete->right == smright) + { + /* set up so after swap they work */ + to_delete->right = to_delete; + smright->parent = smright; + } + + /* swap pointers in to_delete/smright nodes */ + swap_np(&to_delete->parent, &smright->parent); + swap_np(&to_delete->left, &smright->left); + swap_np(&to_delete->right, &smright->right); + + /* now delete to_delete (which is at the location where the smright previously was) */ + } + assert(to_delete->left == RBTREE_NULL || to_delete->right == RBTREE_NULL); + + if(to_delete->left != RBTREE_NULL) child = to_delete->left; + else child = to_delete->right; + + /* unlink to_delete from the tree, replace to_delete with child */ + change_parent_ptr(rbtree, to_delete->parent, to_delete, child); + change_child_ptr(child, to_delete, to_delete->parent); + + if(to_delete->color == RED) + { + /* if node is red then the child (black) can be swapped in */ + } + else if(child->color == RED) + { + /* change child to BLACK, removing a RED node is no problem */ + if(child!=RBTREE_NULL) child->color = BLACK; + } + else rbtree_delete_fixup(rbtree, child, to_delete->parent); + + /* unlink completely */ + to_delete->parent = RBTREE_NULL; + to_delete->left = RBTREE_NULL; + to_delete->right = RBTREE_NULL; + to_delete->color = BLACK; + return to_delete; +} + +static void rbtree_delete_fixup(rbtree_t* rbtree, rbnode_t* child, rbnode_t* child_parent) +{ + rbnode_t* sibling; + int go_up = 1; + + /* determine sibling to the node that is one-black short */ + if(child_parent->right == child) sibling = child_parent->left; + else sibling = child_parent->right; + + while(go_up) + { + if(child_parent == RBTREE_NULL) + { + /* removed parent==black from root, every path, so ok */ + return; + } + + if(sibling->color == RED) + { /* rotate to get a black sibling */ + child_parent->color = RED; + sibling->color = BLACK; + if(child_parent->right == child) + rbtree_rotate_right(rbtree, child_parent); + else rbtree_rotate_left(rbtree, child_parent); + /* new sibling after rotation */ + if(child_parent->right == child) sibling = child_parent->left; + else sibling = child_parent->right; + } + + if(child_parent->color == BLACK + && sibling->color == BLACK + && sibling->left->color == BLACK + && sibling->right->color == BLACK) + { /* fixup local with recolor of sibling */ + if(sibling != RBTREE_NULL) + sibling->color = RED; + + child = child_parent; + child_parent = child_parent->parent; + /* prepare to go up, new sibling */ + if(child_parent->right == child) sibling = child_parent->left; + else sibling = child_parent->right; + } + else go_up = 0; + } + + if(child_parent->color == RED + && sibling->color == BLACK + && sibling->left->color == BLACK + && sibling->right->color == BLACK) + { + /* move red to sibling to rebalance */ + if(sibling != RBTREE_NULL) + sibling->color = RED; + child_parent->color = BLACK; + return; + } + assert(sibling != RBTREE_NULL); + + /* get a new sibling, by rotating at sibling. See which child + of sibling is red */ + if(child_parent->right == child + && sibling->color == BLACK + && sibling->right->color == RED + && sibling->left->color == BLACK) + { + sibling->color = RED; + sibling->right->color = BLACK; + rbtree_rotate_left(rbtree, sibling); + /* new sibling after rotation */ + if(child_parent->right == child) sibling = child_parent->left; + else sibling = child_parent->right; + } + else if(child_parent->left == child + && sibling->color == BLACK + && sibling->left->color == RED + && sibling->right->color == BLACK) + { + sibling->color = RED; + sibling->left->color = BLACK; + rbtree_rotate_right(rbtree, sibling); + /* new sibling after rotation */ + if(child_parent->right == child) sibling = child_parent->left; + else sibling = child_parent->right; + } + + /* now we have a black sibling with a red child. rotate and exchange colors. */ + sibling->color = child_parent->color; + child_parent->color = BLACK; + if(child_parent->right == child) + { + assert(sibling->left->color == RED); + sibling->left->color = BLACK; + rbtree_rotate_right(rbtree, child_parent); + } + else + { + assert(sibling->right->color == RED); + sibling->right->color = BLACK; + rbtree_rotate_left(rbtree, child_parent); + } +} + +int +rbtree_find_less_equal(rbtree_t *rbtree, const void *key, rbnode_t **result) +{ + int r; + rbnode_t *node; + + assert(result); + + /* We start at root... */ + node = rbtree->root; + + *result = NULL; + + /* While there are children... */ + while (node != RBTREE_NULL) { + r = rbtree->cmp(key, node->key); + if (r == 0) { + /* Exact match */ + *result = node; + return 1; + } + if (r < 0) { + node = node->left; + } else { + /* Temporary match */ + *result = node; + node = node->right; + } + } + return 0; +} + +/* + * Finds the first element in the red black tree + * + */ +rbnode_t * +rbtree_first (rbtree_t *rbtree) +{ + rbnode_t *node; + + for (node = rbtree->root; node->left != RBTREE_NULL; node = node->left); + return node; +} + +rbnode_t * +rbtree_last (rbtree_t *rbtree) +{ + rbnode_t *node; + + for (node = rbtree->root; node->right != RBTREE_NULL; node = node->right); + return node; +} + +/* + * Returns the next node... + * + */ +rbnode_t * +rbtree_next (rbnode_t *node) +{ + rbnode_t *parent; + + if (node->right != RBTREE_NULL) { + /* One right, then keep on going left... */ + for (node = node->right; node->left != RBTREE_NULL; node = node->left); + } else { + parent = node->parent; + while (parent != RBTREE_NULL && node == parent->right) { + node = parent; + parent = parent->parent; + } + node = parent; + } + return node; +} + +rbnode_t * +rbtree_previous(rbnode_t *node) +{ + rbnode_t *parent; + + if (node->left != RBTREE_NULL) { + /* One left, then keep on going right... */ + for (node = node->left; node->right != RBTREE_NULL; node = node->right); + } else { + parent = node->parent; + while (parent != RBTREE_NULL && node == parent->left) { + node = parent; + parent = parent->parent; + } + node = parent; + } + return node; +} diff --git a/usr.sbin/nsd/rbtree.h b/usr.sbin/nsd/rbtree.h new file mode 100644 index 00000000000..a381cf0788f --- /dev/null +++ b/usr.sbin/nsd/rbtree.h @@ -0,0 +1,76 @@ +/* + * rbtree.h -- generic red-black tree + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _RBTREE_H_ +#define _RBTREE_H_ + +#include "region-allocator.h" + +/* + * This structure must be the first member of the data structure in + * the rbtree. This allows easy casting between an rbnode_t and the + * user data (poor man's inheritance). + */ +typedef struct rbnode_t rbnode_t; +struct rbnode_t { + rbnode_t *parent; + rbnode_t *left; + rbnode_t *right; + const void *key; + uint8_t color; +}; + +#define RBTREE_NULL &rbtree_null_node +extern rbnode_t rbtree_null_node; + +typedef struct rbtree_t rbtree_t; +struct rbtree_t { + region_type *region; + + /* The root of the red-black tree */ + rbnode_t *root; + + /* The number of the nodes in the tree */ + size_t count; + + /* Current node for walks... */ + rbnode_t *_node; + + /* Key compare function. <0,0,>0 like strcmp. Return 0 on two NULL ptrs. */ + int (*cmp) (const void *, const void *); +}; + +/* rbtree.c */ +rbtree_t *rbtree_create(region_type *region, int (*cmpf)(const void *, const void *)); +rbnode_t *rbtree_insert(rbtree_t *rbtree, rbnode_t *data); +/* returns node that is now unlinked from the tree. User to delete it. + * returns 0 if node not present */ +rbnode_t *rbtree_delete(rbtree_t *rbtree, const void *key); +rbnode_t *rbtree_search(rbtree_t *rbtree, const void *key); +/* returns true if exact match in result. Else result points to <= element, + or NULL if key is smaller than the smallest key. */ +int rbtree_find_less_equal(rbtree_t *rbtree, const void *key, rbnode_t **result); +rbnode_t *rbtree_first(rbtree_t *rbtree); +rbnode_t *rbtree_last(rbtree_t *rbtree); +rbnode_t *rbtree_next(rbnode_t *rbtree); +rbnode_t *rbtree_previous(rbnode_t *rbtree); + +#define RBTREE_WALK(rbtree, k, d) \ + for((rbtree)->_node = rbtree_first(rbtree);\ + (rbtree)->_node != RBTREE_NULL && ((k) = (rbtree)->_node->key) && \ + ((d) = (void *) (rbtree)->_node); (rbtree)->_node = rbtree_next((rbtree)->_node)) + +/* call with node=variable of struct* with rbnode_t as first element. + with type is the type of a pointer to that struct. */ +#define RBTREE_FOR(node, type, rbtree) \ + for(node=(type)rbtree_first(rbtree); \ + (rbnode_t*)node != RBTREE_NULL; \ + node = (type)rbtree_next((rbnode_t*)node)) + +#endif /* _RBTREE_H_ */ diff --git a/usr.sbin/nsd/rdata.c b/usr.sbin/nsd/rdata.c new file mode 100644 index 00000000000..26f4b703b8c --- /dev/null +++ b/usr.sbin/nsd/rdata.c @@ -0,0 +1,765 @@ +/* + * rdata.c -- RDATA conversion functions. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <netdb.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#include "rdata.h" +#include "zonec.h" + +/* Taken from RFC 4398, section 2.1. */ +lookup_table_type dns_certificate_types[] = { +/* 0 Reserved */ + { 1, "PKIX" }, /* X.509 as per PKIX */ + { 2, "SPKI" }, /* SPKI cert */ + { 3, "PGP" }, /* OpenPGP packet */ + { 4, "IPKIX" }, /* The URL of an X.509 data object */ + { 5, "ISPKI" }, /* The URL of an SPKI certificate */ + { 6, "IPGP" }, /* The fingerprint and URL of an OpenPGP packet */ + { 7, "ACPKIX" }, /* Attribute Certificate */ + { 8, "IACPKIX" }, /* The URL of an Attribute Certificate */ + { 253, "URI" }, /* URI private */ + { 254, "OID" }, /* OID private */ +/* 255 Reserved */ +/* 256-65279 Available for IANA assignment */ +/* 65280-65534 Experimental */ +/* 65535 Reserved */ + { 0, NULL } +}; + +/* Taken from RFC 2535, section 7. */ +lookup_table_type dns_algorithms[] = { + { 1, "RSAMD5" }, /* RFC 2537 */ + { 2, "DH" }, /* RFC 2539 */ + { 3, "DSA" }, /* RFC 2536 */ + { 4, "ECC" }, + { 5, "RSASHA1" }, /* RFC 3110 */ + { 252, "INDIRECT" }, + { 253, "PRIVATEDNS" }, + { 254, "PRIVATEOID" }, + { 0, NULL } +}; + +typedef int (*rdata_to_string_type)(buffer_type *output, + rdata_atom_type rdata, + rr_type *rr); + +static int +rdata_dname_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + buffer_printf(output, + "%s", + dname_to_string(domain_dname(rdata_atom_domain(rdata)), + NULL)); + return 1; +} + +static int +rdata_dns_name_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + const uint8_t *data = rdata_atom_data(rdata); + size_t offset = 0; + uint8_t length = data[offset]; + size_t i; + + while (length > 0) + { + if (offset) /* concat label */ + buffer_printf(output, "."); + + for (i = 1; i <= length; ++i) { + char ch = (char) data[i+offset]; + if (isprint((int)ch)) + buffer_printf(output, "%c", ch); + else + buffer_printf(output, "\\%03u", (unsigned) ch); + } + /* next label */ + offset = offset+length+1; + length = data[offset]; + } + + /* root label */ + buffer_printf(output, "."); + return 1; +} + +static int +rdata_text_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + const uint8_t *data = rdata_atom_data(rdata); + uint8_t length = data[0]; + size_t i; + + buffer_printf(output, "\""); + for (i = 1; i <= length; ++i) { + char ch = (char) data[i]; + if (isprint((int)ch)) { + if (ch == '"' || ch == '\\') { + buffer_printf(output, "\\"); + } + buffer_printf(output, "%c", ch); + } else { + buffer_printf(output, "\\%03u", (unsigned) ch); + } + } + buffer_printf(output, "\""); + return 1; +} + +static int +rdata_byte_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + uint8_t data = *rdata_atom_data(rdata); + buffer_printf(output, "%lu", (unsigned long) data); + return 1; +} + +static int +rdata_short_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + uint16_t data = read_uint16(rdata_atom_data(rdata)); + buffer_printf(output, "%lu", (unsigned long) data); + return 1; +} + +static int +rdata_long_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + uint32_t data = read_uint32(rdata_atom_data(rdata)); + buffer_printf(output, "%lu", (unsigned long) data); + return 1; +} + +static int +rdata_a_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + int result = 0; + char str[200]; + if (inet_ntop(AF_INET, rdata_atom_data(rdata), str, sizeof(str))) { + buffer_printf(output, "%s", str); + result = 1; + } + return result; +} + +static int +rdata_aaaa_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + int result = 0; + char str[200]; + if (inet_ntop(AF_INET6, rdata_atom_data(rdata), str, sizeof(str))) { + buffer_printf(output, "%s", str); + result = 1; + } + return result; +} + +static int +rdata_rrtype_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + uint16_t type = read_uint16(rdata_atom_data(rdata)); + buffer_printf(output, "%s", rrtype_to_string(type)); + return 1; +} + +static int +rdata_algorithm_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + uint8_t id = *rdata_atom_data(rdata); + lookup_table_type *alg + = lookup_by_id(dns_algorithms, id); + if (alg) { + buffer_printf(output, "%s", alg->name); + } else { + buffer_printf(output, "%u", (unsigned) id); + } + return 1; +} + +static int +rdata_certificate_type_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + uint16_t id = read_uint16(rdata_atom_data(rdata)); + lookup_table_type *type + = lookup_by_id(dns_certificate_types, id); + if (type) { + buffer_printf(output, "%s", type->name); + } else { + buffer_printf(output, "%u", (unsigned) id); + } + return 1; +} + +static int +rdata_period_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + uint32_t period = read_uint32(rdata_atom_data(rdata)); + buffer_printf(output, "%lu", (unsigned long) period); + return 1; +} + +static int +rdata_time_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + int result = 0; + time_t time = (time_t) read_uint32(rdata_atom_data(rdata)); + struct tm *tm = gmtime(&time); + char buf[15]; + if (strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", tm)) { + buffer_printf(output, "%s", buf); + result = 1; + } + return result; +} + +static int +rdata_base32_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + int length; + size_t size = rdata_atom_size(rdata); + if(size == 0) { + buffer_write(output, "-", 1); + return 1; + } + size -= 1; /* remove length byte from count */ + buffer_reserve(output, size * 2 + 1); + length = b32_ntop(rdata_atom_data(rdata)+1, size, + (char *) buffer_current(output), size * 2); + if (length > 0) { + buffer_skip(output, length); + } + return length != -1; +} + +static int +rdata_base64_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + int length; + size_t size = rdata_atom_size(rdata); + buffer_reserve(output, size * 2 + 1); + length = b64_ntop(rdata_atom_data(rdata), size, + (char *) buffer_current(output), size * 2); + if (length > 0) { + buffer_skip(output, length); + } + return length != -1; +} + +static void +hex_to_string(buffer_type *output, const uint8_t *data, size_t size) +{ + static const char hexdigits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + size_t i; + + buffer_reserve(output, size * 2); + for (i = 0; i < size; ++i) { + uint8_t octet = *data++; + buffer_write_u8(output, hexdigits[octet >> 4]); + buffer_write_u8(output, hexdigits[octet & 0x0f]); + } +} + +static int +rdata_hex_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + hex_to_string(output, rdata_atom_data(rdata), rdata_atom_size(rdata)); + return 1; +} + +static int +rdata_hexlen_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + if(rdata_atom_size(rdata) <= 1) { + /* NSEC3 salt hex can be empty */ + buffer_printf(output, "-"); + return 1; + } + hex_to_string(output, rdata_atom_data(rdata)+1, rdata_atom_size(rdata)-1); + return 1; +} + +static int +rdata_nsap_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + buffer_printf(output, "0x"); + hex_to_string(output, rdata_atom_data(rdata), rdata_atom_size(rdata)); + return 1; +} + +static int +rdata_apl_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + int result = 0; + buffer_type packet; + + buffer_create_from( + &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); + + if (buffer_available(&packet, 4)) { + uint16_t address_family = buffer_read_u16(&packet); + uint8_t prefix = buffer_read_u8(&packet); + uint8_t length = buffer_read_u8(&packet); + int negated = length & APL_NEGATION_MASK; + int af = -1; + + length &= APL_LENGTH_MASK; + switch (address_family) { + case 1: af = AF_INET; break; + case 2: af = AF_INET6; break; + } + if (af != -1 && buffer_available(&packet, length)) { + char text_address[1000]; + uint8_t address[128]; + memset(address, 0, sizeof(address)); + buffer_read(&packet, address, length); + if (inet_ntop(af, address, text_address, sizeof(text_address))) { + buffer_printf(output, "%s%d:%s/%d", + negated ? "!" : "", + (int) address_family, + text_address, + (int) prefix); + result = 1; + } + } + } + return result; +} + +static int +rdata_services_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + int result = 0; + buffer_type packet; + + buffer_create_from( + &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); + + if (buffer_available(&packet, 1)) { + uint8_t protocol_number = buffer_read_u8(&packet); + ssize_t bitmap_size = buffer_remaining(&packet); + uint8_t *bitmap = buffer_current(&packet); + struct protoent *proto = getprotobynumber(protocol_number); + + if (proto) { + int i; + + buffer_printf(output, "%s", proto->p_name); + + for (i = 0; i < bitmap_size * 8; ++i) { + if (get_bit(bitmap, i)) { + struct servent *service = getservbyport((int)htons(i), proto->p_name); + if (service) { + buffer_printf(output, " %s", service->s_name); + } else { + buffer_printf(output, " %d", i); + } + } + } + buffer_skip(&packet, bitmap_size); + result = 1; + } + } + return result; +} + +static int +rdata_ipsecgateway_to_string(buffer_type *output, rdata_atom_type rdata, rr_type* rr) +{ + int gateway_type = rdata_atom_data(rr->rdatas[1])[0]; + switch(gateway_type) { + case IPSECKEY_NOGATEWAY: + buffer_printf(output, "."); + break; + case IPSECKEY_IP4: + rdata_a_to_string(output, rdata, rr); + break; + case IPSECKEY_IP6: + rdata_aaaa_to_string(output, rdata, rr); + break; + case IPSECKEY_DNAME: + rdata_dname_to_string(output, rdata, rr); + break; + default: + return 0; + } + return 1; +} + +static int +rdata_nxt_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + size_t i; + uint8_t *bitmap = rdata_atom_data(rdata); + size_t bitmap_size = rdata_atom_size(rdata); + + for (i = 0; i < bitmap_size * 8; ++i) { + if (get_bit(bitmap, i)) { + buffer_printf(output, "%s ", rrtype_to_string(i)); + } + } + + buffer_skip(output, -1); + + return 1; +} + +static int +rdata_nsec_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + size_t saved_position = buffer_position(output); + buffer_type packet; + int insert_space = 0; + + buffer_create_from( + &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); + + while (buffer_available(&packet, 2)) { + uint8_t window = buffer_read_u8(&packet); + uint8_t bitmap_size = buffer_read_u8(&packet); + uint8_t *bitmap = buffer_current(&packet); + int i; + + if (!buffer_available(&packet, bitmap_size)) { + buffer_set_position(output, saved_position); + return 0; + } + + for (i = 0; i < bitmap_size * 8; ++i) { + if (get_bit(bitmap, i)) { + buffer_printf(output, + "%s%s", + insert_space ? " " : "", + rrtype_to_string( + window * 256 + i)); + insert_space = 1; + } + } + buffer_skip(&packet, bitmap_size); + } + + return 1; +} + +static int +rdata_loc_to_string(buffer_type *ATTR_UNUSED(output), + rdata_atom_type ATTR_UNUSED(rdata), + rr_type* ATTR_UNUSED(rr)) +{ + /* + * Returning 0 forces the record to be printed in unknown + * format. + */ + return 0; +} + +static int +rdata_unknown_to_string(buffer_type *output, rdata_atom_type rdata, + rr_type* ATTR_UNUSED(rr)) +{ + uint16_t size = rdata_atom_size(rdata); + buffer_printf(output, "\\# %lu ", (unsigned long) size); + hex_to_string(output, rdata_atom_data(rdata), size); + return 1; +} + +static rdata_to_string_type rdata_to_string_table[RDATA_ZF_UNKNOWN + 1] = { + rdata_dname_to_string, + rdata_dns_name_to_string, + rdata_text_to_string, + rdata_byte_to_string, + rdata_short_to_string, + rdata_long_to_string, + rdata_a_to_string, + rdata_aaaa_to_string, + rdata_rrtype_to_string, + rdata_algorithm_to_string, + rdata_certificate_type_to_string, + rdata_period_to_string, + rdata_time_to_string, + rdata_base64_to_string, + rdata_base32_to_string, + rdata_hex_to_string, + rdata_hexlen_to_string, + rdata_nsap_to_string, + rdata_apl_to_string, + rdata_ipsecgateway_to_string, + rdata_services_to_string, + rdata_nxt_to_string, + rdata_nsec_to_string, + rdata_loc_to_string, + rdata_unknown_to_string +}; + +int +rdata_atom_to_string(buffer_type *output, rdata_zoneformat_type type, + rdata_atom_type rdata, rr_type* record) +{ + return rdata_to_string_table[type](output, rdata, record); +} + +ssize_t +rdata_wireformat_to_rdata_atoms(region_type *region, + domain_table_type *owners, + uint16_t rrtype, + uint16_t data_size, + buffer_type *packet, + rdata_atom_type **rdatas) +{ + size_t end = buffer_position(packet) + data_size; + ssize_t i; + rdata_atom_type temp_rdatas[MAXRDATALEN]; + rrtype_descriptor_type *descriptor = rrtype_descriptor_by_type(rrtype); + region_type *temp_region; + + assert(descriptor->maximum <= MAXRDATALEN); + + if (!buffer_available(packet, data_size)) { + return -1; + } + + temp_region = region_create(xalloc, free); + + for (i = 0; i < descriptor->maximum; ++i) { + int is_domain = 0; + int is_normalized = 0; + int is_wirestore = 0; + size_t length = 0; + int required = i < descriptor->minimum; + + switch (rdata_atom_wireformat_type(rrtype, i)) { + case RDATA_WF_COMPRESSED_DNAME: + case RDATA_WF_UNCOMPRESSED_DNAME: + is_domain = 1; + is_normalized = 1; + break; + case RDATA_WF_LITERAL_DNAME: + is_domain = 1; + is_wirestore = 1; + break; + case RDATA_WF_BYTE: + length = sizeof(uint8_t); + break; + case RDATA_WF_SHORT: + length = sizeof(uint16_t); + break; + case RDATA_WF_LONG: + length = sizeof(uint32_t); + break; + case RDATA_WF_TEXT: + case RDATA_WF_BINARYWITHLENGTH: + /* Length is stored in the first byte. */ + length = 1; + if (buffer_position(packet) + length <= end) { + length += buffer_current(packet)[length - 1]; + } + break; + case RDATA_WF_A: + length = sizeof(in_addr_t); + break; + case RDATA_WF_AAAA: + length = IP6ADDRLEN; + break; + case RDATA_WF_BINARY: + /* Remaining RDATA is binary. */ + length = end - buffer_position(packet); + break; + case RDATA_WF_APL: + length = (sizeof(uint16_t) /* address family */ + + sizeof(uint8_t) /* prefix */ + + sizeof(uint8_t)); /* length */ + if (buffer_position(packet) + length <= end) { + /* Mask out negation bit. */ + length += (buffer_current(packet)[length - 1] + & APL_LENGTH_MASK); + } + break; + case RDATA_WF_IPSECGATEWAY: + switch(rdata_atom_data(temp_rdatas[1])[0]) /* gateway type */ { + default: + case IPSECKEY_NOGATEWAY: + length = 0; + break; + case IPSECKEY_IP4: + length = IP4ADDRLEN; + break; + case IPSECKEY_IP6: + length = IP6ADDRLEN; + break; + case IPSECKEY_DNAME: + is_domain = 1; + is_normalized = 1; + is_wirestore = 1; + break; + } + break; + } + + if (is_domain) { + const dname_type *dname; + + if (!required && buffer_position(packet) == end) { + break; + } + + dname = dname_make_from_packet( + temp_region, packet, 1, is_normalized); + if (!dname || buffer_position(packet) > end) { + /* Error in domain name. */ + region_destroy(temp_region); + return -1; + } + if(is_wirestore) { + temp_rdatas[i].data = (uint16_t *) region_alloc( + region, sizeof(uint16_t) + dname->name_size); + temp_rdatas[i].data[0] = dname->name_size; + memcpy(temp_rdatas[i].data+1, dname_name(dname), + dname->name_size); + } else + temp_rdatas[i].domain + = domain_table_insert(owners, dname); + } else { + if (buffer_position(packet) + length > end) { + if (required) { + /* Truncated RDATA. */ + region_destroy(temp_region); + return -1; + } else { + break; + } + } + + temp_rdatas[i].data = (uint16_t *) region_alloc( + region, sizeof(uint16_t) + length); + temp_rdatas[i].data[0] = length; + buffer_read(packet, temp_rdatas[i].data + 1, length); + } + } + + if (buffer_position(packet) < end) { + /* Trailing garbage. */ + region_destroy(temp_region); + return -1; + } + + *rdatas = (rdata_atom_type *) region_alloc_init( + region, temp_rdatas, i * sizeof(rdata_atom_type)); + region_destroy(temp_region); + return i; +} + +size_t +rdata_maximum_wireformat_size(rrtype_descriptor_type *descriptor, + size_t rdata_count, + rdata_atom_type *rdatas) +{ + size_t result = 0; + size_t i; + for (i = 0; i < rdata_count; ++i) { + if (rdata_atom_is_domain(descriptor->type, i)) { + result += domain_dname(rdata_atom_domain(rdatas[i]))->name_size; + } else { + result += rdata_atom_size(rdatas[i]); + } + } + return result; +} + +int +rdata_atoms_to_unknown_string(buffer_type *output, + rrtype_descriptor_type *descriptor, + size_t rdata_count, + rdata_atom_type *rdatas) +{ + size_t i; + size_t size = + rdata_maximum_wireformat_size(descriptor, rdata_count, rdatas); + buffer_printf(output, " \\# %lu ", (unsigned long) size); + for (i = 0; i < rdata_count; ++i) { + if (rdata_atom_is_domain(descriptor->type, i)) { + const dname_type *dname = + domain_dname(rdata_atom_domain(rdatas[i])); + hex_to_string( + output, dname_name(dname), dname->name_size); + } else { + hex_to_string(output, rdata_atom_data(rdatas[i]), + rdata_atom_size(rdatas[i])); + } + } + return 1; +} + +int +print_rdata(buffer_type *output, rrtype_descriptor_type *descriptor, + rr_type *record) +{ + size_t i; + size_t saved_position = buffer_position(output); + + for (i = 0; i < record->rdata_count; ++i) { + if (i == 0) { + buffer_printf(output, "\t"); + } else if (descriptor->type == TYPE_SOA && i == 2) { + buffer_printf(output, " (\n\t\t"); + } else { + buffer_printf(output, " "); + } + if (!rdata_atom_to_string( + output, + (rdata_zoneformat_type) descriptor->zoneformat[i], + record->rdatas[i], record)) + { + buffer_set_position(output, saved_position); + return 0; + } + } + if (descriptor->type == TYPE_SOA) { + buffer_printf(output, " )"); + } + + return 1; +} + + diff --git a/usr.sbin/nsd/rdata.h b/usr.sbin/nsd/rdata.h new file mode 100644 index 00000000000..0da8eab6ec0 --- /dev/null +++ b/usr.sbin/nsd/rdata.h @@ -0,0 +1,60 @@ +/* + * rdata.h -- RDATA conversion functions. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _RDATA_H_ +#define _RDATA_H_ + +#include "dns.h" +#include "namedb.h" + +/* High bit of the APL length field is the negation bit. */ +#define APL_NEGATION_MASK 0x80U +#define APL_LENGTH_MASK (~APL_NEGATION_MASK) + +extern lookup_table_type dns_certificate_types[]; +extern lookup_table_type dns_algorithms[]; + +int rdata_atom_to_string(buffer_type *output, rdata_zoneformat_type type, + rdata_atom_type rdata, rr_type *rr); + +/* + * Split the wireformat RDATA into an array of rdata atoms. Domain + * names are inserted into the OWNERS table. The number of rdata atoms + * is returned and the array itself is allocated in REGION and stored + * in RDATAS. + * + * Returns -1 on failure. + */ +ssize_t rdata_wireformat_to_rdata_atoms(region_type *region, + domain_table_type *owners, + uint16_t rrtype, + uint16_t rdata_size, + buffer_type *packet, + rdata_atom_type **rdatas); + +/* + * Calculate the maximum size of the rdata assuming domain names are + * not compressed. + */ +size_t rdata_maximum_wireformat_size(rrtype_descriptor_type *descriptor, + size_t rdata_count, + rdata_atom_type *rdatas); + +int rdata_atoms_to_unknown_string(buffer_type *out, + rrtype_descriptor_type *descriptor, + size_t rdata_count, + rdata_atom_type *rdatas); + +/* print rdata to a text string (as for a zone file) returns 0 + on a failure (bufpos is reset to original position). + returns 1 on success, bufpos is moved. */ +int print_rdata(buffer_type *output, rrtype_descriptor_type *descriptor, + rr_type *record); + +#endif /* _DNS_H_ */ diff --git a/usr.sbin/nsd/region-allocator.c b/usr.sbin/nsd/region-allocator.c new file mode 100644 index 00000000000..c2ad8215264 --- /dev/null +++ b/usr.sbin/nsd/region-allocator.c @@ -0,0 +1,459 @@ +/* + * region-allocator.c -- region based memory allocator. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "region-allocator.h" + +#ifdef ALIGNMENT +#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() */ + +typedef struct cleanup cleanup_type; +struct cleanup +{ + void (*action)(void *); + void *data; +}; + +struct recycle_elem { + struct recycle_elem* next; +}; + +struct region +{ + size_t total_allocated; + size_t small_objects; + size_t large_objects; + size_t chunk_count; + size_t unused_space; /* Unused space due to alignment, etc. */ + + size_t allocated; + char *initial_data; + char *data; + + void *(*allocator)(size_t); + void (*deallocator)(void *); + + size_t maximum_cleanup_count; + size_t cleanup_count; + cleanup_type *cleanups; + + size_t chunk_size; + size_t large_object_size; + + /* if not NULL recycling is enabled. + * It is an array of linked lists of parts held for recycle. + * The parts are all pointers to within the allocated chunks. + * Array [i] points to elements of size i. */ + struct recycle_elem** recycle_bin; + /* amount of memory in recycle storage */ + size_t recycle_size; +}; + + +static region_type * +alloc_region_base(void *(*allocator)(size_t size), + void (*deallocator)(void *), + size_t initial_cleanup_count) +{ + region_type *result = (region_type *) allocator(sizeof(region_type)); + if (!result) return NULL; + + result->total_allocated = 0; + result->small_objects = 0; + result->large_objects = 0; + result->chunk_count = 1; + result->unused_space = 0; + result->recycle_bin = NULL; + result->recycle_size = 0; + + result->allocated = 0; + result->data = NULL; + result->initial_data = NULL; + + result->allocator = allocator; + result->deallocator = deallocator; + + assert(initial_cleanup_count > 0); + result->maximum_cleanup_count = initial_cleanup_count; + result->cleanup_count = 0; + result->cleanups = (cleanup_type *) allocator( + result->maximum_cleanup_count * sizeof(cleanup_type)); + if (!result->cleanups) { + deallocator(result); + return NULL; + } + + result->chunk_size = DEFAULT_CHUNK_SIZE; + result->large_object_size = DEFAULT_LARGE_OBJECT_SIZE; + return result; +} + +region_type * +region_create(void *(*allocator)(size_t size), + void (*deallocator)(void *)) +{ + region_type* result = alloc_region_base(allocator, deallocator, + DEFAULT_INITIAL_CLEANUP_SIZE); + if(!result) + return NULL; + result->data = (char *) allocator(result->chunk_size); + if (!result->data) { + deallocator(result->cleanups); + deallocator(result); + return NULL; + } + result->initial_data = result->data; + + return result; +} + + +region_type *region_create_custom(void *(*allocator)(size_t), + void (*deallocator)(void *), + size_t chunk_size, + size_t large_object_size, + size_t initial_cleanup_size, + int recycle) +{ + region_type* result = alloc_region_base(allocator, deallocator, + initial_cleanup_size); + if(!result) + return NULL; + assert(large_object_size <= chunk_size); + result->chunk_size = chunk_size; + result->large_object_size = large_object_size; + if(result->chunk_size > 0) { + result->data = (char *) allocator(result->chunk_size); + if (!result->data) { + deallocator(result->cleanups); + deallocator(result); + return NULL; + } + result->initial_data = result->data; + } + if(recycle) { + result->recycle_bin = allocator(sizeof(struct recycle_elem*) + * result->large_object_size); + if(!result->recycle_bin) { + region_destroy(result); + return NULL; + } + memset(result->recycle_bin, 0, sizeof(struct recycle_elem*) + * result->large_object_size); + } + return result; +} + + +void +region_destroy(region_type *region) +{ + void (*deallocator)(void *); + if (!region) + return; + + deallocator = region->deallocator; + + region_free_all(region); + deallocator(region->cleanups); + deallocator(region->initial_data); + if(region->recycle_bin) + deallocator(region->recycle_bin); + deallocator(region); +} + + +size_t +region_add_cleanup(region_type *region, void (*action)(void *), void *data) +{ + assert(action); + + if (region->cleanup_count >= region->maximum_cleanup_count) { + cleanup_type *cleanups = (cleanup_type *) region->allocator( + 2 * region->maximum_cleanup_count * sizeof(cleanup_type)); + if (!cleanups) + return 0; + + memcpy(cleanups, region->cleanups, + region->cleanup_count * sizeof(cleanup_type)); + region->deallocator(region->cleanups); + + region->cleanups = cleanups; + region->maximum_cleanup_count *= 2; + } + + region->cleanups[region->cleanup_count].action = action; + region->cleanups[region->cleanup_count].data = data; + + ++region->cleanup_count; + return region->cleanup_count; +} + +void * +region_alloc(region_type *region, size_t size) +{ + size_t aligned_size; + void *result; + + if (size == 0) { + size = 1; + } + aligned_size = ALIGN_UP(size, ALIGNMENT); + + if (aligned_size >= region->large_object_size) { + result = region->allocator(size); + if (!result) + return NULL; + + if (!region_add_cleanup(region, region->deallocator, result)) { + region->deallocator(result); + return NULL; + } + + region->total_allocated += size; + ++region->large_objects; + + return result; + } + + if (region->recycle_bin && region->recycle_bin[aligned_size]) { + result = (void*)region->recycle_bin[aligned_size]; + region->recycle_bin[aligned_size] = region->recycle_bin[aligned_size]->next; + region->recycle_size -= aligned_size; + region->unused_space += aligned_size - size; + return result; + } + + if (region->allocated + aligned_size > region->chunk_size) { + void *chunk = region->allocator(region->chunk_size); + size_t wasted; + if (!chunk) + return NULL; + + wasted = (region->chunk_size - region->allocated) & (~(ALIGNMENT-1)); + if(wasted >= ALIGNMENT) { + /* put wasted part in recycle bin for later use */ + region->total_allocated += wasted; + ++region->small_objects; + region_recycle(region, region->data+region->allocated, wasted); + region->allocated += wasted; + } + ++region->chunk_count; + region->unused_space += region->chunk_size - region->allocated; + + if(!region_add_cleanup(region, region->deallocator, chunk)) { + region->deallocator(chunk); + region->chunk_count--; + region->unused_space -= + region->chunk_size - region->allocated; + return NULL; + } + region->allocated = 0; + region->data = (char *) chunk; + } + + result = region->data + region->allocated; + region->allocated += aligned_size; + + region->total_allocated += aligned_size; + region->unused_space += aligned_size - size; + ++region->small_objects; + + return result; +} + +void * +region_alloc_init(region_type *region, const void *init, size_t size) +{ + void *result = region_alloc(region, size); + if (!result) return NULL; + memcpy(result, init, size); + return result; +} + +void * +region_alloc_zero(region_type *region, size_t size) +{ + void *result = region_alloc(region, size); + if (!result) return NULL; + memset(result, 0, size); + return result; +} + +void +region_free_all(region_type *region) +{ + size_t i; + assert(region); + assert(region->cleanups); + + i = region->cleanup_count; + while (i > 0) { + --i; + assert(region->cleanups[i].action); + region->cleanups[i].action(region->cleanups[i].data); + } + + if(region->recycle_bin) { + memset(region->recycle_bin, 0, sizeof(struct recycle_elem*) + * region->large_object_size); + region->recycle_size = 0; + } + + region->data = region->initial_data; + region->cleanup_count = 0; + region->allocated = 0; + + region->total_allocated = 0; + region->small_objects = 0; + region->large_objects = 0; + region->chunk_count = 1; + region->unused_space = 0; +} + + +char * +region_strdup(region_type *region, const char *string) +{ + return (char *) region_alloc_init(region, string, strlen(string) + 1); +} + +void +region_recycle(region_type *region, void *block, size_t size) +{ + size_t aligned_size; + size_t i; + + if(!block || !region->recycle_bin) + return; + + if (size == 0) { + size = 1; + } + aligned_size = ALIGN_UP(size, ALIGNMENT); + + if(aligned_size < region->large_object_size) { + struct recycle_elem* elem = (struct recycle_elem*)block; + /* we rely on the fact that ALIGNMENT is void* so the next will fit */ + assert(aligned_size >= sizeof(struct recycle_elem)); + + if(CHECK_DOUBLE_FREE) { + /* make sure the same ptr is not freed twice. */ + struct recycle_elem *p = region->recycle_bin[aligned_size]; + while(p) { + assert(p != elem); + p = p->next; + } + } + + 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; + } + } +} + +void +region_dump_stats(region_type *region, FILE *out) +{ + fprintf(out, "%lu objects (%lu small/%lu large), %lu bytes allocated (%lu wasted) in %lu chunks, %lu cleanups, %lu in recyclebin", + (unsigned long) (region->small_objects + region->large_objects), + (unsigned long) region->small_objects, + (unsigned long) region->large_objects, + (unsigned long) region->total_allocated, + (unsigned long) region->unused_space, + (unsigned long) region->chunk_count, + (unsigned long) region->cleanup_count, + (unsigned long) region->recycle_size); + if(1 && region->recycle_bin) { + /* print details of the recycle bin */ + size_t i; + for(i=0; i<region->large_object_size; i++) { + size_t count = 0; + struct recycle_elem* el = region->recycle_bin[i]; + while(el) { + count++; + el = el->next; + } + if(i%ALIGNMENT == 0 && i!=0) + fprintf(out, " %lu", (unsigned long)count); + } + } +} + +size_t region_get_recycle_size(region_type* region) +{ + return region->recycle_size; +} + +/* debug routine, includes here to keep base region-allocator independent */ +#undef ALIGN_UP +#include "util.h" +void +region_log_stats(region_type *region) +{ + char buf[10240], *str=buf; + int len=0; + sprintf(str, "%lu objects (%lu small/%lu large), %lu bytes allocated (%lu wasted) in %lu chunks, %lu cleanups, %lu in recyclebin%n", + (unsigned long) (region->small_objects + region->large_objects), + (unsigned long) region->small_objects, + (unsigned long) region->large_objects, + (unsigned long) region->total_allocated, + (unsigned long) region->unused_space, + (unsigned long) region->chunk_count, + (unsigned long) region->cleanup_count, + (unsigned long) region->recycle_size, + &len); + str+=len; + if(1 && region->recycle_bin) { + /* print details of the recycle bin */ + size_t i; + for(i=0; i<region->large_object_size; i++) { + size_t count = 0; + struct recycle_elem* el = region->recycle_bin[i]; + while(el) { + count++; + el = el->next; + } + if(i%ALIGNMENT == 0 && i!=0) { + sprintf(str, " %lu%n", (unsigned long)count, + &len); + str+=len; + } + } + } + log_msg(LOG_INFO, "memory: %s", buf); +} diff --git a/usr.sbin/nsd/region-allocator.h b/usr.sbin/nsd/region-allocator.h new file mode 100644 index 00000000000..41891651f45 --- /dev/null +++ b/usr.sbin/nsd/region-allocator.h @@ -0,0 +1,114 @@ +/* + * region-allocator.h -- region based memory allocator. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _REGION_ALLOCATOR_H_ +#define _REGION_ALLOCATOR_H_ + +#include <stdio.h> + +typedef struct region region_type; + +#define DEFAULT_CHUNK_SIZE 4096 +#define DEFAULT_LARGE_OBJECT_SIZE (DEFAULT_CHUNK_SIZE / 8) +#define DEFAULT_INITIAL_CLEANUP_SIZE 16 + +/* + * Create a new region. + */ +region_type *region_create(void *(*allocator)(size_t), + void (*deallocator)(void *)); + + +/* + * Create a new region, with chunk size and large object size. + * Note that large_object_size must be <= chunk_size. + * Anything larger than the large object size is individually alloced. + * large_object_size = chunk_size/8 is reasonable; + * initial_cleanup_size is the number of prealloced ptrs for cleanups. + * The cleanups are in a growing array, and it must start larger than zero. + * If recycle is true, environmentally friendly memory recycling is be enabled. + */ +region_type *region_create_custom(void *(*allocator)(size_t), + void (*deallocator)(void *), + size_t chunk_size, + size_t large_object_size, + size_t initial_cleanup_size, + int recycle); + + +/* + * Destroy REGION. All memory associated with REGION is freed as if + * region_free_all was called. + */ +void region_destroy(region_type *region); + + +/* + * Add a cleanup to REGION. ACTION will be called with DATA as + * parameter when the region is freed or destroyed. + * + * Returns 0 on failure. + */ +size_t region_add_cleanup(region_type *region, + void (*action)(void *), + void *data); + + +/* + * Allocate SIZE bytes of memory inside REGION. The memory is + * deallocated when region_free_all is called for this region. + */ +void *region_alloc(region_type *region, size_t size); + + +/* + * Allocate SIZE bytes of memory inside REGION and copy INIT into it. + * The memory is deallocated when region_free_all is called for this + * region. + */ +void *region_alloc_init(region_type *region, const void *init, size_t size); + + +/* + * Allocate SIZE bytes of memory inside REGION that are initialized to + * 0. The memory is deallocated when region_free_all is called for + * this region. + */ +void *region_alloc_zero(region_type *region, size_t size); + + +/* + * Run the cleanup actions and free all memory associated with REGION. + */ +void region_free_all(region_type *region); + + +/* + * Duplicate STRING and allocate the result in REGION. + */ +char *region_strdup(region_type *region, const char *string); + +/* + * Recycle an allocated memory block. Pass size used to alloc it. + * Does nothing if recycling is not enabled for the region. + */ +void region_recycle(region_type *region, void *block, size_t size); + +/* + * Print some REGION statistics to OUT. + */ +void region_dump_stats(region_type *region, FILE *out); + +/* get size of recyclebin */ +size_t region_get_recycle_size(region_type* region); + +/* Debug print REGION statistics to LOG. */ +void region_log_stats(region_type *region); + +#endif /* _REGION_ALLOCATOR_H_ */ diff --git a/usr.sbin/nsd/server.c b/usr.sbin/nsd/server.c new file mode 100644 index 00000000000..c0a84dafe0b --- /dev/null +++ b/usr.sbin/nsd/server.c @@ -0,0 +1,1900 @@ +/* + * server.c -- nsd(8) network input/output + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <fcntl.h> +#include <netdb.h> + +#include "axfr.h" +#include "namedb.h" +#include "netio.h" +#include "xfrd.h" +#include "xfrd-tcp.h" +#include "difffile.h" +#include "nsec3.h" +#include "ipc.h" + +/* + * Data for the UDP handlers. + */ +struct udp_handler_data +{ + struct nsd *nsd; + struct nsd_socket *socket; + 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; +}; + +/* + * Data for the TCP connection handlers. + * + * The TCP handlers use non-blocking I/O. This is necessary to avoid + * blocking the entire server on a slow TCP connection, but does make + * reading from and writing to the socket more complicated. + * + * Basically, whenever a read/write would block (indicated by the + * EAGAIN errno variable) we remember the position we were reading + * from/writing to and return from the TCP reading/writing event + * handler. When the socket becomes readable/writable again we + * continue from the same position. + */ +struct tcp_handler_data +{ + /* + * The region used to allocate all TCP connection related + * data, including this structure. This region is destroyed + * when the connection is closed. + */ + region_type* region; + + /* + * The global nsd structure. + */ + struct nsd* nsd; + + /* + * The current query data for this TCP connection. + */ + 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. + */ + query_state_type query_state; + + /* + * 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 + * to specify the packet length on a TCP connection. + */ + size_t bytes_transmitted; + + /* + * The number of queries handled by this specific TCP connection. + */ + int query_count; +}; + +/* + * 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); + +/* + * Handle incoming connections on the TCP sockets. These handlers + * usually wait for the NETIO_EVENT_READ event (indicating an incoming + * connection) but are disabled when the number of current TCP + * connections is equal to the maximum number of TCP connections. + * Disabling is done by changing the handler to wait for the + * 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); + +/* + * 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); + +/* + * 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); + +/* + * Send all children the quit nonblocking, then close pipe. + */ +static void send_children_quit(struct nsd* nsd); + +/* set childrens flags to send NSD_STATS to them */ +#ifdef BIND8_STATS +static void set_children_stats(struct nsd* nsd); +#endif /* BIND8_STATS */ + +/* + * 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 uint16_t *compressed_dname_offsets = 0; +static uint32_t compression_table_capacity = 0; +static uint32_t compression_table_size = 0; + +/* + * Remove the specified pid from the list of child pids. Returns -1 if + * the pid is not in the list, child_num otherwise. The field is set to 0. + */ +static int +delete_child_pid(struct nsd *nsd, pid_t pid) +{ + size_t i; + for (i = 0; i < nsd->child_count; ++i) { + if (nsd->children[i].pid == pid) { + nsd->children[i].pid = 0; + if(!nsd->children[i].need_to_exit) { + if(nsd->children[i].child_fd > 0) + close(nsd->children[i].child_fd); + nsd->children[i].child_fd = -1; + if(nsd->children[i].handler) + nsd->children[i].handler->fd = -1; + } + return i; + } + } + return -1; +} + +/* + * Restart child servers if necessary. + */ +static int +restart_child_servers(struct nsd *nsd, region_type* region, netio_type* netio, + int* xfrd_sock_p) +{ + struct main_ipc_handler_data *ipc_data; + size_t i; + int sv[2]; + + /* Fork the child processes... */ + for (i = 0; i < nsd->child_count; ++i) { + if (nsd->children[i].pid <= 0) { + if (nsd->children[i].child_fd > 0) + close(nsd->children[i].child_fd); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { + log_msg(LOG_ERR, "socketpair: %s", + strerror(errno)); + return -1; + } + nsd->children[i].child_fd = sv[0]; + nsd->children[i].parent_fd = sv[1]; + nsd->children[i].pid = fork(); + switch (nsd->children[i].pid) { + default: /* SERVER MAIN */ + close(nsd->children[i].parent_fd); + nsd->children[i].parent_fd = -1; + if(!nsd->children[i].handler) + { + ipc_data = (struct main_ipc_handler_data*) region_alloc( + region, sizeof(struct main_ipc_handler_data)); + ipc_data->nsd = nsd; + ipc_data->child = &nsd->children[i]; + ipc_data->child_num = i; + ipc_data->xfrd_sock = xfrd_sock_p; + ipc_data->packet = buffer_create(region, QIOBUFSZ); + ipc_data->forward_mode = 0; + 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; + nsd->children[i].handler->timeout = NULL; + nsd->children[i].handler->user_data = ipc_data; + nsd->children[i].handler->event_types = NETIO_EVENT_READ; + nsd->children[i].handler->event_handler = parent_handle_child_command; + netio_add_handler(netio, nsd->children[i].handler); + } + /* clear any ongoing ipc */ + 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 */ + 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 = 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(nsd->this_child->child_fd); + nsd->this_child->child_fd = -1; + server_child(nsd); + /* NOTREACH */ + exit(0); + case -1: + log_msg(LOG_ERR, "fork failed: %s", + strerror(errno)); + return -1; + } + } + } + return 0; +} + +#ifdef BIND8_STATS +static void set_bind8_alarm(struct nsd* nsd) +{ + /* resync so that the next alarm is on the next whole minute */ + if(nsd->st.period > 0) /* % by 0 gives divbyzero error */ + alarm(nsd->st.period - (time(NULL) % nsd->st.period)); +} +#endif + +static void +cleanup_dname_compression_tables(void *ptr) +{ + free(ptr); + compressed_dname_offsets = NULL; + compression_table_capacity = 0; +} + +static void +initialize_dname_compression_tables(struct nsd *nsd) +{ + size_t needed = domain_table_count(nsd->db->domains) + 1; + needed += EXTRA_DOMAIN_NUMBERS; + if(compression_table_capacity < needed) { + compressed_dname_offsets = (uint16_t *) xalloc( + needed * sizeof(uint16_t)); + region_add_cleanup(nsd->db->region, cleanup_dname_compression_tables, + compressed_dname_offsets); + compression_table_capacity = needed; + compression_table_size=domain_table_count(nsd->db->domains)+1; + } + memset(compressed_dname_offsets, 0, needed * sizeof(uint16_t)); + compressed_dname_offsets[0] = QHEADERSZ; /* The original query name */ +} + +/* + * Initialize the server, create and bind the sockets. + * + */ +int +server_init(struct nsd *nsd) +{ + size_t i; +#if defined(SO_REUSEADDR) || (defined(INET6) && (defined(IPV6_V6ONLY) || defined(IPV6_USE_MIN_MTU) || defined(IPV6_MTU))) + int on = 1; +#endif + + /* UDP */ + + /* Make a socket... */ + for (i = 0; i < nsd->ifs; i++) { + if (!nsd->udp[i].addr) { + nsd->udp[i].s = -1; + continue; + } + if ((nsd->udp[i].s = socket(nsd->udp[i].addr->ai_family, nsd->udp[i].addr->ai_socktype, 0)) == -1) { +#if defined(INET6) + if (nsd->udp[i].addr->ai_family == AF_INET6 && + errno == EAFNOSUPPORT && nsd->grab_ip6_optional) { + log_msg(LOG_WARNING, "fallback to UDP4, no IPv6: not supported"); + continue; + } +#endif /* INET6 */ + log_msg(LOG_ERR, "can't create a socket: %s", strerror(errno)); + return -1; + } + +#if defined(INET6) + if (nsd->udp[i].addr->ai_family == AF_INET6) { +# if defined(IPV6_V6ONLY) + if (setsockopt(nsd->udp[i].s, + IPPROTO_IPV6, IPV6_V6ONLY, + &on, sizeof(on)) < 0) + { + log_msg(LOG_ERR, "setsockopt(..., IPV6_V6ONLY, ...) failed: %s", + strerror(errno)); + return -1; + } +# endif +# if defined(IPV6_USE_MIN_MTU) + /* + * There is no fragmentation of IPv6 datagrams + * during forwarding in the network. Therefore + * we do not send UDP datagrams larger than + * the minimum IPv6 MTU of 1280 octets. The + * EDNS0 message length can be larger if the + * network stack supports IPV6_USE_MIN_MTU. + */ + if (setsockopt(nsd->udp[i].s, + IPPROTO_IPV6, IPV6_USE_MIN_MTU, + &on, sizeof(on)) < 0) + { + log_msg(LOG_ERR, "setsockopt(..., IPV6_USE_MIN_MTU, ...) failed: %s", + strerror(errno)); + return -1; + } +# elif defined(IPV6_MTU) + /* + * On Linux, PMTUD is disabled by default for datagrams + * so set the MTU equal to the MIN MTU to get the same. + */ + on = IPV6_MIN_MTU; + if (setsockopt(nsd->udp[i].s, IPPROTO_IPV6, IPV6_MTU, + &on, sizeof(on)) < 0) + { + log_msg(LOG_ERR, "setsockopt(..., IPV6_MTU, ...) failed: %s", + strerror(errno)); + return -1; + } + on = 1; +# endif + } +#endif +#if defined(AF_INET) + if (nsd->udp[i].addr->ai_family == AF_INET) { +# if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + int action = IP_PMTUDISC_DONT; + if (setsockopt(nsd->udp[i].s, IPPROTO_IP, + IP_MTU_DISCOVER, &action, sizeof(action)) < 0) + { + log_msg(LOG_ERR, "setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_DONT...) failed: %s", + strerror(errno)); + return -1; + } +# elif defined(IP_DONTFRAG) + int off = 0; + if (setsockopt(nsd->udp[i].s, IPPROTO_IP, IP_DONTFRAG, + &off, sizeof(off)) < 0) + { + log_msg(LOG_ERR, "setsockopt(..., IP_DONTFRAG, ...) failed: %s", + strerror(errno)); + return -1; + } +# endif + } +#endif + /* set it nonblocking */ + /* otherwise, on OSes with thundering herd problems, the + UDP recv could block NSD after select returns readable. */ + if (fcntl(nsd->udp[i].s, F_SETFL, O_NONBLOCK) == -1) { + log_msg(LOG_ERR, "cannot fcntl udp: %s", strerror(errno)); + } + + /* Bind it... */ + if (bind(nsd->udp[i].s, (struct sockaddr *) nsd->udp[i].addr->ai_addr, nsd->udp[i].addr->ai_addrlen) != 0) { + log_msg(LOG_ERR, "can't bind udp socket: %s", strerror(errno)); + return -1; + } + } + + /* TCP */ + + /* Make a socket... */ + for (i = 0; i < nsd->ifs; i++) { + if (!nsd->tcp[i].addr) { + nsd->tcp[i].s = -1; + continue; + } + if ((nsd->tcp[i].s = socket(nsd->tcp[i].addr->ai_family, nsd->tcp[i].addr->ai_socktype, 0)) == -1) { +#if defined(INET6) + if (nsd->tcp[i].addr->ai_family == AF_INET6 && + errno == EAFNOSUPPORT && nsd->grab_ip6_optional) { + log_msg(LOG_WARNING, "fallback to TCP4, no IPv6: not supported"); + continue; + } +#endif /* INET6 */ + log_msg(LOG_ERR, "can't create a socket: %s", strerror(errno)); + return -1; + } + +#ifdef SO_REUSEADDR + if (setsockopt(nsd->tcp[i].s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + log_msg(LOG_ERR, "setsockopt(..., SO_REUSEADDR, ...) failed: %s", strerror(errno)); + } +#endif /* SO_REUSEADDR */ + +#if defined(INET6) && defined(IPV6_V6ONLY) + if (nsd->tcp[i].addr->ai_family == AF_INET6 && + setsockopt(nsd->tcp[i].s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) + { + log_msg(LOG_ERR, "setsockopt(..., IPV6_V6ONLY, ...) failed: %s", strerror(errno)); + return -1; + } +#endif + /* set it nonblocking */ + /* (StevensUNP p463), if tcp listening socket is blocking, then + it may block in accept, even if select() says readable. */ + if (fcntl(nsd->tcp[i].s, F_SETFL, O_NONBLOCK) == -1) { + log_msg(LOG_ERR, "cannot fcntl tcp: %s", strerror(errno)); + } + + /* Bind it... */ + if (bind(nsd->tcp[i].s, (struct sockaddr *) nsd->tcp[i].addr->ai_addr, nsd->tcp[i].addr->ai_addrlen) != 0) { + log_msg(LOG_ERR, "can't bind tcp socket: %s", strerror(errno)); + return -1; + } + + /* Listen to it... */ + if (listen(nsd->tcp[i].s, TCP_BACKLOG) == -1) { + log_msg(LOG_ERR, "can't listen: %s", strerror(errno)); + return -1; + } + } + + return 0; +} + +/* + * Prepare the server for take off. + * + */ +int +server_prepare(struct nsd *nsd) +{ + /* Open the database... */ + if ((nsd->db = namedb_open(nsd->dbfile, nsd->options, nsd->child_count)) == NULL) { + log_msg(LOG_ERR, "unable to open the database %s: %s", + nsd->dbfile, strerror(errno)); + 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 + + compression_table_capacity = 0; + initialize_dname_compression_tables(nsd); + +#ifdef BIND8_STATS + /* Initialize times... */ + time(&nsd->st.boot); + set_bind8_alarm(nsd); +#endif /* BIND8_STATS */ + + return 0; +} + +/* + * Fork the required number of servers. + */ +static int +server_start_children(struct nsd *nsd, region_type* region, netio_type* netio, + int* xfrd_sock_p) +{ + size_t i; + + /* Start all child servers initially. */ + for (i = 0; i < nsd->child_count; ++i) { + nsd->children[i].pid = 0; + } + + return restart_child_servers(nsd, region, netio, xfrd_sock_p); +} + +static void +close_all_sockets(struct nsd_socket sockets[], size_t n) +{ + size_t i; + + /* Close all the sockets... */ + for (i = 0; i < n; ++i) { + if (sockets[i].s != -1) { + close(sockets[i].s); + free(sockets[i].addr); + sockets[i].s = -1; + } + } +} + +/* + * Close the sockets, shutdown the server and exit. + * Does not return. + * + */ +static void +server_shutdown(struct nsd *nsd) +{ + size_t i; + + close_all_sockets(nsd->udp, nsd->ifs); + close_all_sockets(nsd->tcp, nsd->ifs); + /* CHILD: close command channel to parent */ + if(nsd->this_child && nsd->this_child->parent_fd > 0) + { + close(nsd->this_child->parent_fd); + nsd->this_child->parent_fd = -1; + } + /* SERVER: close command channels to children */ + if(!nsd->this_child) + { + for(i=0; i < nsd->child_count; ++i) + if(nsd->children[i].child_fd > 0) + { + close(nsd->children[i].child_fd); + nsd->children[i].child_fd = -1; + } + } + + log_finalize(); + tsig_finalize(); + + nsd_options_destroy(nsd->options); + region_destroy(nsd->region); + + exit(0); +} + +static pid_t +server_start_xfrd(struct nsd *nsd, netio_handler_type* handler) +{ + 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 (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { + log_msg(LOG_ERR, "startxfrd failed on socketpair: %s", strerror(errno)); + return -1; + } + 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 */ + close(sockets[0]); + xfrd_init(sockets[1], nsd); + /* ENOTREACH */ + break; + default: + /* PARENT: close second socket, use first one */ + close(sockets[1]); + handler->fd = sockets[0]; + break; + } + /* PARENT only */ + handler->timeout = NULL; + handler->event_types = NETIO_EVENT_READ; + handler->event_handler = parent_handle_xfrd_command; + /* clear ongoing ipc reads */ + data = (struct ipc_handler_conn_data *) handler->user_data; + data->conn->is_reading = 0; + return pid; +} + +/* pass timeout=-1 for blocking. Returns size, 0, -1(err), or -2(timeout) */ +static ssize_t +block_read(struct nsd* nsd, int s, void* p, ssize_t sz, int timeout) +{ + uint8_t* buf = (uint8_t*) p; + ssize_t total = 0; + fd_set rfds; + struct timeval tv; + FD_ZERO(&rfds); + + while( total < sz) { + ssize_t ret; + FD_SET(s, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + ret = select(s+1, &rfds, NULL, NULL, timeout==-1?NULL:&tv); + if(ret == -1) { + if(errno == EAGAIN) + /* blocking read */ + continue; + if(errno == EINTR) { + if(nsd->signal_hint_quit || nsd->signal_hint_shutdown) + return -1; + /* other signals can be handled later */ + continue; + } + /* some error */ + return -1; + } + if(ret == 0) { + /* operation timed out */ + return -2; + } + ret = read(s, buf+total, sz-total); + if(ret == -1) { + if(errno == EAGAIN) + /* blocking read */ + continue; + if(errno == EINTR) { + if(nsd->signal_hint_quit || nsd->signal_hint_shutdown) + return -1; + /* other signals can be handled later */ + continue; + } + /* some error */ + return -1; + } + if(ret == 0) { + /* closed connection! */ + return 0; + } + total += ret; + } + return total; +} + +/* + * 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) +{ + pid_t old_pid; + sig_atomic_t cmd = NSD_QUIT_SYNC; + zone_type* zone; + int xfrd_sock = *xfrd_sock_p; + int ret; + + 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(!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 + prehash(nsd->db, 1); +#endif /* NSEC3 */ + + 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); + set_bind8_alarm(nsd); +#endif + + /* Start new child processes */ + if (server_start_children(nsd, server_region, netio, xfrd_sock_p) != 0) { + send_children_quit(nsd); + exit(1); + } + + /* 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)) == -1) + { + log_msg(LOG_ERR, "problems sending command from reload %d to oldnsd %d: %s", + (int)nsd->pid, (int)old_pid, 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")); + ret = block_read(nsd, cmdsocket, &cmd, sizeof(cmd), + RELOAD_SYNC_TIMEOUT); + if(ret == -2) { + DEBUG(DEBUG_IPC, 1, (LOG_ERR, "reload timeout QUITSYNC. retry")); + } + } while (ret == -2); + if(ret == -1) { + log_msg(LOG_ERR, "reload: could not wait for parent to quit: %s", + strerror(errno)); + } + DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: ipc reply main %d %d", ret, cmd)); + 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; + } + 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)); + } + + /* try to reopen file */ + if (nsd->file_rotation_ok) + log_reopen(nsd->log_filename, 1); + /* exit reload, continue as new server_main */ +} + +/* + * Get the mode depending on the signal hints that have been received. + * Multiple signal hints can be received and will be handled in turn. + */ +static sig_atomic_t +server_signal_mode(struct nsd *nsd) +{ + if(nsd->signal_hint_quit) { + nsd->signal_hint_quit = 0; + return NSD_QUIT; + } + else if(nsd->signal_hint_shutdown) { + nsd->signal_hint_shutdown = 0; + return NSD_SHUTDOWN; + } + else if(nsd->signal_hint_child) { + nsd->signal_hint_child = 0; + return NSD_REAP_CHILDREN; + } + else if(nsd->signal_hint_reload) { + nsd->signal_hint_reload = 0; + return NSD_RELOAD; + } + else if(nsd->signal_hint_stats) { + nsd->signal_hint_stats = 0; +#ifdef BIND8_STATS + set_bind8_alarm(nsd); +#endif + return NSD_STATS; + } + else if(nsd->signal_hint_statsusr) { + nsd->signal_hint_statsusr = 0; + return NSD_STATS; + } + return NSD_RUN; +} + +/* + * The main server simply waits for signals and child processes to + * terminate. Child processes are restarted as necessary. + */ +void +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; + + /* 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); + + /* Start the child processes that handle incoming queries */ + if (server_start_children(nsd, server_region, netio, &xfrd_listener.fd) != 0) { + send_children_quit(nsd); + exit(1); + } + reload_listener.fd = -1; + + /* This_child MUST be 0, because this is the parent process */ + assert(nsd->this_child == 0); + + /* Run the server until we get a shutdown signal */ + while ((mode = nsd->mode) != NSD_SHUTDOWN) { + /* Did we receive a signal that changes our mode? */ + if(mode == NSD_RUN) { + nsd->mode = mode = server_signal_mode(nsd); + } + + switch (mode) { + case NSD_RUN: + /* see if any child processes terminated */ + while((child_pid = waitpid(0, &status, WNOHANG)) != -1 && child_pid != 0) { + int is_child = delete_child_pid(nsd, child_pid); + if (is_child != -1 && nsd->children[is_child].need_to_exit) { + if(nsd->children[is_child].child_fd == -1) + nsd->children[is_child].has_exited = 1; + parent_check_all_children_exited(nsd); + } else if(is_child != -1) { + log_msg(LOG_WARNING, + "server %d died unexpectedly with status %d, restarting", + (int) child_pid, status); + restart_child_servers(nsd, server_region, netio, + &xfrd_listener.fd); + } else if (child_pid == reload_pid) { + sig_atomic_t cmd = NSD_SOA_END; + 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); + reload_listener.fd = -1; + reload_listener.event_types = NETIO_EVENT_NONE; + /* inform xfrd reload attempt ended */ + if(!write_socket(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); + } else { + log_msg(LOG_WARNING, + "Unknown child %d terminated with status %d", + (int) child_pid, status); + } + } + if (child_pid == -1) { + if (errno == EINTR) { + continue; + } + log_msg(LOG_WARNING, "wait failed: %s", strerror(errno)); + } + if (nsd->mode != NSD_RUN) + break; + + /* timeout to collect processes. In case no sigchild happens. */ + timeout_spec.tv_sec = 60; + timeout_spec.tv_nsec = 0; + + /* listen on ports, timeout for collecting terminated children */ + if(netio_dispatch(netio, &timeout_spec, 0) == -1) { + if (errno != EINTR) { + log_msg(LOG_ERR, "netio_dispatch failed: %s", strerror(errno)); + } + } + + break; + case NSD_RELOAD: + /* Continue to run nsd after reload */ + nsd->mode = NSD_RUN; + + 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..."); + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, reload_sockets) == -1) { + log_msg(LOG_ERR, "reload failed on socketpair: %s", strerror(errno)); + reload_pid = -1; + break; + } + + /* Do actual reload */ + reload_pid = fork(); + switch (reload_pid) { + case -1: + log_msg(LOG_ERR, "fork failed: %s", strerror(errno)); + break; + case 0: + /* CHILD */ + close(reload_sockets[0]); + server_reload(nsd, server_region, netio, + reload_sockets[1], &xfrd_listener.fd); + 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) + ->conn->is_reading = 0; + reload_pid = -1; + reload_listener.fd = -1; + reload_listener.event_types = NETIO_EVENT_NONE; + DEBUG(DEBUG_IPC,2, (LOG_INFO, "Reload resetup; run")); + break; + default: + /* PARENT, keep running until NSD_QUIT_SYNC + * received from CHILD. + */ + close(reload_sockets[1]); + reload_listener.fd = reload_sockets[0]; + reload_listener.timeout = NULL; + reload_listener.user_data = nsd; + reload_listener.event_types = NETIO_EVENT_READ; + reload_listener.event_handler = parent_handle_reload_command; /* listens to Quit */ + netio_add_handler(netio, &reload_listener); + break; + } + break; + case NSD_QUIT_SYNC: + /* synchronisation of xfrd, parent and reload */ + if(!nsd->quit_sync_done && reload_listener.fd > 0) { + sig_atomic_t cmd = NSD_RELOAD; + /* 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))) { + log_msg(LOG_ERR, "server_main: could not send reload " + "indication to xfrd: %s", strerror(errno)); + } + /* wait for ACK from xfrd */ + DEBUG(DEBUG_IPC,1, (LOG_INFO, "main: wait ipc reply xfrd")); + nsd->quit_sync_done = 1; + } + nsd->mode = NSD_RUN; + break; + case NSD_QUIT: + /* silent shutdown during reload */ + if(reload_listener.fd > 0) { + /* acknowledge the quit, to sync reload that we will really quit now */ + sig_atomic_t cmd = NSD_RELOAD; + DEBUG(DEBUG_IPC,1, (LOG_INFO, "main: ipc ack reload")); + if(!write_socket(reload_listener.fd, &cmd, sizeof(cmd))) { + log_msg(LOG_ERR, "server_main: " + "could not ack quit: %s", strerror(errno)); + } + close(reload_listener.fd); + } + /* only quit children after xfrd has acked */ + send_children_quit(nsd); + + region_destroy(server_region); + namedb_close(nsd->db); + server_shutdown(nsd); + + /* ENOTREACH */ + break; + case NSD_SHUTDOWN: + send_children_quit(nsd); + log_msg(LOG_WARNING, "signal received, shutting down..."); + break; + case NSD_REAP_CHILDREN: + /* continue; wait for child in run loop */ + nsd->mode = NSD_RUN; + break; + case NSD_STATS: +#ifdef BIND8_STATS + set_children_stats(nsd); +#endif + nsd->mode = NSD_RUN; + break; + default: + log_msg(LOG_WARNING, "NSD main server mode invalid: %d", 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)); + } + close(fd); + + /* Unlink it if possible... */ + unlinkpid(nsd->pidfile); + + if(reload_listener.fd > 0) + close(reload_listener.fd); + if(xfrd_listener.fd > 0) { + /* 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))) { + log_msg(LOG_ERR, "server_main: could not send quit to xfrd: %s", + strerror(errno)); + } + fsync(xfrd_listener.fd); + close(xfrd_listener.fd); + } + + namedb_close(nsd->db); + region_destroy(server_region); + server_shutdown(nsd); +} + +static query_state_type +server_process_query(struct nsd *nsd, struct query *query) +{ + return query_process(query, nsd); +} + + +/* + * Serve DNS requests. + */ +void +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; + query_type *udp_query; + sig_atomic_t mode; + + assert(nsd->server_kind != NSD_SERVER_MAIN); + DEBUG(DEBUG_IPC, 2, (LOG_INFO, "child process started")); + + if (!(nsd->server_kind & NSD_SERVER_TCP)) { + close_all_sockets(nsd->tcp, nsd->ifs); + } + if (!(nsd->server_kind & NSD_SERVER_UDP)) { + close_all_sockets(nsd->udp, nsd->ifs); + } + + 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( + 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); + } + + if (nsd->server_kind & NSD_SERVER_UDP) { + udp_query = query_create(server_region, + compressed_dname_offsets, compression_table_size); + + for (i = 0; i < nsd->ifs; ++i) { + struct udp_handler_data *data; + netio_handler_type *handler; + + data = (struct udp_handler_data *) region_alloc( + server_region, + sizeof(struct udp_handler_data)); + data->query = udp_query; + 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); + } + } + + /* + * Keep track of all the TCP accept handlers so we can enable + * 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)); + 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)); + 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; + handler->event_handler = handle_tcp_accept; + netio_add_handler(netio, handler); + } + } + + /* The main loop... */ + while ((mode = nsd->mode) != NSD_QUIT) { + if(mode == NSD_RUN) nsd->mode = mode = server_signal_mode(nsd); + + /* Do we need to do the statistics... */ + if (mode == NSD_STATS) { +#ifdef BIND8_STATS + /* Dump the statistics */ + bind8_stats(nsd); +#else /* !BIND8_STATS */ + log_msg(LOG_NOTICE, "Statistics support not enabled at compile time."); +#endif /* BIND8_STATS */ + + nsd->mode = NSD_RUN; + } + else if (mode == NSD_REAP_CHILDREN) { + /* got signal, notify parent. parent reaps terminated children. */ + if (nsd->this_child->parent_fd > 0) { + sig_atomic_t parent_notify = NSD_REAP_CHILDREN; + if (write(nsd->this_child->parent_fd, + &parent_notify, + sizeof(parent_notify)) == -1) + { + log_msg(LOG_ERR, "problems sending command from %d to parent: %s", + (int) nsd->this_child->pid, strerror(errno)); + } + } else /* no parent, so reap 'em */ + while (waitpid(0, NULL, WNOHANG) > 0) ; + nsd->mode = NSD_RUN; + } + else if(mode == NSD_RUN) { + /* Wait for a query... */ + if (netio_dispatch(netio, NULL, NULL) == -1) { + if (errno != EINTR) { + log_msg(LOG_ERR, "netio_dispatch failed: %s", strerror(errno)); + break; + } + } + } else if(mode == NSD_QUIT) { + /* ignore here, quit */ + } else { + log_msg(LOG_ERR, "mode bad value %d, back to service.", + mode); + nsd->mode = NSD_RUN; + } + } + +#ifdef BIND8_STATS + bind8_stats(nsd); +#endif /* BIND8_STATS */ + + namedb_close(nsd->db); + region_destroy(server_region); + server_shutdown(nsd); +} + + +static void +handle_udp(netio_type *ATTR_UNUSED(netio), + netio_handler_type *handler, + netio_event_types_type event_types) +{ + struct udp_handler_data *data + = (struct udp_handler_data *) handler->user_data; + int received, sent; + struct query *q = data->query; + + if (!(event_types & NETIO_EVENT_READ)) { + return; + } + + /* 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); + } + + /* Initialize the query... */ + query_reset(q, UDP_MAX_MESSAGE_LEN, 0); + + received = recvfrom(handler->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); + } + } else { + buffer_skip(q->packet, received); + buffer_flip(q->packet); + + /* Process and answer the query... */ + if (server_process_query(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); + + sent = sendto(handler->fd, + buffer_begin(q->packet), + buffer_remaining(q->packet), + 0, + (struct sockaddr *) &q->addr, + q->addrlen); + if (sent == -1) { + log_msg(LOG_ERR, "sendto failed: %s", strerror(errno)); + STATUP(data->nsd, txerr); + } 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)); + } else { +#ifdef BIND8_STATS + /* Account the rcode & TC... */ + STATUP2(data->nsd, rcode, RCODE(q->packet)); + if (TC(q->packet)) + STATUP(data->nsd, truncated); +#endif /* BIND8_STATS */ + } + } else { + STATUP(data->nsd, dropped); + } + } +} + + +static void +cleanup_tcp_handler(netio_type *netio, netio_handler_type *handler) +{ + struct tcp_handler_data *data + = (struct tcp_handler_data *) handler->user_data; + netio_remove_handler(netio, handler); + close(handler->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); + } + --data->nsd->current_tcp_count; + assert(data->nsd->current_tcp_count >= 0); + + region_destroy(data->region); +} + +static void +handle_tcp_reading(netio_type *netio, + netio_handler_type *handler, + netio_event_types_type event_types) +{ + struct tcp_handler_data *data + = (struct tcp_handler_data *) handler->user_data; + ssize_t received; + + if (event_types & NETIO_EVENT_TIMEOUT) { + /* Connection timed out. */ + cleanup_tcp_handler(netio, handler); + 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); + return; + } + + assert(event_types & NETIO_EVENT_READ); + + if (data->bytes_transmitted == 0) { + query_reset(data->query, TCP_MAX_MESSAGE_LEN, 1); + } + + /* + * Check if we received the leading packet length bytes yet. + */ + if (data->bytes_transmitted < sizeof(uint16_t)) { + received = read(handler->fd, + (char *) &data->query->tcplen + + data->bytes_transmitted, + sizeof(uint16_t) - data->bytes_transmitted); + if (received == -1) { + if (errno == EAGAIN || errno == EINTR) { + /* + * Read would block, wait until more + * data is available. + */ + return; + } else { +#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); + return; + } + } else if (received == 0) { + /* EOF */ + cleanup_tcp_handler(netio, handler); + return; + } + + data->bytes_transmitted += received; + if (data->bytes_transmitted < sizeof(uint16_t)) { + /* + * Not done with the tcplen yet, wait for more + * data to become available. + */ + return; + } + + assert(data->bytes_transmitted == sizeof(uint16_t)); + + data->query->tcplen = ntohs(data->query->tcplen); + + /* + * Minimum query size is: + * + * Size of the header (12) + * + Root domain name (1) + * + Query class (2) + * + Query type (2) + */ + 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); + return; + } + + if (data->query->tcplen > data->query->maxlen) { + VERBOSITY(2, (LOG_WARNING, "insufficient tcp buffer, dropping connection")); + cleanup_tcp_handler(netio, handler); + return; + } + + buffer_set_limit(data->query->packet, data->query->tcplen); + } + + assert(buffer_remaining(data->query->packet) > 0); + + /* Read the (remaining) query data. */ + received = read(handler->fd, + buffer_current(data->query->packet), + buffer_remaining(data->query->packet)); + if (received == -1) { + if (errno == EAGAIN || errno == EINTR) { + /* + * Read would block, wait until more data is + * available. + */ + return; + } else { +#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); + return; + } + } else if (received == 0) { + /* EOF */ + cleanup_tcp_handler(netio, handler); + return; + } + + data->bytes_transmitted += received; + buffer_skip(data->query->packet, received); + if (buffer_remaining(data->query->packet) > 0) { + /* + * Message not yet complete, wait for more data to + * become available. + */ + return; + } + + assert(buffer_position(data->query->packet) == data->query->tcplen); + + /* Account... */ +#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 + + /* We have a complete query, process it. */ + + /* tcp-query-count: handle query counter ++ */ + data->query_count++; + + buffer_flip(data->query->packet); + data->query_state = server_process_query(data->nsd, data->query); + if (data->query_state == QUERY_DISCARDED) { + /* Drop the packet and the entire connection... */ + STATUP(data->nsd, dropped); + cleanup_tcp_handler(netio, handler); + return; + } + + if (RCODE(data->query->packet) == RCODE_OK + && !AA(data->query->packet)) + { + STATUP(data->nsd, nona); + } + + query_add_optional(data->query, data->nsd); + + /* Switch to the tcp write handler. */ + buffer_flip(data->query->packet); + 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; +} + +static void +handle_tcp_writing(netio_type *netio, + netio_handler_type *handler, + netio_event_types_type event_types) +{ + struct tcp_handler_data *data + = (struct tcp_handler_data *) handler->user_data; + ssize_t sent; + struct query *q = data->query; + + if (event_types & NETIO_EVENT_TIMEOUT) { + /* Connection timed out. */ + cleanup_tcp_handler(netio, handler); + return; + } + + assert(event_types & NETIO_EVENT_WRITE); + + if (data->bytes_transmitted < sizeof(q->tcplen)) { + /* Writing the response packet length. */ + uint16_t n_tcplen = htons(q->tcplen); + sent = write(handler->fd, + (const char *) &n_tcplen + data->bytes_transmitted, + sizeof(n_tcplen) - data->bytes_transmitted); + if (sent == -1) { + if (errno == EAGAIN || errno == EINTR) { + /* + * Write would block, wait until + * socket becomes writable again. + */ + return; + } else { +#ifdef ECONNRESET + if(verbosity >= 2 || errno != ECONNRESET) +#endif /* ECONNRESET */ + log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); + cleanup_tcp_handler(netio, handler); + return; + } + } + + data->bytes_transmitted += sent; + if (data->bytes_transmitted < sizeof(q->tcplen)) { + /* + * Writing not complete, wait until socket + * becomes writable again. + */ + return; + } + + assert(data->bytes_transmitted == sizeof(q->tcplen)); + } + + assert(data->bytes_transmitted < q->tcplen + sizeof(q->tcplen)); + + sent = write(handler->fd, + buffer_current(q->packet), + buffer_remaining(q->packet)); + if (sent == -1) { + if (errno == EAGAIN || errno == EINTR) { + /* + * Write would block, wait until + * socket becomes writable again. + */ + return; + } else { +#ifdef ECONNRESET + if(verbosity >= 2 || errno != ECONNRESET) +#endif /* ECONNRESET */ + log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); + cleanup_tcp_handler(netio, handler); + return; + } + } + + buffer_skip(q->packet, sent); + data->bytes_transmitted += sent; + if (data->bytes_transmitted < q->tcplen + sizeof(q->tcplen)) { + /* + * Still more data to write when socket becomes + * writable again. + */ + return; + } + + assert(data->bytes_transmitted == q->tcplen + sizeof(q->tcplen)); + + if (data->query_state == QUERY_IN_AXFR) { + /* Continue processing AXFR and writing back results. */ + buffer_clear(q->packet); + data->query_state = query_axfr(data->nsd, q); + if (data->query_state != QUERY_PROCESSED) { + query_add_optional(data->query, data->nsd); + + /* Reset data. */ + buffer_flip(q->packet); + 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)); + + /* + * Write data if/when the socket is writable + * again. + */ + return; + } + } + + /* + * Done sending, wait for the next request to arrive on the + * TCP socket by installing the TCP read handler. + */ + if (data->nsd->tcp_query_count > 0 && + data->query_count >= data->nsd->tcp_query_count) { + + (void) shutdown(handler->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; +} + + +/* + * Handle an incoming TCP connection. The connection is accepted and + * a new TCP reader event handler is added to NETIO. 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) +{ + struct tcp_accept_handler_data *data + = (struct tcp_accept_handler_data *) handler->user_data; + 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; + + if (!(event_types & NETIO_EVENT_READ)) { + return; + } + + if (data->nsd->current_tcp_count >= data->nsd->maximum_tcp_count) { + return; + } + + /* Accept it... */ + addrlen = sizeof(addr); + s = accept(handler->fd, (struct sockaddr *) &addr, &addrlen); + if (s == -1) { + /* EINTR is a signal interrupt. The others are various OS ways + of saying that the client has closed the connection. */ + if ( errno != EINTR + && errno != EWOULDBLOCK +#ifdef ECONNABORTED + && errno != ECONNABORTED +#endif /* ECONNABORTED */ +#ifdef EPROTO + && errno != EPROTO +#endif /* EPROTO */ + ) { + log_msg(LOG_ERR, "accept failed: %s", strerror(errno)); + } + return; + } + + if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { + log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno)); + close(s); + return; + } + + /* + * This region is deallocated when the TCP connection is + * closed by the TCP handler. + */ + tcp_region = region_create(xalloc, free); + tcp_data = (struct tcp_handler_data *) region_alloc( + tcp_region, sizeof(struct tcp_handler_data)); + tcp_data->region = tcp_region; + tcp_data->query = query_create(tcp_region, compressed_dname_offsets, + compression_table_size); + 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)); + + 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); + + /* + * Keep track of the total number of TCP handlers installed so + * we can stop accepting connections when the maximum number + * of simultaneous TCP connections is reached. + */ + ++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); + } +} + +static void +send_children_quit(struct nsd* nsd) +{ + sig_atomic_t command = NSD_QUIT; + size_t i; + assert(nsd->server_kind == NSD_SERVER_MAIN && nsd->this_child == 0); + for (i = 0; i < nsd->child_count; ++i) { + if (nsd->children[i].pid > 0 && nsd->children[i].child_fd > 0) { + if (write(nsd->children[i].child_fd, + &command, + sizeof(command)) == -1) + { + if(errno != EAGAIN && errno != EINTR) + log_msg(LOG_ERR, "problems sending command %d to server %d: %s", + (int) command, + (int) nsd->children[i].pid, + strerror(errno)); + } + fsync(nsd->children[i].child_fd); + close(nsd->children[i].child_fd); + nsd->children[i].child_fd = -1; + } + } +} + +#ifdef BIND8_STATS +static void +set_children_stats(struct nsd* nsd) +{ + size_t i; + assert(nsd->server_kind == NSD_SERVER_MAIN && nsd->this_child == 0); + DEBUG(DEBUG_IPC, 1, (LOG_INFO, "parent set stats to send to children")); + for (i = 0; i < nsd->child_count; ++i) { + nsd->children[i].need_to_send_STATS = 1; + nsd->children[i].handler->event_types |= NETIO_EVENT_WRITE; + } +} +#endif /* BIND8_STATS */ + +static void +configure_handler_event_types(size_t count, + netio_handler_type *handlers, + netio_event_types_type event_types) +{ + size_t i; + + assert(handlers); + + for (i = 0; i < count; ++i) { + handlers[i].event_types = event_types; + } +} diff --git a/usr.sbin/nsd/tsig-openssl.c b/usr.sbin/nsd/tsig-openssl.c new file mode 100644 index 00000000000..5773fd2a674 --- /dev/null +++ b/usr.sbin/nsd/tsig-openssl.c @@ -0,0 +1,127 @@ +/* + * tsig-openssl.h -- Interface to OpenSSL for TSIG support. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#if defined(TSIG) && defined(HAVE_SSL) + +#include "tsig-openssl.h" +#include "tsig.h" +#include "util.h" + +static void *create_context(region_type *region); +static void init_context(void *context, + tsig_algorithm_type *algorithm, + tsig_key_type *key); +static void update(void *context, const void *data, size_t size); +static void final(void *context, uint8_t *digest, size_t *size); + +static int +tsig_openssl_init_algorithm(region_type* region, + const char* digest, const char* name, const char* wireformat) +{ + tsig_algorithm_type* algorithm; + const EVP_MD *hmac_algorithm; + + hmac_algorithm = EVP_get_digestbyname(digest); + if (!hmac_algorithm) { + log_msg(LOG_ERR, "%s digest not available", digest); + return 0; + } + + algorithm = (tsig_algorithm_type *) region_alloc( + region, sizeof(tsig_algorithm_type)); + algorithm->short_name = name; + algorithm->wireformat_name + = dname_parse(region, wireformat); + if (!algorithm->wireformat_name) { + log_msg(LOG_ERR, "cannot parse %s algorithm", wireformat); + return 0; + } + algorithm->maximum_digest_size = EVP_MAX_MD_SIZE; + algorithm->data = hmac_algorithm; + algorithm->hmac_create_context = create_context; + algorithm->hmac_init_context = init_context; + algorithm->hmac_update = update; + algorithm->hmac_final = final; + tsig_add_algorithm(algorithm); + + return 1; +} + +int +tsig_openssl_init(region_type *region) +{ + OpenSSL_add_all_digests(); + + /* TODO: walk lookup supported algorithms table */ + if (!tsig_openssl_init_algorithm(region, "md5", "hmac-md5","hmac-md5.sig-alg.reg.int.")) + return 0; +#ifdef HAVE_EVP_SHA1 + if (!tsig_openssl_init_algorithm(region, "sha1", "hmac-sha1", "hmac-sha1.")) + return 0; +#endif /* HAVE_EVP_SHA1 */ + +#ifdef HAVE_EVP_SHA256 + if (!tsig_openssl_init_algorithm(region, "sha256", "hmac-sha256", "hmac-sha256.")) + return 0; +#endif /* HAVE_EVP_SHA256 */ + return 1; +} + +static void +cleanup_context(void *data) +{ + HMAC_CTX *context = (HMAC_CTX *) data; + HMAC_CTX_cleanup(context); +} + +static void * +create_context(region_type *region) +{ + HMAC_CTX *context + = (HMAC_CTX *) region_alloc(region, sizeof(HMAC_CTX)); + region_add_cleanup(region, cleanup_context, context); + HMAC_CTX_init(context); + return context; +} + +static void +init_context(void *context, + tsig_algorithm_type *algorithm, + tsig_key_type *key) +{ + HMAC_CTX *ctx = (HMAC_CTX *) context; + const EVP_MD *md = (const EVP_MD *) algorithm->data; + HMAC_Init_ex(ctx, key->data, key->size, md, NULL); +} + +static void +update(void *context, const void *data, size_t size) +{ + HMAC_CTX *ctx = (HMAC_CTX *) context; + HMAC_Update(ctx, (unsigned char *) data, (int) size); +} + +static void +final(void *context, uint8_t *digest, size_t *size) +{ + HMAC_CTX *ctx = (HMAC_CTX *) context; + unsigned len = (unsigned) *size; + HMAC_Final(ctx, digest, &len); + *size = (size_t) len; +} + +void +tsig_openssl_finalize() +{ + EVP_cleanup(); +} + +#endif /* defined(TSIG) && defined(HAVE_SSL) */ diff --git a/usr.sbin/nsd/tsig-openssl.h b/usr.sbin/nsd/tsig-openssl.h new file mode 100644 index 00000000000..30345b6596f --- /dev/null +++ b/usr.sbin/nsd/tsig-openssl.h @@ -0,0 +1,29 @@ +/* + * tsig-openssl.h -- Interface to OpenSSL for TSIG support. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _TSIG_OPENSSL_H_ +#define _TSIG_OPENSSL_H_ + +#if defined(TSIG) && defined(HAVE_SSL) + +#include "region-allocator.h" + +#include <openssl/hmac.h> +#include <openssl/sha.h> + +/* + * Initialize OpenSSL support for TSIG. + */ +int tsig_openssl_init(region_type *region); + +void tsig_openssl_finalize(); + +#endif /* defined(TSIG) && defined(HAVE_SSL) */ + +#endif /* _TSIG_H_ */ diff --git a/usr.sbin/nsd/tsig.c b/usr.sbin/nsd/tsig.c new file mode 100644 index 00000000000..8b4f3249470 --- /dev/null +++ b/usr.sbin/nsd/tsig.c @@ -0,0 +1,674 @@ +/* + * tsig.h -- TSIG definitions (RFC 2845). + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + + +#include <config.h> +#include <stdlib.h> +#include <ctype.h> + +#include "tsig.h" +#include "tsig-openssl.h" +#include "dns.h" +#include "packet.h" +#include "query.h" + +static region_type *tsig_region; + +struct tsig_key_table +{ + struct tsig_key_table *next; + tsig_key_type *key; +}; +typedef struct tsig_key_table tsig_key_table_type; +static tsig_key_table_type *tsig_key_table; + +struct tsig_algorithm_table +{ + struct tsig_algorithm_table *next; + tsig_algorithm_type *algorithm; +}; +typedef struct tsig_algorithm_table tsig_algorithm_table_type; +static tsig_algorithm_table_type *tsig_algorithm_table; +static size_t max_algo_digest_size = 0; + +tsig_lookup_algorithm_table tsig_supported_algorithms[] = { + { TSIG_HMAC_MD5, "hmac-md5" }, +#ifdef HAVE_EVP_SHA1 + { TSIG_HMAC_SHA1, "hmac-sha1" }, +#endif /* HAVE_EVP_SHA1 */ + +#ifdef HAVE_EVP_SHA256 + { TSIG_HMAC_SHA256, "hmac-sha256" }, +#endif /* HAVE_EVP_SHA256 */ + { 0, NULL } +}; + +static void +tsig_digest_variables(tsig_record_type *tsig, int tsig_timers_only) +{ + uint16_t klass = htons(CLASS_ANY); + uint32_t ttl = htonl(0); + uint16_t signed_time_high = htons(tsig->signed_time_high); + uint32_t signed_time_low = htonl(tsig->signed_time_low); + uint16_t signed_time_fudge = htons(tsig->signed_time_fudge); + uint16_t error_code = htons(tsig->error_code); + uint16_t other_size = htons(tsig->other_size); + + if (!tsig_timers_only) { + tsig->algorithm->hmac_update(tsig->context, + dname_name(tsig->key_name), + tsig->key_name->name_size); + tsig->algorithm->hmac_update(tsig->context, + &klass, + sizeof(klass)); + tsig->algorithm->hmac_update(tsig->context, + &ttl, + sizeof(ttl)); + tsig->algorithm->hmac_update(tsig->context, + dname_name(tsig->algorithm_name), + tsig->algorithm_name->name_size); + } + tsig->algorithm->hmac_update(tsig->context, + &signed_time_high, + sizeof(signed_time_high)); + tsig->algorithm->hmac_update(tsig->context, + &signed_time_low, + sizeof(signed_time_low)); + tsig->algorithm->hmac_update(tsig->context, + &signed_time_fudge, + sizeof(signed_time_fudge)); + if (!tsig_timers_only) { + tsig->algorithm->hmac_update(tsig->context, + &error_code, + sizeof(error_code)); + tsig->algorithm->hmac_update(tsig->context, + &other_size, + sizeof(other_size)); + tsig->algorithm->hmac_update(tsig->context, + tsig->other_data, + tsig->other_size); + } +} + +int +tsig_init(region_type *region) +{ + tsig_region = region; + tsig_key_table = NULL; + tsig_algorithm_table = NULL; + +#if defined(TSIG) && defined(HAVE_SSL) + return tsig_openssl_init(region); +#endif + return 1; +} + +void +tsig_add_key(tsig_key_type *key) +{ + tsig_key_table_type *entry = (tsig_key_table_type *) region_alloc( + tsig_region, sizeof(tsig_key_table_type)); + entry->key = key; + entry->next = tsig_key_table; + tsig_key_table = entry; +} + +void +tsig_add_algorithm(tsig_algorithm_type *algorithm) +{ + tsig_algorithm_table_type *entry + = (tsig_algorithm_table_type *) region_alloc( + tsig_region, sizeof(tsig_algorithm_table_type)); + entry->algorithm = algorithm; + entry->next = tsig_algorithm_table; + tsig_algorithm_table = entry; + if(algorithm->maximum_digest_size > max_algo_digest_size) + max_algo_digest_size = algorithm->maximum_digest_size; +} + +/** + * compare a tsig algorithm string lowercased + */ +int +tsig_strlowercmp(const char* str1, const char* str2) +{ + while (str1 && str2 && *str1 != '\0' && *str2 != '\0') { + if(tolower((int)*str1) != tolower((int)*str2)) { + if(tolower((int)*str1) < tolower((int)*str2)) + return -1; + return 1; + } + str1++; + str2++; + } + if (str1 && str2) { + if (*str1 == *str2) + return 0; + else if (*str1 == '\0') + return -1; + } + else if (!str1 && !str2) + return 0; + else if (!str1 && str2) + return -1; + return 1; +} + + +/* + * Find an HMAC algorithm based on its short name. + */ +tsig_algorithm_type * +tsig_get_algorithm_by_name(const char *name) +{ + tsig_algorithm_table_type *algorithm_entry; + + for (algorithm_entry = tsig_algorithm_table; + algorithm_entry; + algorithm_entry = algorithm_entry->next) + { + if (tsig_strlowercmp(name, algorithm_entry->algorithm->short_name) == 0) + { + return algorithm_entry->algorithm; + } + } + + return NULL; +} + +/* + * Find an HMAC algorithm based on its id. + */ +tsig_algorithm_type * +tsig_get_algorithm_by_id(uint8_t alg) +{ + int i=0; + for (/*empty*/; tsig_supported_algorithms[i].id > 0; i++) { + if (tsig_supported_algorithms[i].id == alg) + return tsig_get_algorithm_by_name(tsig_supported_algorithms[i].short_name); + } + return NULL; +} + +const char * +tsig_error(int error_code) +{ + static char message[1000]; + + switch (error_code) { + case TSIG_ERROR_NOERROR: + return "No Error"; + break; + case TSIG_ERROR_BADSIG: + return "Bad Signature"; + break; + case TSIG_ERROR_BADKEY: + return "Bad Key"; + break; + case TSIG_ERROR_BADTIME: + return "Bad Time"; + break; + default: + if(error_code < 16) /* DNS rcodes */ + return rcode2str(error_code); + + snprintf(message, sizeof(message), + "Unknown Error %d", error_code); + break; + } + return message; +} + +static void +tsig_cleanup(void *data) +{ + tsig_record_type *tsig = (tsig_record_type *) data; + region_destroy(tsig->rr_region); + region_destroy(tsig->context_region); +} + +void +tsig_create_record(tsig_record_type *tsig, region_type *region) +{ + tsig_create_record_custom(tsig, region, DEFAULT_CHUNK_SIZE, + DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE); +} + +void +tsig_create_record_custom(tsig_record_type *tsig, region_type *region, + size_t chunk_size, size_t large_object_size, size_t initial_cleanup_size) +{ + tsig->rr_region = region_create_custom(xalloc, free, chunk_size, + large_object_size, initial_cleanup_size, 0); + tsig->context_region = region_create_custom(xalloc, free, chunk_size, + large_object_size, initial_cleanup_size, 0); + region_add_cleanup(region, tsig_cleanup, tsig); + tsig_init_record(tsig, NULL, NULL); +} + +void +tsig_init_record(tsig_record_type *tsig, + tsig_algorithm_type *algorithm, + tsig_key_type *key) +{ + tsig->status = TSIG_NOT_PRESENT; + tsig->error_code = TSIG_ERROR_NOERROR; + tsig->position = 0; + tsig->response_count = 0; + tsig->context = NULL; + tsig->algorithm = algorithm; + tsig->key = key; + tsig->prior_mac_size = 0; + tsig->prior_mac_data = NULL; + region_free_all(tsig->context_region); +} + +int +tsig_from_query(tsig_record_type *tsig) +{ + tsig_key_table_type *key_entry; + tsig_key_type *key = NULL; + tsig_algorithm_table_type *algorithm_entry; + tsig_algorithm_type *algorithm = NULL; + uint64_t current_time; + uint64_t signed_time; + + assert(tsig->status == TSIG_OK); + assert(!tsig->algorithm); + assert(!tsig->key); + + /* XXX: TODO: slow linear check for keyname */ + for (key_entry = tsig_key_table; + key_entry; + key_entry = key_entry->next) + { + if (dname_compare(tsig->key_name, key_entry->key->name) == 0) { + key = key_entry->key; + break; + } + } + + for (algorithm_entry = tsig_algorithm_table; + algorithm_entry; + algorithm_entry = algorithm_entry->next) + { + if (dname_compare( + tsig->algorithm_name, + algorithm_entry->algorithm->wireformat_name) == 0) + { + algorithm = algorithm_entry->algorithm; + break; + } + } + + if (!algorithm || !key) { + /* Algorithm or key is unknown, cannot authenticate. */ + tsig->error_code = TSIG_ERROR_BADKEY; + return 0; + } + + if ((tsig->algorithm && algorithm != tsig->algorithm) + || (tsig->key && key != tsig->key)) + { + /* + * Algorithm or key changed during a single connection, + * return error. + */ + tsig->error_code = TSIG_ERROR_BADKEY; + return 0; + } + + signed_time = ((((uint64_t) tsig->signed_time_high) << 32) | + ((uint64_t) tsig->signed_time_low)); + + current_time = (uint64_t) time(NULL); + if ((current_time < signed_time - tsig->signed_time_fudge) + || (current_time > signed_time + tsig->signed_time_fudge)) + { + uint16_t current_time_high; + uint32_t current_time_low; + +#if 0 /* debug */ + char current_time_text[26]; + char signed_time_text[26]; + time_t clock; + + clock = (time_t) current_time; + ctime_r(&clock, current_time_text); + current_time_text[24] = '\0'; + + clock = (time_t) signed_time; + ctime_r(&clock, signed_time_text); + signed_time_text[24] = '\0'; + + log_msg(LOG_ERR, + "current server time %s is outside the range of TSIG" + " signed time %s with fudge %u", + current_time_text, + signed_time_text, + (unsigned) tsig->signed_time_fudge); +#endif + + tsig->error_code = TSIG_ERROR_BADTIME; + current_time_high = (uint16_t) (current_time >> 32); + current_time_low = (uint32_t) current_time; + tsig->other_size = 6; + tsig->other_data = (uint8_t *) region_alloc( + tsig->rr_region, sizeof(uint16_t) + sizeof(uint32_t)); + write_uint16(tsig->other_data, current_time_high); + write_uint32(tsig->other_data + 2, current_time_low); + return 0; + } + + tsig->algorithm = algorithm; + tsig->key = key; + tsig->response_count = 0; + tsig->prior_mac_size = 0; + + return 1; +} + +void +tsig_init_query(tsig_record_type *tsig, uint16_t original_query_id) +{ + assert(tsig); + assert(tsig->algorithm); + assert(tsig->key); + + tsig->response_count = 0; + tsig->prior_mac_size = 0; + tsig->algorithm_name = tsig->algorithm->wireformat_name; + tsig->key_name = tsig->key->name; + tsig->mac_size = 0; + tsig->mac_data = NULL; + tsig->original_query_id = original_query_id; + tsig->error_code = TSIG_ERROR_NOERROR; + tsig->other_size = 0; + tsig->other_data = NULL; +} + +void +tsig_prepare(tsig_record_type *tsig) +{ + if (!tsig->context) { + assert(tsig->algorithm); + tsig->context = tsig->algorithm->hmac_create_context( + tsig->context_region); + tsig->prior_mac_data = (uint8_t *) region_alloc( + tsig->context_region, + tsig->algorithm->maximum_digest_size); + } + tsig->algorithm->hmac_init_context(tsig->context, + tsig->algorithm, + tsig->key); + + if (tsig->prior_mac_size > 0) { + uint16_t mac_size = htons(tsig->prior_mac_size); + tsig->algorithm->hmac_update(tsig->context, + &mac_size, + sizeof(mac_size)); + tsig->algorithm->hmac_update(tsig->context, + tsig->prior_mac_data, + tsig->prior_mac_size); + } + + tsig->updates_since_last_prepare = 0; +} + +void +tsig_update(tsig_record_type *tsig, buffer_type *packet, size_t length) +{ + uint16_t original_query_id = htons(tsig->original_query_id); + + assert(length <= buffer_limit(packet)); + + tsig->algorithm->hmac_update(tsig->context, + &original_query_id, + sizeof(original_query_id)); + tsig->algorithm->hmac_update( + tsig->context, + buffer_at(packet, sizeof(original_query_id)), + length - sizeof(original_query_id)); + if (QR(packet)) { + ++tsig->response_count; + } + + ++tsig->updates_since_last_prepare; +} + +void +tsig_sign(tsig_record_type *tsig) +{ + uint64_t current_time = (uint64_t) time(NULL); + tsig->signed_time_high = (uint16_t) (current_time >> 32); + tsig->signed_time_low = (uint32_t) current_time; + tsig->signed_time_fudge = 300; /* XXX; hardcoded value */ + + tsig_digest_variables(tsig, tsig->response_count > 1); + + tsig->algorithm->hmac_final(tsig->context, + tsig->prior_mac_data, + &tsig->prior_mac_size); + + tsig->mac_size = tsig->prior_mac_size; + tsig->mac_data = tsig->prior_mac_data; +} + +int +tsig_verify(tsig_record_type *tsig) +{ + tsig_digest_variables(tsig, tsig->response_count > 1); + + tsig->algorithm->hmac_final(tsig->context, + tsig->prior_mac_data, + &tsig->prior_mac_size); + + if (tsig->mac_size != tsig->prior_mac_size + || memcmp(tsig->mac_data, + tsig->prior_mac_data, + tsig->mac_size) != 0) + { + /* Digest is incorrect, cannot authenticate. */ + tsig->error_code = TSIG_ERROR_BADSIG; + return 0; + } else { + return 1; + } +} + +int +tsig_find_rr(tsig_record_type *tsig, buffer_type *packet) +{ + size_t saved_position = buffer_position(packet); + size_t rrcount = (QDCOUNT(packet) + + ANCOUNT(packet) + + NSCOUNT(packet) + + ARCOUNT(packet)); + size_t i; + int result; + + if (ARCOUNT(packet) == 0) { + tsig->status = TSIG_NOT_PRESENT; + return 1; + } + + buffer_set_position(packet, QHEADERSZ); + + /* TSIG must be the last record, so skip all others. */ + for (i = 0; i < rrcount - 1; ++i) { + if (!packet_skip_rr(packet, i < QDCOUNT(packet))) { + buffer_set_position(packet, saved_position); + return 0; + } + } + + result = tsig_parse_rr(tsig, packet); + buffer_set_position(packet, saved_position); + return result; +} + +int +tsig_parse_rr(tsig_record_type *tsig, buffer_type *packet) +{ + uint16_t type; + uint16_t klass; + uint32_t ttl; + uint16_t rdlen; + + tsig->status = TSIG_NOT_PRESENT; + tsig->position = buffer_position(packet); + tsig->key_name = NULL; + tsig->algorithm_name = NULL; + tsig->mac_data = NULL; + tsig->other_data = NULL; + region_free_all(tsig->rr_region); + + tsig->key_name = dname_make_from_packet(tsig->rr_region, packet, 1, 1); + if (!tsig->key_name) { + buffer_set_position(packet, tsig->position); + return 0; + } + + if (!buffer_available(packet, 10)) { + buffer_set_position(packet, tsig->position); + return 0; + } + + type = buffer_read_u16(packet); + klass = buffer_read_u16(packet); + + /* TSIG not present */ + if (type != TYPE_TSIG || klass != CLASS_ANY) { + buffer_set_position(packet, tsig->position); + return 1; + } + + ttl = buffer_read_u32(packet); + rdlen = buffer_read_u16(packet); + + tsig->status = TSIG_ERROR; + tsig->error_code = RCODE_FORMAT; + if (ttl != 0 || !buffer_available(packet, rdlen)) { + buffer_set_position(packet, tsig->position); + return 0; + } + + tsig->algorithm_name = dname_make_from_packet( + tsig->rr_region, packet, 1, 1); + if (!tsig->algorithm_name || !buffer_available(packet, 10)) { + buffer_set_position(packet, tsig->position); + return 0; + } + + tsig->signed_time_high = buffer_read_u16(packet); + tsig->signed_time_low = buffer_read_u32(packet); + tsig->signed_time_fudge = buffer_read_u16(packet); + tsig->mac_size = buffer_read_u16(packet); + if (!buffer_available(packet, tsig->mac_size)) { + buffer_set_position(packet, tsig->position); + tsig->mac_size = 0; + return 0; + } + tsig->mac_data = (uint8_t *) region_alloc_init( + tsig->rr_region, buffer_current(packet), tsig->mac_size); + buffer_skip(packet, tsig->mac_size); + if (!buffer_available(packet, 6)) { + buffer_set_position(packet, tsig->position); + return 0; + } + tsig->original_query_id = buffer_read_u16(packet); + tsig->error_code = buffer_read_u16(packet); + tsig->other_size = buffer_read_u16(packet); + if (!buffer_available(packet, tsig->other_size) || tsig->other_size > 16) { + tsig->other_size = 0; + buffer_set_position(packet, tsig->position); + return 0; + } + tsig->other_data = (uint8_t *) region_alloc_init( + tsig->rr_region, buffer_current(packet), tsig->other_size); + buffer_skip(packet, tsig->other_size); + tsig->status = TSIG_OK; + tsig->error_code = TSIG_ERROR_NOERROR; + + return 1; +} + +void +tsig_append_rr(tsig_record_type *tsig, buffer_type *packet) +{ + size_t rdlength_pos; + + /* XXX: TODO key name compression? */ + if(tsig->key_name) + buffer_write(packet, dname_name(tsig->key_name), + tsig->key_name->name_size); + else buffer_write_u8(packet, 0); + buffer_write_u16(packet, TYPE_TSIG); + buffer_write_u16(packet, CLASS_ANY); + buffer_write_u32(packet, 0); /* TTL */ + rdlength_pos = buffer_position(packet); + buffer_skip(packet, sizeof(uint16_t)); + if(tsig->algorithm_name) + buffer_write(packet, dname_name(tsig->algorithm_name), + tsig->algorithm_name->name_size); + else buffer_write_u8(packet, 0); + buffer_write_u16(packet, tsig->signed_time_high); + buffer_write_u32(packet, tsig->signed_time_low); + buffer_write_u16(packet, tsig->signed_time_fudge); + buffer_write_u16(packet, tsig->mac_size); + buffer_write(packet, tsig->mac_data, tsig->mac_size); + buffer_write_u16(packet, tsig->original_query_id); + buffer_write_u16(packet, tsig->error_code); + buffer_write_u16(packet, tsig->other_size); + buffer_write(packet, tsig->other_data, tsig->other_size); + + buffer_write_u16_at(packet, rdlength_pos, + buffer_position(packet) - rdlength_pos + - sizeof(uint16_t)); +} + +size_t +tsig_reserved_space(tsig_record_type *tsig) +{ + if (tsig->status == TSIG_NOT_PRESENT) + return 0; + + return ( + (tsig->key_name?tsig->key_name->name_size:1) /* Owner */ + + sizeof(uint16_t) /* Type */ + + sizeof(uint16_t) /* Class */ + + sizeof(uint32_t) /* TTL */ + + sizeof(uint16_t) /* RDATA length */ + + (tsig->algorithm_name?tsig->algorithm_name->name_size:1) + + sizeof(uint16_t) /* Signed time (high) */ + + sizeof(uint32_t) /* Signed time (low) */ + + sizeof(uint16_t) /* Signed time fudge */ + + sizeof(uint16_t) /* MAC size */ + + max_algo_digest_size /* MAC data */ + + sizeof(uint16_t) /* Original query ID */ + + sizeof(uint16_t) /* Error code */ + + sizeof(uint16_t) /* Other size */ + + tsig->other_size); /* Other data */ +} + +void +tsig_error_reply(tsig_record_type *tsig) +{ + if(tsig->mac_data) + memset(tsig->mac_data, 0, tsig->mac_size); + tsig->mac_size = 0; +} + +void +tsig_finalize() +{ +#if defined(TSIG) && defined(HAVE_SSL) + tsig_openssl_finalize(); +#endif +} diff --git a/usr.sbin/nsd/tsig.h b/usr.sbin/nsd/tsig.h new file mode 100644 index 00000000000..7af8dfe5886 --- /dev/null +++ b/usr.sbin/nsd/tsig.h @@ -0,0 +1,293 @@ +/* + * tsig.h -- TSIG definitions (RFC 2845). + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _TSIG_H_ +#define _TSIG_H_ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +#include "buffer.h" +#include "dname.h" + +#define TSIG_ERROR_NOERROR 0 +#define TSIG_ERROR_BADSIG 16 +#define TSIG_ERROR_BADKEY 17 +#define TSIG_ERROR_BADTIME 18 + +#define TSIG_HMAC_MD5 157 +#define TSIG_HMAC_SHA1 158 +#define TSIG_HMAC_SHA256 159 + +typedef struct tsig_algorithm tsig_algorithm_type; +typedef struct tsig_key tsig_key_type; +typedef struct tsig_record tsig_record_type; + +enum tsig_status +{ + TSIG_NOT_PRESENT, + TSIG_OK, + TSIG_ERROR +}; +typedef enum tsig_status tsig_status_type; + +struct tsig_lookup_struct_table +{ + uint8_t id; + const char* short_name; +}; +typedef struct tsig_lookup_struct_table tsig_lookup_algorithm_table; + +/* + * A TSIG HMAC algorithm, such as hmac-md5. + */ +struct tsig_algorithm +{ + /* + * Short name of the algorithm, such as "hmac-md5". + */ + const char *short_name; + + /* + * Full wireformat name of the algorithm, such as + * "hmac-md5.sig-alg.reg.int." + */ + const dname_type *wireformat_name; + + /* + * The maximum size of a digest generated by this algorithm. + */ + size_t maximum_digest_size; + + /* + * Algorithm implementation specific data. + */ + const void *data; + + /* + * Create a new HMAC context. + */ + void *(*hmac_create_context)(region_type *region); + + /* + * Initialize an HMAC context with the specified algorithm and + * key. + */ + void (*hmac_init_context)(void *context, + tsig_algorithm_type *algorithm, + tsig_key_type *key); + + /* + * Update the HMAC context with the specified data. + */ + void (*hmac_update)(void *context, const void *data, size_t size); + + /* + * Generate the final digest. DIGEST points to a buffer of at + * least maximum_digest_size bytes. + */ + void (*hmac_final)(void *context, uint8_t *digest, size_t *size); +}; + +/* + * A TSIG key used to sign and verify packets. + */ +struct tsig_key +{ + const dname_type *name; + size_t size; + const uint8_t *data; +}; + +struct tsig_record +{ + tsig_status_type status; + size_t position; + size_t response_count; + size_t updates_since_last_prepare; + void *context; + tsig_algorithm_type *algorithm; + tsig_key_type *key; + size_t prior_mac_size; + uint8_t *prior_mac_data; + + /* TSIG RR data is allocated in the rr_region. */ + region_type *rr_region; + region_type *context_region; + const dname_type *key_name; + const dname_type *algorithm_name; + uint16_t signed_time_high; + uint32_t signed_time_low; + uint16_t signed_time_fudge; + uint16_t mac_size; + uint8_t *mac_data; + uint16_t original_query_id; + uint16_t error_code; + uint16_t other_size; + uint8_t *other_data; +}; + +/* + * Initialize the TSIG module (including TSIG implementation modules + * such as tsig-openssl). + */ +int tsig_init(region_type *region); + +/* + * Add the specified key to the TSIG key table. + */ +void tsig_add_key(tsig_key_type *key); + +/* + * Add the specified algorithm to the TSIG algorithm table. + */ +void tsig_add_algorithm(tsig_algorithm_type *algorithm); + +/* + * Find an HMAC algorithm based on its short name. + */ +tsig_algorithm_type *tsig_get_algorithm_by_name(const char *name); + +/* + * Find an HMAC algorithm based on its identifier. + */ +tsig_algorithm_type *tsig_get_algorithm_by_id(uint8_t alg); + +/* + * Return a descriptive error message based on the TSIG error code. + */ +const char *tsig_error(int error_code); + +/* + * Create the tsig record internal structure. Allocs it. + * Call init_record afterwards before doing more with it. + * + * The region is used to attach a cleanup function that destroys the tsig. + */ +void tsig_create_record(tsig_record_type* tsig, + region_type* region); + +/* + * Like tsig_create_record, with custom region settings. + * The size params are used to customise the rr_region and context_region. + */ +void tsig_create_record_custom(tsig_record_type* tsig, + region_type* region, + size_t chunk_size, + size_t large_object_size, + size_t initial_cleanup_size); + +/* + * Call this before starting to analyze or signing a sequence of + * packets. + * + * ALGORITHM and KEY are optional and are only needed if you want to + * sign the initial query. Otherwise the key and algorithm are looked + * up in the algorithm and key table when a received TSIG RR is + * processed. + */ +void tsig_init_record(tsig_record_type *data, + tsig_algorithm_type *algorithm, + tsig_key_type *key); + +/* + * Validate the TSIG RR key and algorithm from the TSIG RR. Otherwise + * update the TSIG error code. The MAC itself is not validated. + * + * Returns non-zero if the key and algorithm could be validated. + */ +int tsig_from_query(tsig_record_type *tsig); + +/* + * Prepare TSIG for signing of a query. This initializes TSIG with + * the algorithm and key stored in the TSIG record. + */ +void tsig_init_query(tsig_record_type *tsig, uint16_t original_query_id); + +/* + * Prepare TSIG for performing an HMAC calculation. If the TSIG + * contains a prior HMAC it is inserted first into the hash + * calculation. + */ +void tsig_prepare(tsig_record_type *tsig); + +/* + * Add the first LENGTH octets of PACKET to the TSIG hash, replacing + * the PACKET's id with the original query id from TSIG. If the query + * is a response the TSIG response count is incremented. + */ +void tsig_update(tsig_record_type *tsig, buffer_type *packet, size_t length); + +/* + * Finalize the TSIG record by hashing the TSIG data. If the TSIG + * response count is greater than 1 only the timers are hashed. + * Signed time is set to the current time. The TSIG record can be + * added to a packet using tsig_append_rr(). + * + * The calculated MAC is also stored as the prior MAC, so it can be + * used as a running MAC. + */ +void tsig_sign(tsig_record_type *tsig); + +/* + * Verify the calculated MAC against the MAC in the TSIG RR. + * + * The calculated MAC is also stored as the prior MAC, so it can be + * used as a running MAC. + */ +int tsig_verify(tsig_record_type *tsig); + +/* + * Find the TSIG RR in QUERY and parse it if present. Store the + * parsed results in TSIG. + * + * Returns non-zero if no parsing error occurred, use the tsig->status + * field to find out if the TSIG record was present. + */ +int tsig_find_rr(tsig_record_type *tsig, buffer_type *packet); + +/* + * Call this to analyze the TSIG RR starting at the current location + * of PACKET. On success true is returned and the results are stored + * in TSIG. + * + * Returns non-zero if no parsing error occurred, use the tsig->status + * field to find out if the TSIG record was present. + */ +int tsig_parse_rr(tsig_record_type *tsig, buffer_type *packet); + +/* + * Append the TSIG record to the response PACKET. + */ +void tsig_append_rr(tsig_record_type *tsig, buffer_type *packet); + +/* + * The amount of space to reserve in the response for the TSIG data + * (if required). + */ +size_t tsig_reserved_space(tsig_record_type *tsig); + +/* + * status or error_code must already be in error. + * prepares content for error packet. + */ +void tsig_error_reply(tsig_record_type *tsig); + +/* + * compare tsig algorithm names case insensitive. + */ +int tsig_strlowercmp(const char* str1, const char* str2); + +/* + * cleanup tsig openssl stuff. + */ +void tsig_finalize(void); + +#endif /* _TSIG_H_ */ diff --git a/usr.sbin/nsd/util.c b/usr.sbin/nsd/util.c new file mode 100644 index 00000000000..dbdf64308e7 --- /dev/null +++ b/usr.sbin/nsd/util.c @@ -0,0 +1,960 @@ +/* + * util.c -- set of various support routines. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif /* HAVE_SYSLOG_H */ +#include <unistd.h> + +#include "util.h" +#include "region-allocator.h" +#include "dname.h" +#include "namedb.h" +#include "rdata.h" + +#ifndef NDEBUG +unsigned nsd_debug_facilities = 0xffff; +int nsd_debug_level = 0; +#endif + +int verbosity = 0; + +static const char *global_ident = NULL; +static log_function_type *current_log_function = log_file; +static FILE *current_log_file = NULL; + +void +log_init(const char *ident) +{ + global_ident = ident; + current_log_file = stderr; +} + +void +log_open(int option, int facility, const char *filename) +{ +#ifdef HAVE_SYSLOG_H + openlog(global_ident, option, facility); +#endif /* HAVE_SYSLOG_H */ + if (filename) { + FILE *file = fopen(filename, "a"); + if (!file) { + log_msg(LOG_ERR, "Cannot open %s for appending (%s), " + "logging to stderr", + filename, strerror(errno)); + } else { + current_log_file = file; + } + } +} + +void +log_reopen(const char *filename, uint8_t verbose) +{ + if (filename) { + FILE *file = fopen(filename, "a"); + if (!file) { + if (verbose) + VERBOSITY(2, (LOG_WARNING, + "Cannot reopen %s for appending (%s), " + "keeping old logfile", + filename, strerror(errno))); + } else { + if (current_log_file && current_log_file != stderr) + fclose(current_log_file); + current_log_file = file; + } + } +} + +void +log_finalize(void) +{ +#ifdef HAVE_SYSLOG_H + closelog(); +#endif /* HAVE_SYSLOG_H */ + if (current_log_file && current_log_file != stderr) { + fclose(current_log_file); + } + current_log_file = NULL; +} + +static lookup_table_type log_priority_table[] = { + { LOG_ERR, "error" }, + { LOG_WARNING, "warning" }, + { LOG_NOTICE, "notice" }, + { LOG_INFO, "info" }, + { 0, NULL } +}; + +void +log_file(int priority, const char *message) +{ + size_t length; + lookup_table_type *priority_info; + const char *priority_text = "unknown"; + + assert(global_ident); + assert(current_log_file); + + priority_info = lookup_by_id(log_priority_table, priority); + if (priority_info) { + priority_text = priority_info->name; + } + + /* Bug #104, add time_t timestamp */ + fprintf(current_log_file, "[%d] %s[%d]: %s: %s", + (int)time(NULL), global_ident, (int) getpid(), priority_text, message); + length = strlen(message); + if (length == 0 || message[length - 1] != '\n') { + fprintf(current_log_file, "\n"); + } + fflush(current_log_file); +} + +void +log_syslog(int priority, const char *message) +{ +#ifdef HAVE_SYSLOG_H + syslog(priority, "%s", message); +#endif /* !HAVE_SYSLOG_H */ + log_file(priority, message); +} + +void +log_set_log_function(log_function_type *log_function) +{ + current_log_function = log_function; +} + +void +log_msg(int priority, const char *format, ...) +{ + va_list args; + va_start(args, format); + log_vmsg(priority, format, args); + va_end(args); +} + +void +log_vmsg(int priority, const char *format, va_list args) +{ + char message[MAXSYSLOGMSGLEN]; + vsnprintf(message, sizeof(message), format, args); + current_log_function(priority, message); +} + +void +set_bit(uint8_t bits[], size_t index) +{ + /* + * The bits are counted from left to right, so bit #0 is the + * left most bit. + */ + bits[index / 8] |= (1 << (7 - index % 8)); +} + +void +clear_bit(uint8_t bits[], size_t index) +{ + /* + * The bits are counted from left to right, so bit #0 is the + * left most bit. + */ + bits[index / 8] &= ~(1 << (7 - index % 8)); +} + +int +get_bit(uint8_t bits[], size_t index) +{ + /* + * The bits are counted from left to right, so bit #0 is the + * left most bit. + */ + return bits[index / 8] & (1 << (7 - index % 8)); +} + +lookup_table_type * +lookup_by_name(lookup_table_type *table, const char *name) +{ + while (table->name != NULL) { + if (strcasecmp(name, table->name) == 0) + return table; + table++; + } + return NULL; +} + +lookup_table_type * +lookup_by_id(lookup_table_type *table, int id) +{ + while (table->name != NULL) { + if (table->id == id) + return table; + table++; + } + return NULL; +} + +void * +xalloc(size_t size) +{ + void *result = malloc(size); + + if (!result) { + log_msg(LOG_ERR, "malloc failed: %s", strerror(errno)); + exit(1); + } + return result; +} + +void * +xalloc_zero(size_t size) +{ + void *result = xalloc(size); + memset(result, 0, size); + return result; +} + +void * +xrealloc(void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + if (!ptr) { + log_msg(LOG_ERR, "realloc failed: %s", strerror(errno)); + exit(1); + } + return ptr; +} + +int +write_data(FILE *file, const void *data, size_t size) +{ + size_t result; + + if (size == 0) + return 1; + + result = fwrite(data, 1, size, file); + + if (result == 0) { + log_msg(LOG_ERR, "write failed: %s", strerror(errno)); + return 0; + } else if (result < size) { + log_msg(LOG_ERR, "short write (disk full?)"); + return 0; + } else { + return 1; + } +} + +int write_socket(int s, const void *buf, size_t size) +{ + const char* data = (const char*)buf; + size_t total_count = 0; + + while (total_count < size) { + ssize_t count + = write(s, data + total_count, size - total_count); + if (count == -1) { + if (errno != EAGAIN && errno != EINTR) { + return 0; + } else { + continue; + } + } + total_count += count; + } + return 1; +} + +int +timespec_compare(const struct timespec *left, + const struct timespec *right) +{ + /* Compare seconds. */ + if (left->tv_sec < right->tv_sec) { + return -1; + } else if (left->tv_sec > right->tv_sec) { + return 1; + } else { + /* Seconds are equal, compare nanoseconds. */ + if (left->tv_nsec < right->tv_nsec) { + return -1; + } else if (left->tv_nsec > right->tv_nsec) { + return 1; + } else { + return 0; + } + } +} + + +/* One second is 1e9 nanoseconds. */ +#define NANOSECONDS_PER_SECOND 1000000000L + +void +timespec_add(struct timespec *left, + const struct timespec *right) +{ + left->tv_sec += right->tv_sec; + left->tv_nsec += right->tv_nsec; + if (left->tv_nsec >= NANOSECONDS_PER_SECOND) { + /* Carry. */ + ++left->tv_sec; + left->tv_nsec -= NANOSECONDS_PER_SECOND; + } +} + +void +timespec_subtract(struct timespec *left, + const struct timespec *right) +{ + left->tv_sec -= right->tv_sec; + left->tv_nsec -= right->tv_nsec; + if (left->tv_nsec < 0L) { + /* Borrow. */ + --left->tv_sec; + left->tv_nsec += NANOSECONDS_PER_SECOND; + } +} + +uint32_t +strtoserial(const char* nptr, const char** endptr) +{ + uint32_t i = 0; + uint32_t serial = 0; + + for(*endptr = nptr; **endptr; (*endptr)++) { + switch (**endptr) { + case ' ': + case '\t': + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i *= 10; + i += (**endptr - '0'); + break; + default: + break; + } + } + serial += i; + return serial; +} + +uint32_t +strtottl(const char *nptr, const char **endptr) +{ + uint32_t i = 0; + uint32_t seconds = 0; + + for(*endptr = nptr; **endptr; (*endptr)++) { + switch (**endptr) { + case ' ': + case '\t': + break; + case 's': + case 'S': + seconds += i; + i = 0; + break; + case 'm': + case 'M': + seconds += i * 60; + i = 0; + break; + case 'h': + case 'H': + seconds += i * 60 * 60; + i = 0; + break; + case 'd': + case 'D': + seconds += i * 60 * 60 * 24; + i = 0; + break; + case 'w': + case 'W': + seconds += i * 60 * 60 * 24 * 7; + i = 0; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i *= 10; + i += (**endptr - '0'); + break; + default: + seconds += i; + return seconds; + } + } + seconds += i; + return seconds; +} + + +ssize_t +hex_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) +{ + static char hexdigits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + size_t i; + + if (targsize < srclength * 2 + 1) { + return -1; + } + + for (i = 0; i < srclength; ++i) { + *target++ = hexdigits[src[i] >> 4U]; + *target++ = hexdigits[src[i] & 0xfU]; + } + *target = '\0'; + return 2 * srclength; +} + +ssize_t +hex_pton(const char* src, uint8_t* target, size_t targsize) +{ + uint8_t *t = target; + if(strlen(src) % 2 != 0 || strlen(src)/2 > targsize) { + return -1; + } + while(*src) { + if(!isxdigit((int)src[0]) || !isxdigit((int)src[1])) + return -1; + *t++ = hexdigit_to_int(src[0]) * 16 + + hexdigit_to_int(src[1]) ; + src += 2; + } + return t-target; +} + +int +b32_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) +{ + static char b32[]="0123456789abcdefghijklmnopqrstuv"; + char buf[9]; + ssize_t len=0; + + while(srclength > 0) + { + int t; + memset(buf,'\0',sizeof buf); + + /* xxxxx000 00000000 00000000 00000000 00000000 */ + buf[0]=b32[src[0] >> 3]; + + /* 00000xxx xx000000 00000000 00000000 00000000 */ + t=(src[0]&7) << 2; + if(srclength > 1) + t+=src[1] >> 6; + buf[1]=b32[t]; + if(srclength == 1) + break; + + /* 00000000 00xxxxx0 00000000 00000000 00000000 */ + buf[2]=b32[(src[1] >> 1)&0x1f]; + + /* 00000000 0000000x xxxx0000 00000000 00000000 */ + t=(src[1]&1) << 4; + if(srclength > 2) + t+=src[2] >> 4; + buf[3]=b32[t]; + if(srclength == 2) + break; + + /* 00000000 00000000 0000xxxx x0000000 00000000 */ + t=(src[2]&0xf) << 1; + if(srclength > 3) + t+=src[3] >> 7; + buf[4]=b32[t]; + if(srclength == 3) + break; + + /* 00000000 00000000 00000000 0xxxxx00 00000000 */ + buf[5]=b32[(src[3] >> 2)&0x1f]; + + /* 00000000 00000000 00000000 000000xx xxx00000 */ + t=(src[3]&3) << 3; + if(srclength > 4) + t+=src[4] >> 5; + buf[6]=b32[t]; + if(srclength == 4) + break; + + /* 00000000 00000000 00000000 00000000 000xxxxx */ + buf[7]=b32[src[4]&0x1f]; + + if(targsize < 8) + return -1; + + src += 5; + srclength -= 5; + + memcpy(target,buf,8); + target += 8; + targsize -= 8; + len += 8; + } + if(srclength) + { + if(targsize < strlen(buf)+1) + return -1; + strcpy(target, buf); + len += strlen(buf); + } + else if(targsize < 1) + return -1; + else + *target='\0'; + return len; +} + +int +b32_pton(const char *src, uint8_t *target, size_t tsize) +{ + char ch; + size_t p=0; + + memset(target,'\0',tsize); + while((ch = *src++)) { + uint8_t d; + size_t b; + size_t n; + + if(p+5 >= tsize*8) + return -1; + + if(isspace(ch)) + continue; + + if(ch >= '0' && ch <= '9') + d=ch-'0'; + else if(ch >= 'A' && ch <= 'V') + d=ch-'A'+10; + else if(ch >= 'a' && ch <= 'v') + d=ch-'a'+10; + else + return -1; + + b=7-p%8; + n=p/8; + + if(b >= 4) + target[n]|=d << (b-4); + else { + target[n]|=d >> (4-b); + target[n+1]|=d << (b+4); + } + p+=5; + } + return (p+7)/8; +} + +void +strip_string(char *str) +{ + char *start = str; + char *end = str + strlen(str) - 1; + + while (isspace(*start)) + ++start; + if (start > end) { + /* Completely blank. */ + str[0] = '\0'; + } else { + while (isspace(*end)) + --end; + *++end = '\0'; + + if (str != start) + memmove(str, start, end - start + 1); + } +} + +int +hexdigit_to_int(char ch) +{ + switch (ch) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + default: + abort(); + } +} + +/* Number of days per month (except for February in leap years). */ +static const int mdays[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static int +is_leap_year(int year) +{ + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); +} + +static int +leap_days(int y1, int y2) +{ + --y1; + --y2; + return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400); +} + +/* + * Code adapted from Python 2.4.1 sources (Lib/calendar.py). + */ +time_t +mktime_from_utc(const struct tm *tm) +{ + int year = 1900 + tm->tm_year; + time_t days = 365 * (year - 1970) + leap_days(1970, year); + time_t hours; + time_t minutes; + time_t seconds; + int i; + + for (i = 0; i < tm->tm_mon; ++i) { + days += mdays[i]; + } + if (tm->tm_mon > 1 && is_leap_year(year)) { + ++days; + } + days += tm->tm_mday - 1; + + hours = days * 24 + tm->tm_hour; + minutes = hours * 60 + tm->tm_min; + seconds = minutes * 60 + tm->tm_sec; + + return seconds; +} + +/* code to calculate CRC. Lifted from BSD 4.4 crc.c in cksum(1). BSD license. + http://www.tsfr.org/~orc/Code/bsd/bsd-current/cksum/crc.c. + or http://gobsd.com/code/freebsd/usr.bin/cksum/crc.c + The polynomial is 0x04c11db7L. */ +static u_long crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] + +uint32_t compute_crc(uint32_t crc, uint8_t* data, size_t len) +{ + size_t i; + for(i=0; i<len; ++i) + COMPUTE(crc, data[i]); + return crc; +} + +int write_data_crc(FILE *file, const void *data, size_t size, uint32_t* crc) +{ + int ret = write_data(file, data, size); + *crc = compute_crc(*crc, (uint8_t*)data, size); + return ret; +} + +#define SERIAL_BITS 32 +int compare_serial(uint32_t a, uint32_t b) +{ + const uint32_t cutoff = ((uint32_t) 1 << (SERIAL_BITS - 1)); + + if (a == b) { + return 0; + } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) { + return -1; + } else { + return 1; + } +} + +void +cleanup_region(void *data) +{ + region_type *region = (region_type *) data; + region_destroy(region); +} + +struct state_pretty_rr* +create_pretty_rr(struct region* region) +{ + struct state_pretty_rr* state = (struct state_pretty_rr*) + region_alloc(region, sizeof(struct state_pretty_rr)); + state->previous_owner_region = region_create(xalloc, free); + state->previous_owner = NULL; + state->previous_owner_origin = NULL; + region_add_cleanup(region, cleanup_region, + state->previous_owner_region); + return state; +} + +static void +set_previous_owner(struct state_pretty_rr *state, const dname_type *dname) +{ + region_free_all(state->previous_owner_region); + state->previous_owner = dname_copy(state->previous_owner_region, dname); + state->previous_owner_origin = dname_origin( + state->previous_owner_region, state->previous_owner); +} + +int +print_rr(FILE *out, + struct state_pretty_rr *state, + rr_type *record) +{ + region_type *region = region_create(xalloc, free); + buffer_type *output = buffer_create(region, MAX_RDLENGTH); + rrtype_descriptor_type *descriptor + = rrtype_descriptor_by_type(record->type); + int result; + 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)); + } + + 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); + } + + if (result) { + buffer_printf(output, "\n"); + buffer_flip(output); + (void)write_data(out, buffer_current(output), buffer_remaining(output)); +/* fflush(out); */ + } + + region_destroy(region); + 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; + } + 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; +} + +int +addr2ip( +#ifdef INET6 + struct sockaddr_storage addr +#else + struct sockaddr_in addr +#endif +, char *address, socklen_t size) +{ +#ifdef 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); + } + + return (0); +} diff --git a/usr.sbin/nsd/util.h b/usr.sbin/nsd/util.h new file mode 100644 index 00000000000..37461ae8cb2 --- /dev/null +++ b/usr.sbin/nsd/util.h @@ -0,0 +1,368 @@ +/* + * util.h -- set of various support routines. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include <config.h> +#include <sys/time.h> +#include <stdarg.h> +#include <stdio.h> +#include <time.h> +struct rr; + +#ifdef HAVE_SYSLOG_H +# include <syslog.h> +#else +# define LOG_ERR 3 +# define LOG_WARNING 4 +# define LOG_NOTICE 5 +# define LOG_INFO 6 +#endif + +#define ALIGN_UP(n, alignment) \ + (((n) + (alignment) - 1) & (~((alignment) - 1))) +#define PADDING(n, alignment) \ + (ALIGN_UP((n), (alignment)) - (n)) + +/* + * Initialize the logging system. All messages are logged to stderr + * until log_open and log_set_log_function are called. + */ +void log_init(const char *ident); + +/* + * Open the system log. If FILENAME is not NULL, a log file is opened + * as well. + */ +void log_open(int option, int facility, const char *filename); + +/* + * Reopen the logfile. + */ +void log_reopen(const char *filename, uint8_t verbose); + +/* + * Finalize the logging system. + */ +void log_finalize(void); + +/* + * Type of function to use for the actual logging. + */ +typedef void log_function_type(int priority, const char *message); + +/* + * The function used to log to the log file. + */ +log_function_type log_file; + +/* + * The function used to log to syslog. The messages are also logged + * using log_file. + */ +log_function_type log_syslog; + +/* + * Set the logging function to use (log_file or log_syslog). + */ +void log_set_log_function(log_function_type *log_function); + +/* + * Log a message using the current log function. + */ +void log_msg(int priority, const char *format, ...) + ATTR_FORMAT(printf, 2, 3); + +/* + * Log a message using the current log function. + */ +void log_vmsg(int priority, const char *format, va_list args); + +/* + * Verbose output switch + */ +extern int verbosity; +#define VERBOSITY(level, args) \ + do { \ + if ((level) <= verbosity) { \ + log_msg args ; \ + } \ + } while (0) + +/* + * Set the INDEXth bit of BITS to 1. + */ +void set_bit(uint8_t bits[], size_t index); + +/* + * Set the INDEXth bit of BITS to 0. + */ +void clear_bit(uint8_t bits[], size_t index); + +/* + * Return the value of the INDEXth bit of BITS. + */ +int get_bit(uint8_t bits[], size_t index); + +/* A general purpose lookup table */ +typedef struct lookup_table lookup_table_type; +struct lookup_table { + int id; + const char *name; +}; + +/* + * Looks up the table entry by name, returns NULL if not found. + */ +lookup_table_type *lookup_by_name(lookup_table_type table[], const char *name); + +/* + * Looks up the table entry by id, returns NULL if not found. + */ +lookup_table_type *lookup_by_id(lookup_table_type table[], int id); + +/* + * (Re-)allocate SIZE bytes of memory. Report an error if the memory + * could not be allocated and exit the program. These functions never + * return NULL. + */ +void *xalloc(size_t size); +void *xalloc_zero(size_t size); +void *xrealloc(void *ptr, size_t size); + +/* + * Write SIZE bytes of DATA to FILE. Report an error on failure. + * + * Returns 0 on failure, 1 on success. + */ +int write_data(FILE *file, const void *data, size_t size); + +/* + * like write_data, but keeps track of crc + */ +int write_data_crc(FILE *file, const void *data, size_t size, uint32_t* crc); + +/* + * Write the complete buffer to the socket, irrespective of short + * writes or interrupts. This function blocks to write the data. + * Returns 0 on error, 1 on success. + */ +int write_socket(int s, const void *data, size_t size); + +/* + * Copy data allowing for unaligned accesses in network byte order + * (big endian). + */ +static inline void +write_uint16(void *dst, uint16_t data) +{ +#ifdef ALLOW_UNALIGNED_ACCESSES + * (uint16_t *) dst = htons(data); +#else + uint8_t *p = (uint8_t *) dst; + p[0] = (uint8_t) ((data >> 8) & 0xff); + p[1] = (uint8_t) (data & 0xff); +#endif +} + +static inline void +write_uint32(void *dst, uint32_t data) +{ +#ifdef ALLOW_UNALIGNED_ACCESSES + * (uint32_t *) dst = htonl(data); +#else + uint8_t *p = (uint8_t *) dst; + p[0] = (uint8_t) ((data >> 24) & 0xff); + p[1] = (uint8_t) ((data >> 16) & 0xff); + p[2] = (uint8_t) ((data >> 8) & 0xff); + p[3] = (uint8_t) (data & 0xff); +#endif +} + +/* + * Copy data allowing for unaligned accesses in network byte order + * (big endian). + */ +static inline uint16_t +read_uint16(const void *src) +{ +#ifdef ALLOW_UNALIGNED_ACCESSES + return ntohs(* (uint16_t *) src); +#else + uint8_t *p = (uint8_t *) src; + return (p[0] << 8) | p[1]; +#endif +} + +static inline uint32_t +read_uint32(const void *src) +{ +#ifdef ALLOW_UNALIGNED_ACCESSES + return ntohl(* (uint32_t *) src); +#else + uint8_t *p = (uint8_t *) src; + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +#endif +} + +/* + * Print debugging information using log_msg, + * set the logfile as /dev/stdout or /dev/stderr if you like. + * nsd -F 0xFFFF enables all debug facilities. + */ +#define DEBUG_PARSER 0x0001U +#define DEBUG_ZONEC 0x0002U +#define DEBUG_QUERY 0x0004U +#define DEBUG_DBACCESS 0x0008U +#define DEBUG_NAME_COMPRESSION 0x0010U +#define DEBUG_XFRD 0x0020U +#define DEBUG_IPC 0x0040U + +extern unsigned nsd_debug_facilities; +extern int nsd_debug_level; +#ifdef NDEBUG +#define DEBUG(facility, level, args) /* empty */ +#else +#define DEBUG(facility, level, args) \ + do { \ + if ((facility) & nsd_debug_facilities && \ + (level) <= nsd_debug_level) { \ + log_msg args ; \ + } \ + } while (0) +#endif + + +/* + * Timespec functions. + */ +int timespec_compare(const struct timespec *left, const struct timespec *right); +void timespec_add(struct timespec *left, const struct timespec *right); +void timespec_subtract(struct timespec *left, const struct timespec *right); + +static inline void +timeval_to_timespec(struct timespec *left, + const struct timeval *right) +{ + left->tv_sec = right->tv_sec; + left->tv_nsec = 1000 * right->tv_usec; +} + + +/* + * Converts a string representation of a period of time into + * a long integer of seconds or serial value. + * + * Set the endptr to the first illegal character. + * + * Interface is similar as strtol(3) + * + * Returns: + * LONG_MIN if underflow occurs + * LONG_MAX if overflow occurs. + * otherwise number of seconds + * + * XXX These functions do not check the range. + * + */ +uint32_t strtoserial(const char *nptr, const char **endptr); +uint32_t strtottl(const char *nptr, const char **endptr); + +/* + * Convert binary data to a string of hexadecimal characters. + */ +ssize_t hex_ntop(uint8_t const *src, size_t srclength, char *target, + size_t targsize); +ssize_t hex_pton(const char* src, uint8_t* target, size_t targsize); + +/* + * convert base32 data from and to string. Returns length. + * -1 on error. Use (byte count*8)%5==0. + */ +int b32_pton(char const *src, uint8_t *target, size_t targsize); +int b32_ntop(uint8_t const *src, size_t srclength, char *target, + size_t targsize); + +/* + * Strip trailing and leading whitespace from str. + */ +void strip_string(char *str); + +/* + * Convert a single (hexidecimal) digit to its integer value. + */ +int hexdigit_to_int(char ch); + +/* + * Convert TM to seconds since epoch (midnight, January 1st, 1970). + * Like timegm(3), which is not always available. + */ +time_t mktime_from_utc(const struct tm *tm); + +/* + * Add bytes to given crc. Returns new CRC sum. + * Start crc val with 0xffffffff on first call. XOR crc with + * 0xffffffff at the end again to get final POSIX 1003.2 checksum. + */ +uint32_t compute_crc(uint32_t crc, uint8_t* data, size_t len); + +/* + * Compares two 32-bit serial numbers as defined in RFC1982. Returns + * <0 if a < b, 0 if a == b, and >0 if a > b. The result is undefined + * if a != b but neither is greater or smaller (see RFC1982 section + * 3.2.). + */ +int compare_serial(uint32_t a, uint32_t b); + +/* + * call region_destroy on (region*)data, useful for region_add_cleanup(). + */ +void cleanup_region(void *data); + +/* + * Region used to store owner and origin of previous RR (used + * for pretty printing of zone data). + * Keep the same between calls to print_rr. + */ +struct state_pretty_rr { + struct region *previous_owner_region; + const struct dname *previous_owner; + const struct dname *previous_owner_origin; +}; +struct state_pretty_rr* create_pretty_rr(struct region* region); +/* print rr to file, returns 0 on failure(nothing is written) */ +int print_rr(FILE *out, struct state_pretty_rr* state, struct rr *record); + +/* + * Convert a numeric rcode value to a human readable string + */ +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( +#ifdef INET6 + struct sockaddr_storage addr +#else + struct sockaddr_in addr +#endif +, char address[], socklen_t size); + +#endif /* _UTIL_H_ */ diff --git a/usr.sbin/nsd/xfrd-disk.c b/usr.sbin/nsd/xfrd-disk.c new file mode 100644 index 00000000000..4925a97da66 --- /dev/null +++ b/usr.sbin/nsd/xfrd-disk.c @@ -0,0 +1,458 @@ +/* + * xfrd-disk.c - XFR (transfer) Daemon TCP system source file. Read/Write state to disk. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include "xfrd-disk.h" +#include "xfrd.h" +#include "buffer.h" +#include "nsd.h" +#include "options.h" + +/* quick tokenizer, reads words separated by whitespace. + No quoted strings. Comments are skipped (#... eol). */ +static char* +xfrd_read_token(FILE* in) +{ + static char buf[4000]; + buf[sizeof(buf)-1]=0; + while(1) { + if(fscanf(in, " %3990s", buf) != 1) + return 0; + + if(buf[0] != '#') + return buf; + + if(!fgets(buf, sizeof(buf), in)) + return 0; + } +} + +static int +xfrd_read_i16(FILE *in, uint16_t* v) +{ + char* p = xfrd_read_token(in); + if(!p) + return 0; + + *v=atoi(p); + return 1; +} + +static int +xfrd_read_i32(FILE *in, uint32_t* v) +{ + char* p = xfrd_read_token(in); + if(!p) + return 0; + + *v=atoi(p); + return 1; +} + +static int +xfrd_read_time_t(FILE *in, time_t* v) +{ + char* p = xfrd_read_token(in); + if(!p) + return 0; + + *v=atol(p); + return 1; +} + +static int +xfrd_read_check_str(FILE* in, const char* str) +{ + char *p = xfrd_read_token(in); + if(!p) + return 0; + + if(strcmp(p, str) != 0) + return 0; + + return 1; +} + +static int +xfrd_read_state_soa(FILE* in, const char* id_acquired, + const char* id, xfrd_soa_t* soa, time_t* soatime) +{ + char *p; + + if(!xfrd_read_check_str(in, id_acquired) || + !xfrd_read_time_t(in, soatime)) { + return 0; + } + + if(*soatime == 0) + return 1; + + if(!xfrd_read_check_str(in, id) || + !xfrd_read_i16(in, &soa->type) || + !xfrd_read_i16(in, &soa->klass) || + !xfrd_read_i32(in, &soa->ttl) || + !xfrd_read_i16(in, &soa->rdata_count)) + { + return 0; + } + + soa->type = htons(soa->type); + soa->klass = htons(soa->klass); + soa->ttl = htonl(soa->ttl); + soa->rdata_count = htons(soa->rdata_count); + + if(!(p=xfrd_read_token(in)) || + !(soa->prim_ns[0] = dname_parse_wire(soa->prim_ns+1, p))) + return 0; + + if(!(p=xfrd_read_token(in)) || + !(soa->email[0] = dname_parse_wire(soa->email+1, p))) + return 0; + + if(!xfrd_read_i32(in, &soa->serial) || + !xfrd_read_i32(in, &soa->refresh) || + !xfrd_read_i32(in, &soa->retry) || + !xfrd_read_i32(in, &soa->expire) || + !xfrd_read_i32(in, &soa->minimum)) + { + return 0; + } + + soa->serial = htonl(soa->serial); + soa->refresh = htonl(soa->refresh); + soa->retry = htonl(soa->retry); + soa->expire = htonl(soa->expire); + soa->minimum = htonl(soa->minimum); + return 1; +} + +void +xfrd_read_state(struct xfrd_state* xfrd) +{ + const char* statefile = xfrd->nsd->options->xfrdfile; + FILE *in; + uint32_t filetime = 0; + uint32_t numzones, i; + region_type *tempregion; + + tempregion = region_create(xalloc, free); + if(!tempregion) + return; + + in = fopen(statefile, "r"); + if(!in) { + if(errno != ENOENT) { + log_msg(LOG_ERR, "xfrd: Could not open file %s for reading: %s", + statefile, strerror(errno)); + } else { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: no file %s. refreshing all zones.", + statefile)); + } + region_destroy(tempregion); + return; + } + if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC) || + !xfrd_read_check_str(in, "filetime:") || + !xfrd_read_i32(in, &filetime) || + (time_t)filetime > xfrd_time()+15 || + !xfrd_read_check_str(in, "numzones:") || + !xfrd_read_i32(in, &numzones)) + { + log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%d)", + statefile, (int)filetime, (int)xfrd_time()); + fclose(in); + region_destroy(tempregion); + return; + } + + for(i=0; i<numzones; i++) { + char *p; + xfrd_zone_t* zone; + const dname_type* dname; + uint32_t state, masnum, nextmas, round_num, timeout; + xfrd_soa_t soa_nsd_read, soa_disk_read, soa_notified_read; + time_t soa_nsd_acquired_read, + soa_disk_acquired_read, soa_notified_acquired_read; + xfrd_soa_t incoming_soa; + time_t incoming_acquired; + + memset(&soa_nsd_read, 0, sizeof(soa_nsd_read)); + memset(&soa_disk_read, 0, sizeof(soa_disk_read)); + memset(&soa_notified_read, 0, sizeof(soa_notified_read)); + + if(!xfrd_read_check_str(in, "zone:") || + !xfrd_read_check_str(in, "name:") || + !(p=xfrd_read_token(in)) || + !(dname = dname_parse(tempregion, p)) || + !xfrd_read_check_str(in, "state:") || + !xfrd_read_i32(in, &state) || (state>2) || + !xfrd_read_check_str(in, "master:") || + !xfrd_read_i32(in, &masnum) || + !xfrd_read_check_str(in, "next_master:") || + !xfrd_read_i32(in, &nextmas) || + !xfrd_read_check_str(in, "round_num:") || + !xfrd_read_i32(in, &round_num) || + !xfrd_read_check_str(in, "next_timeout:") || + !xfrd_read_i32(in, &timeout) || + !xfrd_read_state_soa(in, "soa_nsd_acquired:", "soa_nsd:", + &soa_nsd_read, &soa_nsd_acquired_read) || + !xfrd_read_state_soa(in, "soa_disk_acquired:", "soa_disk:", + &soa_disk_read, &soa_disk_acquired_read) || + !xfrd_read_state_soa(in, "soa_notify_acquired:", "soa_notify:", + &soa_notified_read, &soa_notified_acquired_read)) + { + log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%d)", + statefile, (int)filetime, (int)xfrd_time()); + fclose(in); + region_destroy(tempregion); + return; + } + + zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname); + if(!zone) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: state file has info for not configured zone %s", p)); + continue; + } + + if(soa_nsd_acquired_read>xfrd_time()+15 || + soa_disk_acquired_read>xfrd_time()+15 || + soa_notified_acquired_read>xfrd_time()+15) + { + log_msg(LOG_ERR, "xfrd: statefile %s contains" + " times in the future for zone %s. Ignoring.", + statefile, zone->apex_str); + continue; + } + zone->state = state; + zone->master_num = masnum; + zone->next_master = nextmas; + zone->round_num = round_num; + zone->timeout.tv_sec = timeout; + zone->timeout.tv_nsec = 0; + + /* read the zone OK, now set the master properly */ + zone->master = acl_find_num( + zone->zone_options->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_num = 0; + zone->round_num = 0; + } + + /* + * There is no timeout, + * or there is a notification, + * or there is a soa && current time is past refresh point + */ + if(timeout == 0 || soa_notified_acquired_read != 0 || + (soa_disk_acquired_read != 0 && + (uint32_t)xfrd_time() - soa_disk_acquired_read + > ntohl(soa_disk_read.refresh))) + { + zone->state = xfrd_zone_refreshing; + xfrd_set_refresh_now(zone); + } + + /* There is a soa && current time is past expiry point */ + if(soa_disk_acquired_read!=0 && + (uint32_t)xfrd_time() - soa_disk_acquired_read + > ntohl(soa_disk_read.expire)) + { + zone->state = xfrd_zone_expired; + xfrd_set_refresh_now(zone); + } + + /* handle as an incoming SOA. */ + incoming_soa = zone->soa_nsd; + incoming_acquired = zone->soa_nsd_acquired; + zone->soa_nsd = soa_nsd_read; + zone->soa_disk = soa_disk_read; + zone->soa_notified = soa_notified_read; + zone->soa_nsd_acquired = soa_nsd_acquired_read; + zone->soa_disk_acquired = soa_disk_acquired_read; + zone->soa_notified_acquired = soa_notified_acquired_read; + xfrd_handle_incoming_soa(zone, &incoming_soa, incoming_acquired); + } + + if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC)) { + log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%d)", + statefile, (int)filetime, (int)xfrd_time()); + region_destroy(tempregion); + fclose(in); + return; + } + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", numzones)); + fclose(in); + region_destroy(tempregion); +} + +/* prints neato days hours and minutes. */ +static void +neato_timeout(FILE* out, const char* str, uint32_t secs) +{ + fprintf(out, "%s", str); + if(secs <= 0) { + fprintf(out, " %ds", secs); + return; + } + if(secs >= 3600*24) { + fprintf(out, " %dd", secs/(3600*24)); + secs = secs % (3600*24); + } + if(secs >= 3600) { + fprintf(out, " %dh", secs/3600); + secs = secs%3600; + } + if(secs >= 60) { + fprintf(out, " %dm", secs/60); + secs = secs%60; + } + if(secs > 0) { + fprintf(out, " %ds", secs); + } +} + +static void xfrd_write_dname(FILE* out, uint8_t* dname) +{ + uint8_t* d= dname+1; + uint8_t len = *d++; + uint8_t i; + + if(dname[0]<=1) { + fprintf(out, "."); + return; + } + + while(len) + { + assert(d - (dname+1) <= dname[0]); + for(i=0; i<len; i++) + { + uint8_t ch = *d++; + if (isalnum(ch) || ch == '-' || ch == '_') { + fprintf(out, "%c", ch); + } else if (ch == '.' || ch == '\\') { + fprintf(out, "\\%c", ch); + } else { + fprintf(out, "\\%03u", (unsigned int)ch); + } + } + fprintf(out, "."); + len = *d++; + } +} + +static void +xfrd_write_state_soa(FILE* out, const char* id, + xfrd_soa_t* soa, time_t soatime, const dname_type* ATTR_UNUSED(apex)) +{ + fprintf(out, "\t%s_acquired: %d", id, (int)soatime); + if(!soatime) { + fprintf(out, "\n"); + return; + } + neato_timeout(out, "\t# was", xfrd_time()-soatime); + fprintf(out, " ago\n"); + + fprintf(out, "\t%s: %u %u %u %u", id, + ntohs(soa->type), ntohs(soa->klass), + ntohl(soa->ttl), ntohs(soa->rdata_count)); + fprintf(out, " "); + xfrd_write_dname(out, soa->prim_ns); + fprintf(out, " "); + xfrd_write_dname(out, soa->email); + fprintf(out, " %u", ntohl(soa->serial)); + fprintf(out, " %u", ntohl(soa->refresh)); + fprintf(out, " %u", ntohl(soa->retry)); + fprintf(out, " %u", ntohl(soa->expire)); + fprintf(out, " %u\n", ntohl(soa->minimum)); + fprintf(out, "\t#"); + neato_timeout(out, " refresh =", ntohl(soa->refresh)); + neato_timeout(out, " retry =", ntohl(soa->retry)); + neato_timeout(out, " expire =", ntohl(soa->expire)); + neato_timeout(out, " minimum =", ntohl(soa->minimum)); + fprintf(out, "\n"); +} + +void +xfrd_write_state(struct xfrd_state* xfrd) +{ + rbnode_t* p; + const char* statefile = xfrd->nsd->options->xfrdfile; + FILE *out; + time_t now = xfrd_time(); + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: write file %s", statefile)); + out = fopen(statefile, "w"); + if(!out) { + log_msg(LOG_ERR, "xfrd: Could not open file %s for writing: %s", + statefile, strerror(errno)); + return; + } + + fprintf(out, "%s\n", XFRD_FILE_MAGIC); + fprintf(out, "# This file is written on exit by nsd xfr daemon.\n"); + fprintf(out, "# This file contains slave zone information:\n"); + fprintf(out, "# * timeouts (when was zone data acquired)\n"); + fprintf(out, "# * state (OK, refreshing, expired)\n"); + fprintf(out, "# * which master transfer to attempt next\n"); + fprintf(out, "# The file is read on start (but not on reload) by nsd xfr daemon.\n"); + fprintf(out, "# You can edit; but do not change statement order\n"); + fprintf(out, "# and no fancy stuff (like quoted \"strings\").\n"); + fprintf(out, "#\n"); + fprintf(out, "# If you remove a zone entry, it will be refreshed.\n"); + fprintf(out, "# This can be useful for an expired zone; it revives\n"); + fprintf(out, "# the zone temporarily, from refresh-expiry time.\n"); + fprintf(out, "# If you delete the file all slave zones are updated.\n"); + fprintf(out, "#\n"); + fprintf(out, "# Note: if you edit this file while nsd is running,\n"); + fprintf(out, "# it will be overwritten on exit by nsd.\n"); + fprintf(out, "\n"); + fprintf(out, "filetime: %d\t# %s\n", (int)now, ctime(&now)); + fprintf(out, "# The number of zone entries in this file\n"); + fprintf(out, "numzones: %d\n", (int)xfrd->zones->count); + fprintf(out, "\n"); + for(p = rbtree_first(xfrd->zones); p && p!=RBTREE_NULL; p=rbtree_next(p)) + { + xfrd_zone_t* zone = (xfrd_zone_t*)p; + fprintf(out, "zone: \tname: %s\n", zone->apex_str); + fprintf(out, "\tstate: %d", (int)zone->state); + fprintf(out, " # %s", zone->state==xfrd_zone_ok?"OK":( + zone->state==xfrd_zone_refreshing?"refreshing":"expired")); + fprintf(out, "\n"); + fprintf(out, "\tmaster: %d\n", zone->master_num); + 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) { + neato_timeout(out, "\t# =", zone->timeout.tv_sec - xfrd_time()); + } + fprintf(out, "\n"); + xfrd_write_state_soa(out, "soa_nsd", &zone->soa_nsd, + zone->soa_nsd_acquired, zone->apex); + xfrd_write_state_soa(out, "soa_disk", &zone->soa_disk, + zone->soa_disk_acquired, zone->apex); + xfrd_write_state_soa(out, "soa_notify", &zone->soa_notified, + zone->soa_notified_acquired, zone->apex); + fprintf(out, "\n"); + } + + fprintf(out, "%s\n", XFRD_FILE_MAGIC); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: written %d zones to state file", + (int)xfrd->zones->count)); + fclose(out); +} diff --git a/usr.sbin/nsd/xfrd-disk.h b/usr.sbin/nsd/xfrd-disk.h new file mode 100644 index 00000000000..217ecc122b9 --- /dev/null +++ b/usr.sbin/nsd/xfrd-disk.h @@ -0,0 +1,24 @@ +/* + * xfrd-disk.h - XFR (transfer) Daemon TCP system header file. Save/Load state to disk. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef XFRD_DISK_H +#define XFRD_DISK_H + +#include <config.h> +struct xfrd_state; + +/* magic string to identify xfrd state file */ +#define XFRD_FILE_MAGIC "NSDXFRD1" + +/* read from state file as many zones as possible (until error/eof).*/ +void xfrd_read_state(struct xfrd_state* xfrd); +/* write xfrd zone state if possible */ +void xfrd_write_state(struct xfrd_state* xfrd); + +#endif /* XFRD_DISK_H */ diff --git a/usr.sbin/nsd/xfrd-notify.c b/usr.sbin/nsd/xfrd-notify.c new file mode 100644 index 00000000000..1ef2259cfeb --- /dev/null +++ b/usr.sbin/nsd/xfrd-notify.c @@ -0,0 +1,315 @@ +/* + * xfrd-notify.c - notify sending routines + * + * Copyright (c) 2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> +#include <assert.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include "xfrd-notify.h" +#include "xfrd.h" +#include "xfrd-tcp.h" +#include "packet.h" + +#define XFRD_NOTIFY_RETRY_TIMOUT 15 /* seconds between retries sending NOTIFY */ + +/* start sending notifies */ +static void notify_enable(struct notify_zone_t* zone, + struct xfrd_soa* new_soa); +/* stop sending notifies */ +static void notify_disable(struct notify_zone_t* zone); +/* setup the notify active state */ +static void setup_notify_active(struct notify_zone_t* zone); + +/* returns if the notify send is done for the notify_current acl */ +static int xfrd_handle_notify_reply(struct notify_zone_t* zone, buffer_type* packet); + +/* handle zone notify send */ +static void xfrd_handle_notify_send(netio_type *netio, + netio_handler_type *handler, netio_event_types_type event_types); + +static void xfrd_notify_next(struct notify_zone_t* zone); + +static void xfrd_notify_send_udp(struct notify_zone_t* zone, buffer_type* packet); + +static void +notify_disable(struct notify_zone_t* zone) +{ + zone->notify_current = 0; + zone->notify_send_handler.timeout = NULL; + if(zone->notify_send_handler.fd != -1) { + close(zone->notify_send_handler.fd); + zone->notify_send_handler.fd = -1; + } + + if(xfrd->notify_udp_num == XFRD_MAX_UDP_NOTIFY) { + /* find next waiting and needy zone */ + while(xfrd->notify_waiting_first) { + /* snip off */ + struct notify_zone_t* wz = xfrd->notify_waiting_first; + assert(wz->is_waiting); + wz->is_waiting = 0; + xfrd->notify_waiting_first = wz->waiting_next; + if(xfrd->notify_waiting_last == wz) + xfrd->notify_waiting_last = NULL; + /* see if this zone needs notify sending */ + if(wz->notify_current) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s: notify off waiting list.", + zone->apex_str) ); + setup_notify_active(wz); + return; + } + } + } + xfrd->notify_udp_num--; +} + +void +init_notify_send(rbtree_t* tree, netio_type* netio, region_type* region, + const dname_type* apex, zone_options_t* options, zone_type* dbzone) +{ + struct notify_zone_t* not = (struct notify_zone_t*) + region_alloc(region, sizeof(struct notify_zone_t)); + memset(not, 0, sizeof(struct notify_zone_t)); + not->apex = apex; + not->apex_str = options->name; + not->node.key = not->apex; + not->options = options; + + /* if master zone and have a SOA */ + not->current_soa = (struct xfrd_soa*)region_alloc(region, + sizeof(struct xfrd_soa)); + memset(not->current_soa, 0, sizeof(struct xfrd_soa)); + if(dbzone && dbzone->soa_rrset && dbzone->soa_rrset->rrs) { + xfrd_copy_soa(not->current_soa, dbzone->soa_rrset->rrs); + } + + not->is_waiting = 0; + not->notify_send_handler.fd = -1; + not->notify_send_handler.timeout = 0; + not->notify_send_handler.user_data = not; + not->notify_send_handler.event_types = + NETIO_EVENT_READ|NETIO_EVENT_TIMEOUT; + not->notify_send_handler.event_handler = xfrd_handle_notify_send; + netio_add_handler(netio, ¬->notify_send_handler); + +#ifdef TSIG + tsig_create_record_custom(¬->notify_tsig, region, 0, 0, 4); +#endif /* TSIG */ + not->notify_current = 0; + + rbtree_insert(tree, (rbnode_t*)not); +} + +static int +xfrd_handle_notify_reply(struct notify_zone_t* zone, buffer_type* packet) +{ + if((OPCODE(packet) != OPCODE_NOTIFY) || + (QR(packet) == 0)) { + log_msg(LOG_ERR, "xfrd: zone %s: received bad notify reply opcode/flags", + zone->apex_str); + return 0; + } + /* we know it is OPCODE NOTIFY, QUERY_REPLY and for this zone */ + if(ID(packet) != zone->notify_query_id) { + log_msg(LOG_ERR, "xfrd: zone %s: received notify-ack with bad ID", + zone->apex_str); + return 0; + } + /* could check tsig, but why. The reply does not cause processing. */ + if(RCODE(packet) != RCODE_OK) { + log_msg(LOG_ERR, "xfrd: zone %s: received notify response error %s from %s", + zone->apex_str, rcode2str(RCODE(packet)), + zone->notify_current->ip_address_spec); + if(RCODE(packet) == RCODE_IMPL) + return 1; /* rfc1996: notimpl notify reply: consider retries done */ + return 0; + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: host %s acknowledges notify", + zone->apex_str, zone->notify_current->ip_address_spec)); + return 1; +} + +static void +xfrd_notify_next(struct notify_zone_t* zone) +{ + /* advance to next in acl */ + zone->notify_current = zone->notify_current->next; + zone->notify_retry = 0; + if(zone->notify_current == 0) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s: no more notify-send acls. stop notify.", + zone->apex_str)); + notify_disable(zone); + return; + } +} + +static void +xfrd_notify_send_udp(struct notify_zone_t* zone, buffer_type* packet) +{ + if(zone->notify_send_handler.fd != -1) + close(zone->notify_send_handler.fd); + zone->notify_send_handler.fd = -1; + /* Set timeout for next reply */ + zone->notify_timeout.tv_sec = xfrd_time() + XFRD_NOTIFY_RETRY_TIMOUT; + /* send NOTIFY to secondary. */ + xfrd_setup_packet(packet, TYPE_SOA, CLASS_IN, zone->apex); + zone->notify_query_id = ID(packet); + OPCODE_SET(packet, OPCODE_NOTIFY); + AA_SET(packet); + if(zone->current_soa->serial != 0) { + /* add current SOA to answer section */ + ANCOUNT_SET(packet, 1); + xfrd_write_soa_buffer(packet, zone->apex, zone->current_soa); + } +#ifdef TSIG + if(zone->notify_current->key_options) { + xfrd_tsig_sign_request(packet, &zone->notify_tsig, zone->notify_current); + } +#endif /* TSIG */ + buffer_flip(packet); + zone->notify_send_handler.fd = xfrd_send_udp(zone->notify_current, + packet, zone->options->outgoing_interface); + if(zone->notify_send_handler.fd == -1) { + log_msg(LOG_ERR, "xfrd: zone %s: could not send notify #%d to %s", + zone->apex_str, zone->notify_retry, + zone->notify_current->ip_address_spec); + return; + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: sent notify #%d to %s", + zone->apex_str, zone->notify_retry, + zone->notify_current->ip_address_spec)); +} + +static void +xfrd_handle_notify_send(netio_type* ATTR_UNUSED(netio), + netio_handler_type *handler, netio_event_types_type event_types) +{ + struct notify_zone_t* zone = (struct notify_zone_t*)handler->user_data; + buffer_type* packet = xfrd_get_temp_buffer(); + assert(zone->notify_current); + if(zone->is_waiting) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: notify waiting, skipped, %s", zone->apex_str)); + assert(zone->notify_send_handler.fd == -1); + return; + } + if(event_types & NETIO_EVENT_READ) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s: read notify ACK", zone->apex_str)); + assert(handler->fd != -1); + if(xfrd_udp_read_packet(packet, zone->notify_send_handler.fd)) { + if(xfrd_handle_notify_reply(zone, packet)) + xfrd_notify_next(zone); + } + } else if(event_types & NETIO_EVENT_TIMEOUT) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify timeout", + zone->apex_str)); + /* timeout, try again */ + } + /* see if notify is still enabled */ + if(zone->notify_current) { + zone->notify_retry++; + if(zone->notify_retry > zone->options->notify_retry) { + log_msg(LOG_ERR, "xfrd: zone %s: max notify send count reached, %s unreachable", + zone->apex_str, zone->notify_current->ip_address_spec); + xfrd_notify_next(zone); + } + } + if(zone->notify_current) { + /* try again */ + xfrd_notify_send_udp(zone, packet); + } +} + +static void +setup_notify_active(struct notify_zone_t* zone) +{ + zone->notify_retry = 0; + zone->notify_current = zone->options->notify; + zone->notify_send_handler.timeout = &zone->notify_timeout; + zone->notify_timeout.tv_sec = xfrd_time(); + zone->notify_timeout.tv_nsec = 0; +} + +static void +notify_enable(struct notify_zone_t* zone, struct xfrd_soa* new_soa) +{ + if(!zone->options->notify) { + return; /* no notify acl, nothing to do */ + } + + if(new_soa == NULL) + memset(zone->current_soa, 0, sizeof(xfrd_soa_t)); + else + memcpy(zone->current_soa, new_soa, sizeof(xfrd_soa_t)); + if(zone->is_waiting) + return; + + if(xfrd->notify_udp_num < XFRD_MAX_UDP_NOTIFY) { + setup_notify_active(zone); + xfrd->notify_udp_num++; + return; + } + /* put it in waiting list */ + zone->notify_current = zone->options->notify; + zone->is_waiting = 1; + zone->waiting_next = NULL; + if(xfrd->notify_waiting_last) { + xfrd->notify_waiting_last->waiting_next = zone; + } else { + xfrd->notify_waiting_first = zone; + } + xfrd->notify_waiting_last = zone; + zone->notify_send_handler.timeout = NULL; + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify on waiting list.", + zone->apex_str)); +} + +void +xfrd_send_notify(rbtree_t* tree, const dname_type* apex, struct xfrd_soa* new_soa) +{ + /* lookup the zone */ + struct notify_zone_t* zone = (struct notify_zone_t*) + rbtree_search(tree, apex); + assert(zone); + + notify_enable(zone, new_soa); +} + +void +notify_handle_master_zone_soainfo(rbtree_t* tree, + const dname_type* apex, struct xfrd_soa* new_soa) +{ + /* lookup the zone */ + struct notify_zone_t* zone = (struct notify_zone_t*) + rbtree_search(tree, apex); + assert(zone); + + /* check if SOA changed */ + if( (new_soa == NULL && zone->current_soa->serial == 0) || + (new_soa && new_soa->serial == zone->current_soa->serial)) + return; + + notify_enable(zone, new_soa); +} + +void close_notify_fds(rbtree_t* tree) +{ + struct notify_zone_t* zone; + RBTREE_FOR(zone, struct notify_zone_t*, tree) + { + if(zone->notify_send_handler.fd != -1) { + close(zone->notify_send_handler.fd); + zone->notify_send_handler.fd = -1; + } + } +} diff --git a/usr.sbin/nsd/xfrd-notify.h b/usr.sbin/nsd/xfrd-notify.h new file mode 100644 index 00000000000..b9363ae20fa --- /dev/null +++ b/usr.sbin/nsd/xfrd-notify.h @@ -0,0 +1,72 @@ +/* + * xfrd-notify.h - notify sending routines. + * + * Copyright (c) 2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef XFRD_NOTIFY_H +#define XFRD_NOTIFY_H + +#include <config.h> +#include "tsig.h" +#include "netio.h" +#include "rbtree.h" + +struct nsd; +struct region; +struct xfrd_zone; +struct zone_options; +struct zone; +struct xfrd_soa; +struct acl_options; + +/** + * This struct keeps track of outbound notifies for a zone. + */ +struct notify_zone_t { + rbnode_t node; + /* name of the zone */ + const dname_type* apex; + const char* apex_str; + +#ifdef TSIG + tsig_record_type notify_tsig; /* tsig state for notify */ +#endif + struct zone_options* options; + struct xfrd_soa *current_soa; /* current SOA in NSD */ + + /* notify sending handler */ + /* Not saved on disk (i.e. kill of daemon stops notifies) */ + netio_handler_type notify_send_handler; + struct timespec notify_timeout; + struct acl_options* notify_current; /* current slave to notify */ + uint8_t notify_retry; /* how manieth retry in sending to current */ + uint16_t notify_query_id; + + /* is this notify waiting for a socket? */ + uint8_t is_waiting; + /* next in the waiting list for the udp sockets */ + struct notify_zone_t* waiting_next; +}; + +/* initialise outgoing notifies */ +void init_notify_send(rbtree_t* tree, netio_type* netio, region_type* region, + const dname_type* apex, struct zone_options* options, + struct zone* dbzone); + +/* send notifications to all in the notify list */ +void xfrd_send_notify(rbtree_t* tree, const struct dname* apex, + struct xfrd_soa* new_soa); + +/* handle soa update notify for a master zone. newsoa can be NULL. + Makes sure that the soa (serial) has changed. Or drops notify. */ +void notify_handle_master_zone_soainfo(rbtree_t* tree, + const dname_type* apex, struct xfrd_soa* new_soa); + +/* close fds in use for notification sending */ +void close_notify_fds(rbtree_t* tree); + +#endif /* XFRD_NOTIFY_H */ diff --git a/usr.sbin/nsd/xfrd-tcp.c b/usr.sbin/nsd/xfrd-tcp.c new file mode 100644 index 00000000000..fd86149b608 --- /dev/null +++ b/usr.sbin/nsd/xfrd-tcp.c @@ -0,0 +1,585 @@ +/* + * xfrd-tcp.c - XFR (transfer) Daemon TCP system source file. Manages tcp conn. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include "xfrd-tcp.h" +#include "buffer.h" +#include "packet.h" +#include "dname.h" +#include "options.h" +#include "namedb.h" +#include "xfrd.h" +#include "util.h" + +xfrd_tcp_set_t* xfrd_tcp_set_create(struct region* region) +{ + int i; + xfrd_tcp_set_t* tcp_set = region_alloc(region, sizeof(xfrd_tcp_set_t)); + memset(tcp_set, 0, sizeof(xfrd_tcp_set_t)); + tcp_set->tcp_count = 0; + 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); + return tcp_set; +} + +void +xfrd_setup_packet(buffer_type* packet, + uint16_t type, uint16_t klass, const dname_type* dname) +{ + /* Set up the header */ + buffer_clear(packet); + ID_SET(packet, (uint16_t) random()); + FLAGS_SET(packet, 0); + OPCODE_SET(packet, OPCODE_QUERY); + QDCOUNT_SET(packet, 1); + ANCOUNT_SET(packet, 0); + NSCOUNT_SET(packet, 0); + ARCOUNT_SET(packet, 0); + buffer_skip(packet, QHEADERSZ); + + /* The question record. */ + buffer_write(packet, dname_name(dname), dname->name_size); + buffer_write_u16(packet, type); + buffer_write_u16(packet, klass); +} + +static socklen_t +#ifdef INET6 +xfrd_acl_sockaddr(acl_options_t* acl, unsigned int port, + struct sockaddr_storage *sck) +#else +xfrd_acl_sockaddr(acl_options_t* acl, unsigned int port, + struct sockaddr_in *sck, const char* fromto) +#endif /* INET6 */ +{ + /* setup address structure */ +#ifdef INET6 + memset(sck, 0, sizeof(struct sockaddr_storage)); +#else + memset(sck, 0, sizeof(struct sockaddr_in)); +#endif + if(acl->is_ipv6) { +#ifdef INET6 + struct sockaddr_in6* sa = (struct sockaddr_in6*)sck; + sa->sin6_family = AF_INET6; + sa->sin6_port = htons(port); + sa->sin6_addr = acl->addr.addr6; + return sizeof(struct sockaddr_in6); +#else + log_msg(LOG_ERR, "xfrd: IPv6 connection %s %s attempted but no \ +INET6.", fromto, acl->ip_address_spec); + return 0; +#endif + } else { + struct sockaddr_in* sa = (struct sockaddr_in*)sck; + sa->sin_family = AF_INET; + sa->sin_port = htons(port); + sa->sin_addr = acl->addr.addr; + return sizeof(struct sockaddr_in); + } +} + +socklen_t +#ifdef INET6 +xfrd_acl_sockaddr_to(acl_options_t* acl, struct sockaddr_storage *to) +#else +xfrd_acl_sockaddr_to(acl_options_t* acl, struct sockaddr_in *to) +#endif /* INET6 */ +{ + unsigned int port = acl->port?acl->port:(unsigned)atoi(TCP_PORT); +#ifdef INET6 + return xfrd_acl_sockaddr(acl, port, to); +#else + return xfrd_acl_sockaddr(acl, port, to, "to"); +#endif /* INET6 */ +} + +socklen_t +#ifdef INET6 +xfrd_acl_sockaddr_frm(acl_options_t* acl, struct sockaddr_storage *frm) +#else +xfrd_acl_sockaddr_frm(acl_options_t* acl, struct sockaddr_in *frm) +#endif /* INET6 */ +{ + unsigned int port = acl->port?acl->port:0; +#ifdef INET6 + return xfrd_acl_sockaddr(acl, port, frm); +#else + return xfrd_acl_sockaddr(acl, port, frm, "from"); +#endif /* INET6 */ +} + +void +xfrd_write_soa_buffer(struct buffer* packet, + const dname_type* apex, struct xfrd_soa* soa) +{ + size_t rdlength_pos; + uint16_t rdlength; + buffer_write(packet, dname_name(apex), apex->name_size); + + /* already in network order */ + buffer_write(packet, &soa->type, sizeof(soa->type)); + buffer_write(packet, &soa->klass, sizeof(soa->klass)); + buffer_write(packet, &soa->ttl, sizeof(soa->ttl)); + rdlength_pos = buffer_position(packet); + buffer_skip(packet, sizeof(rdlength)); + + /* uncompressed dnames */ + buffer_write(packet, soa->prim_ns+1, soa->prim_ns[0]); + buffer_write(packet, soa->email+1, soa->email[0]); + + buffer_write(packet, &soa->serial, sizeof(uint32_t)); + buffer_write(packet, &soa->refresh, sizeof(uint32_t)); + buffer_write(packet, &soa->retry, sizeof(uint32_t)); + buffer_write(packet, &soa->expire, sizeof(uint32_t)); + buffer_write(packet, &soa->minimum, sizeof(uint32_t)); + + /* write length of RR */ + rdlength = buffer_position(packet) - rdlength_pos - sizeof(rdlength); + buffer_write_u16_at(packet, rdlength_pos, rdlength); +} + +xfrd_tcp_t* +xfrd_tcp_create(region_type* region) +{ + 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->fd = -1; + + return tcp_state; +} + +void +xfrd_tcp_obtain(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +{ + assert(zone->tcp_conn == -1); + assert(zone->tcp_waiting == 0); + + if(set->tcp_count < XFRD_MAX_TCP) { + int i; + assert(!set->tcp_waiting_first); + set->tcp_count ++; + /* find a free tcp_buffer */ + for(i=0; i<XFRD_MAX_TCP; i++) { + if(set->tcp_state[i]->fd == -1) { + zone->tcp_conn = i; + break; + } + } + + assert(zone->tcp_conn != -1); + + zone->tcp_waiting = 0; + + /* stop udp use (if any) */ + if(zone->zone_handler.fd != -1) + xfrd_udp_release(zone); + + if(!xfrd_tcp_open(set, zone)) + return; + + xfrd_tcp_xfr(set, 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 = 1; + if(!set->tcp_waiting_last) { + set->tcp_waiting_first = zone; + set->tcp_waiting_last = zone; + } else { + set->tcp_waiting_last->tcp_waiting_next = zone; + set->tcp_waiting_last = zone; + } + xfrd_unset_timer(zone); +} + +int +xfrd_tcp_open(xfrd_tcp_set_t* set, 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; + + 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; + + 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)); + xfrd_set_refresh_now(zone); + xfrd_tcp_release(set, zone); + return 0; + } + + to_len = xfrd_acl_sockaddr_to(zone->master, &to); + + /* bind it */ + if (!xfrd_bind_local_interface(fd, + zone->zone_options->outgoing_interface, zone->master, 1)) { + + xfrd_set_refresh_now(zone); + xfrd_tcp_release(set, zone); + return 0; + } + + conn = connect(fd, (struct sockaddr*)&to, to_len); + if (conn == -1 && errno != EINPROGRESS) { + log_msg(LOG_ERR, "xfrd: connect %s failed: %s", + zone->master->ip_address_spec, strerror(errno)); + 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); + return 1; +} + +void +xfrd_tcp_xfr(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +{ + xfrd_tcp_t* tcp = set->tcp_state[zone->tcp_conn]; + assert(zone->tcp_conn != -1); + assert(zone->tcp_waiting == 0); + /* start AXFR or IXFR for the zone */ + if(zone->soa_disk_acquired == 0 || zone->master->use_axfr_only || + zone->master->ixfr_disabled) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "request full zone transfer " + "(AXFR) for %s to %s", + zone->apex_str, zone->master->ip_address_spec)); + + xfrd_setup_packet(tcp->packet, TYPE_AXFR, CLASS_IN, zone->apex); + } 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); + NSCOUNT_SET(tcp->packet, 1); + xfrd_write_soa_buffer(tcp->packet, zone->apex, &zone->soa_disk); + } + zone->query_id = ID(tcp->packet); + zone->msg_seq_nr = 0; + zone->msg_rr_count = 0; +#ifdef TSIG + if(zone->master->key_options && zone->master->key_options->tsig_key) { + xfrd_tsig_sign_request(tcp->packet, &zone->tsig, zone->master); + } +#endif /* TSIG */ + 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 */ +} + +static void +tcp_conn_ready_for_reading(xfrd_tcp_t* tcp) +{ + tcp->total_bytes = 0; + tcp->msglen = 0; + buffer_clear(tcp->packet); +} + +int conn_write(xfrd_tcp_t* tcp) +{ + ssize_t sent; + + if(tcp->total_bytes < sizeof(tcp->msglen)) { + uint16_t sendlen = htons(tcp->msglen); + sent = write(tcp->fd, + (const char*)&sendlen + tcp->total_bytes, + sizeof(tcp->msglen) - tcp->total_bytes); + + if(sent == -1) { + if(errno == EAGAIN || errno == EINTR) { + /* write would block, try later */ + return 0; + } else { + return -1; + } + } + + tcp->total_bytes += sent; + if(tcp->total_bytes < sizeof(tcp->msglen)) { + /* incomplete write, resume later */ + return 0; + } + assert(tcp->total_bytes == sizeof(tcp->msglen)); + } + + assert(tcp->total_bytes < tcp->msglen + sizeof(tcp->msglen)); + + sent = write(tcp->fd, + buffer_current(tcp->packet), + buffer_remaining(tcp->packet)); + if(sent == -1) { + if(errno == EAGAIN || errno == EINTR) { + /* write would block, try later */ + return 0; + } else { + return -1; + } + } + + buffer_skip(tcp->packet, sent); + tcp->total_bytes += sent; + + if(tcp->total_bytes < tcp->msglen + sizeof(tcp->msglen)) { + /* more to write when socket becomes writable again */ + return 0; + } + + assert(tcp->total_bytes == tcp->msglen + sizeof(tcp->msglen)); + return 1; +} + +void +xfrd_tcp_write(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +{ + int ret; + xfrd_tcp_t* tcp = set->tcp_state[zone->tcp_conn]; + assert(zone->tcp_conn != -1); + if(tcp->total_bytes == 0) { + /* check for pending error from nonblocking connect */ + /* from Stevens, unix network programming, vol1, 3rd ed, p450 */ + int error = 0; + socklen_t len = sizeof(error); + if(getsockopt(tcp->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0){ + error = errno; /* on solaris errno is error */ + } + 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); + 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); + return; + } + 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); +} + +int +conn_read(xfrd_tcp_t* tcp) +{ + ssize_t received; + /* receive leading packet length bytes */ + if(tcp->total_bytes < sizeof(tcp->msglen)) { + received = read(tcp->fd, + (char*) &tcp->msglen + tcp->total_bytes, + sizeof(tcp->msglen) - tcp->total_bytes); + if(received == -1) { + if(errno == EAGAIN || errno == EINTR) { + /* read would block, try later */ + return 0; + } else { +#ifdef ECONNRESET + if (verbosity >= 2 || errno != ECONNRESET) +#endif /* ECONNRESET */ + log_msg(LOG_ERR, "tcp read sz: %s", strerror(errno)); + return -1; + } + } else if(received == 0) { + /* EOF */ + return -1; + } + tcp->total_bytes += received; + if(tcp->total_bytes < sizeof(tcp->msglen)) { + /* not complete yet, try later */ + return 0; + } + + assert(tcp->total_bytes == sizeof(tcp->msglen)); + tcp->msglen = ntohs(tcp->msglen); + + if(tcp->msglen > buffer_capacity(tcp->packet)) { + log_msg(LOG_ERR, "buffer too small, dropping connection"); + return 0; + } + buffer_set_limit(tcp->packet, tcp->msglen); + } + + assert(buffer_remaining(tcp->packet) > 0); + + received = read(tcp->fd, buffer_current(tcp->packet), + buffer_remaining(tcp->packet)); + if(received == -1) { + if(errno == EAGAIN || errno == EINTR) { + /* read would block, try later */ + return 0; + } else { +#ifdef ECONNRESET + if (verbosity >= 2 || errno != ECONNRESET) +#endif /* ECONNRESET */ + log_msg(LOG_ERR, "tcp read %s", strerror(errno)); + return -1; + } + } else if(received == 0) { + /* EOF */ + return -1; + } + + tcp->total_bytes += received; + buffer_skip(tcp->packet, received); + + if(buffer_remaining(tcp->packet) > 0) { + /* not complete yet, wait for more */ + return 0; + } + + /* completed */ + assert(buffer_position(tcp->packet) == tcp->msglen); + return 1; +} + +void +xfrd_tcp_read(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +{ + xfrd_tcp_t* tcp = set->tcp_state[zone->tcp_conn]; + int ret; + + assert(zone->tcp_conn != -1); + ret = conn_read(tcp); + if(ret == -1) { + xfrd_set_refresh_now(zone); + xfrd_tcp_release(set, zone); + return; + } + if(ret == 0) + return; + + /* completed msg */ + buffer_flip(tcp->packet); + switch(xfrd_handle_received_xfr_packet(zone, tcp->packet)) { + case xfrd_packet_more: + tcp_conn_ready_for_reading(tcp); + break; + case xfrd_packet_transfer: + case xfrd_packet_newlease: + xfrd_tcp_release(set, zone); + assert(zone->round_num == -1); + break; + case xfrd_packet_notimpl: + zone->master->ixfr_disabled = time(NULL); + xfrd_tcp_release(set, zone); + /* query next server */ + xfrd_make_request(zone); + break; + case xfrd_packet_bad: + case xfrd_packet_tcp: + default: + xfrd_tcp_release(set, zone); + /* query next server */ + xfrd_make_request(zone); + break; + } +} + +void +xfrd_tcp_release(xfrd_tcp_set_t* set, xfrd_zone_t* zone) +{ + int conn = zone->tcp_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); + + set->tcp_state[conn]->fd = -1; + + 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; + + set->tcp_waiting_first = zone->tcp_waiting_next; + zone->tcp_waiting_next = 0; + /* 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) + xfrd_udp_release(zone); + + if(!xfrd_tcp_open(set, zone)) + return; + + xfrd_tcp_xfr(set, zone); + } + else { + assert(!set->tcp_waiting_first); + set->tcp_count --; + assert(set->tcp_count >= 0); + } +} diff --git a/usr.sbin/nsd/xfrd-tcp.h b/usr.sbin/nsd/xfrd-tcp.h new file mode 100644 index 00000000000..99e88862df7 --- /dev/null +++ b/usr.sbin/nsd/xfrd-tcp.h @@ -0,0 +1,125 @@ +/* + * xfrd-tcp.h - XFR (transfer) Daemon TCP system header file. Manages tcp conn. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef XFRD_TCP_H +#define XFRD_TCP_H + +#include <config.h> +#include "xfrd.h" + +struct buffer; +struct xfrd_zone; +struct xfrd_soa; +struct xfrd_state; +struct region; +struct dname; +struct acl_options; + +typedef struct xfrd_tcp xfrd_tcp_t; +typedef struct xfrd_tcp_set xfrd_tcp_set_t; +/* + * A set of xfrd tcp connections. + */ +struct xfrd_tcp_set { + /* tcp connections, each has packet and read/wr state */ + struct xfrd_tcp *tcp_state[XFRD_MAX_TCP]; + /* number of TCP connections in use. */ + int tcp_count; + /* TCP timeout. */ + int tcp_timeout; + /* linked list of zones waiting for a TCP connection */ + struct xfrd_zone *tcp_waiting_first, *tcp_waiting_last; +}; + +/* + * Structure to keep track of an open tcp connection + * The xfrd tcp connection is used to first make a request + * Then to receive the answer packet(s). + */ +struct xfrd_tcp { + /* tcp connection state */ + /* state: reading or writing */ + uint8_t is_reading; + + /* how many bytes have been read/written - total, + incl. tcp length bytes */ + uint32_t total_bytes; + + /* msg len bytes */ + uint16_t msglen; + + /* fd of connection. -1 means unconnected */ + int fd; + + /* packet buffer of connection */ + struct buffer* packet; +}; + +/* create set of tcp connections */ +xfrd_tcp_set_t* xfrd_tcp_set_create(struct region* region); + +/* init tcp state */ +xfrd_tcp_t* xfrd_tcp_create(struct region* region); +/* obtain tcp connection for a zone (or wait) */ +void xfrd_tcp_obtain(xfrd_tcp_set_t* set, struct xfrd_zone* zone); +/* release tcp connection for a zone (starts waiting) */ +void xfrd_tcp_release(xfrd_tcp_set_t* set, struct xfrd_zone* zone); +/* use tcp connection to start xfr */ +void xfrd_tcp_xfr(xfrd_tcp_set_t* set, struct xfrd_zone* zone); +/* initialize tcp_state for a zone. Opens the connection. true on success.*/ +int xfrd_tcp_open(xfrd_tcp_set_t* set, struct xfrd_zone* zone); +/* read data from tcp, maybe partial read */ +void xfrd_tcp_read(xfrd_tcp_set_t* set, struct xfrd_zone* zone); +/* write data to tcp, maybe a partial write */ +void xfrd_tcp_write(xfrd_tcp_set_t* set, struct xfrd_zone* zone); + +/* see if the tcp connection is in the reading stage (else writin) */ +static inline int xfrd_tcp_is_reading(xfrd_tcp_set_t* set, int conn) +{return set->tcp_state[conn]->is_reading;} +/* + * Read from a stream connection (size16)+packet into buffer. + * returns value is + * -1 on error. + * 0 on short read, call back later. + * 1 on completed read. + * On first call, make sure total_bytes = 0, msglen=0, buffer_clear(). + * and the packet and fd need to be set. + */ +int conn_read(xfrd_tcp_t* conn); +/* + * Write to a stream connection (size16)+packet. + * return value is + * -1 on error. 0 on short write, call back later. 1 completed write. + * On first call, make sure total_bytes=0, msglen=buffer_limit(), + * buffer_flipped(). packet and fd need to be set. + */ +int conn_write(xfrd_tcp_t* conn); + +/* setup DNS packet for a query of this type */ +void xfrd_setup_packet(struct buffer* packet, + uint16_t type, uint16_t klass, const struct dname* dname); +/* write soa in network format to the packet buffer */ +void xfrd_write_soa_buffer(struct buffer* packet, + const struct dname* apex, struct xfrd_soa* soa); +/* use acl address to setup sockaddr struct, returns length of addr. */ +socklen_t xfrd_acl_sockaddr_to(struct acl_options* acl, +#ifdef INET6 + struct sockaddr_storage *to); +#else + struct sockaddr_in *to); +#endif /* INET6 */ + +socklen_t xfrd_acl_sockaddr_frm(struct acl_options* acl, +#ifdef INET6 + struct sockaddr_storage *frm); +#else + struct sockaddr_in *frm); +#endif /* INET6 */ + +#endif /* XFRD_TCP_H */ diff --git a/usr.sbin/nsd/xfrd.c b/usr.sbin/nsd/xfrd.c new file mode 100644 index 00000000000..f4ed69282ca --- /dev/null +++ b/usr.sbin/nsd/xfrd.c @@ -0,0 +1,1684 @@ +/* + * xfrd.c - XFR (transfer) Daemon source file. Coordinates SOA updates. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> +#include <assert.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include "xfrd.h" +#include "xfrd-tcp.h" +#include "xfrd-disk.h" +#include "xfrd-notify.h" +#include "options.h" +#include "util.h" +#include "netio.h" +#include "region-allocator.h" +#include "nsd.h" +#include "packet.h" +#include "difffile.h" +#include "ipc.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 */ +#define XFRD_UDP_TIMEOUT 10 /* seconds, before a udp request times out */ +#define XFRD_NO_IXFR_CACHE 172800 /* 48h before retrying ixfr's after notimpl */ +#define XFRD_LOWERBOUND_REFRESH 1 /* seconds, smallest refresh timeout */ +#define XFRD_LOWERBOUND_RETRY 1 /* seconds, smallest retry timeout */ +#define XFRD_MAX_ROUNDS 3 /* max number of rounds along the masters */ +#define XFRD_TSIG_MAX_UNSIGNED 103 /* max number of packets without tsig in a tcp stream. */ + /* rfc recommends 100, +3 for offbyone errors/interoperability. */ + +/* the daemon state */ +xfrd_state_t* xfrd = 0; + +/* main xfrd loop */ +static void xfrd_main(); +/* shut down xfrd, close sockets. */ +static void xfrd_shutdown(); +/* create zone rbtree at start */ +static void xfrd_init_zones(); +/* free up memory used by main database */ +static void xfrd_free_namedb(); + +/* 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); + +/* call with buffer just after the soa dname. returns 0 on error. */ +static int xfrd_parse_soa_info(buffer_type* packet, xfrd_soa_t* soa); +/* set the zone state to a new state (takes care of expiry messages) */ +static void xfrd_set_zone_state(xfrd_zone_t* zone, enum xfrd_zone_state new_zone_state); +/* set timer for retry amount (depends on zone_state) */ +static void xfrd_set_timer_retry(xfrd_zone_t* zone); +/* set timer for refresh timeout (depends on zone_state) */ +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); + +/* send expiry notifications to nsd */ +static void xfrd_send_expire_notification(xfrd_zone_t* zone); +/* send ixfr request, returns fd of connection to read on */ +static int xfrd_send_ixfr_request_udp(xfrd_zone_t* zone); +/* obtain udp socket slot */ +static void xfrd_udp_obtain(xfrd_zone_t* zone); + +/* read data via udp */ +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); + +void +xfrd_init(int socket, struct nsd* nsd) +{ + region_type* region; + + assert(xfrd == 0); + /* to setup signalhandling */ + nsd->server_kind = NSD_SERVER_BOTH; + + region = region_create(xalloc, free); + 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->nsd = nsd; + xfrd->packet = buffer_create(xfrd->region, QIOBUFSZ); + xfrd->udp_waiting_first = NULL; + xfrd->udp_waiting_last = NULL; + xfrd->udp_use_num = 0; + 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->reload_timeout.tv_sec = 0; + xfrd->reload_cmd_last_sent = xfrd->xfrd_start_time; + xfrd->can_send_reload = 1; + + 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); + /* 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->need_to_send_reload = 0; + xfrd->sending_zone_state = 0; + xfrd->dirty_zones = stack_create(xfrd->region, + nsd_options_num_zones(nsd->options)); + + xfrd->notify_waiting_first = NULL; + xfrd->notify_waiting_last = NULL; + xfrd->notify_udp_num = 0; + + xfrd->tcp_set = xfrd_tcp_set_create(xfrd->region); + xfrd->tcp_set->tcp_timeout = nsd->tcp_timeout; + srandom((unsigned long) getpid() * (unsigned long) time(NULL)); + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd pre-startup")); + diff_snip_garbage(nsd->db, nsd->options); + xfrd_init_zones(); + xfrd_free_namedb(); + 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->shutdown = 0; + while(!xfrd->shutdown) + { + /* dispatch may block for a longer period, so current is gone */ + xfrd->got_time = 0; + if(netio_dispatch(xfrd->netio, NULL, 0) == -1) { + if (errno != EINTR) { + log_msg(LOG_ERR, + "xfrd netio_dispatch failed: %s", + strerror(errno)); + } + } + if(xfrd->nsd->signal_hint_quit || xfrd->nsd->signal_hint_shutdown) + xfrd->shutdown = 1; + } + xfrd_shutdown(); +} + +static void +xfrd_shutdown() +{ + xfrd_zone_t* zone; + int i; + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd shutdown")); + 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; + } + } + /* close udp 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; + } + close_notify_fds(xfrd->notify_zones); + } + + /* shouldn't we clean up memory used by xfrd process */ + + exit(0); +} + +static void +xfrd_init_zones() +{ + zone_type *dbzone; + zone_options_t *zone_opt; + xfrd_zone_t *xzone; + const dname_type* dname; + + assert(xfrd->zones == 0); + assert(xfrd->nsd->db != 0); + + xfrd->zones = rbtree_create(xfrd->region, + (int (*)(const void *, const void *)) dname_compare); + xfrd->notify_zones = rbtree_create(xfrd->region, + (int (*)(const void *, const void *)) dname_compare); + + 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; + } + + 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); + 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)); + continue; + } + + 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; + +#ifdef TSIG + tsig_create_record_custom(&xzone->tsig, xfrd->region, 0, 0, 4); +#endif /* TSIG */ + + 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); + + xzone->node.key = dname; + rbtree_insert(xfrd->zones, (rbnode_t*)xzone); + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: started server %d secondary zones", (int)xfrd->zones->count)); +} + +void +xfrd_send_expy_all_zones() +{ + xfrd_zone_t* zone; + RBTREE_FOR(zone, xfrd_zone_t*, xfrd->zones) + { + xfrd_send_expire_notification(zone); + } +} + +void +xfrd_reopen_logfile() +{ + if (xfrd->nsd->file_rotation_ok) + log_reopen(xfrd->nsd->log_filename, 0); +} + +static void +xfrd_free_namedb() +{ + namedb_close(xfrd->nsd->db); + xfrd->nsd->db = 0; +} + +static void +xfrd_set_timer_refresh(xfrd_zone_t* zone) +{ + time_t set_refresh; + time_t set_expire; + time_t set_min; + time_t set; + if(zone->soa_disk_acquired == 0 || zone->state != xfrd_zone_ok) { + xfrd_set_timer_retry(zone); + return; + } + /* refresh or expire timeout, whichever is earlier */ + set_refresh = zone->soa_disk_acquired + ntohl(zone->soa_disk.refresh); + set_expire = zone->soa_disk_acquired + ntohl(zone->soa_disk.expire); + if(set_refresh < set_expire) + set = set_refresh; + else set = set_expire; + set_min = zone->soa_disk_acquired + XFRD_LOWERBOUND_REFRESH; + if(set < set_min) + set = set_min; + xfrd_set_timer(zone, set); +} + +static void +xfrd_set_timer_retry(xfrd_zone_t* zone) +{ + /* set timer for next retry or expire timeout if earlier. */ + if(zone->soa_disk_acquired == 0) { + /* if no information, use reasonable timeout */ + xfrd_set_timer(zone, xfrd_time() + zone->fresh_xfr_timeout + + random()%zone->fresh_xfr_timeout); + /* exponential backoff - some master data in zones is paid-for + but non-working, and will not get fixed. */ + zone->fresh_xfr_timeout *= 2; + if(zone->fresh_xfr_timeout > XFRD_TRANSFER_TIMEOUT_MAX) + zone->fresh_xfr_timeout = XFRD_TRANSFER_TIMEOUT_MAX; + } else if(zone->state == xfrd_zone_expired || + xfrd_time() + ntohl(zone->soa_disk.retry) < + zone->soa_disk_acquired + ntohl(zone->soa_disk.expire)) + { + if(ntohl(zone->soa_disk.retry) < XFRD_LOWERBOUND_RETRY) + xfrd_set_timer(zone, xfrd_time() + XFRD_LOWERBOUND_RETRY); + else + xfrd_set_timer(zone, xfrd_time() + 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)); + } +} + +static void +xfrd_handle_zone(netio_type* ATTR_UNUSED(netio), + netio_handler_type *handler, netio_event_types_type event_types) +{ + xfrd_zone_t* zone = (xfrd_zone_t*)handler->user_data; + + 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); + 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; + } + } + + if(event_types & NETIO_EVENT_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); + xfrd_udp_read(zone); + return; + } + + /* timeout */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s timeout", zone->apex_str)); + if(handler->fd != -1) { + assert(zone->tcp_conn == -1); + xfrd_udp_release(zone); + } + + if(zone->tcp_waiting) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s skips retry, TCP connections full", + zone->apex_str)); + xfrd_unset_timer(zone); + return; + } + if(zone->udp_waiting) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s skips retry, UDP connections full", + zone->apex_str)); + xfrd_unset_timer(zone); + return; + } + + if(zone->soa_disk_acquired) + { + if (zone->state != xfrd_zone_expired && + (uint32_t)xfrd_time() >= zone->soa_disk_acquired + ntohl(zone->soa_disk.expire)) { + /* zone expired */ + log_msg(LOG_ERR, "xfrd: zone %s has expired", zone->apex_str); + xfrd_set_zone_state(zone, xfrd_zone_expired); + } + else if(zone->state == xfrd_zone_ok && + (uint32_t)xfrd_time() >= zone->soa_disk_acquired + ntohl(zone->soa_disk.refresh)) { + /* zone goes to refreshing state. */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s is refreshing", zone->apex_str)); + xfrd_set_zone_state(zone, xfrd_zone_refreshing); + } + } + /* make a new request */ + xfrd_make_request(zone); +} + +void +xfrd_make_request(xfrd_zone_t* zone) +{ + if(zone->next_master != -1) { + /* we are told to use this next master */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "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); + /* if there is no next master, fallback to use the first one */ + if(!zone->master) { + zone->master = zone->zone_options->request_xfr; + zone->master_num = 0; + } + /* fallback to cycle master */ + zone->next_master = -1; + zone->round_num = 0; /* fresh set of retries after notify */ + } else { + /* cycle master */ + + if(zone->round_num != -1 && zone->master && zone->master->next) + { + /* try the next master */ + zone->master = zone->master->next; + zone->master_num++; + } else { + /* start a new round */ + zone->master = zone->zone_options->request_xfr; + zone->master_num = 0; + zone->round_num++; + } + if(zone->round_num >= XFRD_MAX_ROUNDS) { + /* tried all servers that many times, wait */ + zone->round_num = -1; + xfrd_set_timer_retry(zone); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd zone %s makereq wait_retry, rd %d mr %d nx %d", + zone->apex_str, zone->round_num, zone->master_num, zone->next_master)); + return; + } + } + + /* cache ixfr_disabled only for XFRD_NO_IXFR_CACHE time */ + if (zone->master->ixfr_disabled && + (zone->master->ixfr_disabled + XFRD_NO_IXFR_CACHE) <= time(NULL)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "clear negative caching ixfr " + "disabled for master %s num " + "%d ", + zone->master->ip_address_spec, zone->master_num)); + zone->master->ixfr_disabled = 0; + } + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s make request round %d mr %d nx %d", + zone->apex_str, zone->round_num, zone->master_num, zone->next_master)); + /* perform xfr request */ + if (!zone->master->use_axfr_only && zone->soa_disk_acquired > 0 && + !zone->master->ixfr_disabled) { + + if (zone->master->allow_udp) { + xfrd_set_timer(zone, xfrd_time() + 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_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_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); + xfrd_tcp_obtain(xfrd->tcp_set, zone); + } + 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)); + } +} + +static void +xfrd_udp_obtain(xfrd_zone_t* zone) +{ + assert(zone->udp_waiting == 0); + if(zone->tcp_conn != -1) { + /* no tcp and udp at the same time */ + xfrd_tcp_release(xfrd->tcp_set, zone); + } + if(xfrd->udp_use_num < XFRD_MAX_UDP) { + xfrd->udp_use_num++; + zone->zone_handler.fd = xfrd_send_ixfr_request_udp(zone); + if(zone->zone_handler.fd == -1) + xfrd->udp_use_num--; + return; + } + /* queue the zone as last */ + zone->udp_waiting = 1; + zone->udp_waiting_next = NULL; + if(!xfrd->udp_waiting_first) + xfrd->udp_waiting_first = zone; + if(xfrd->udp_waiting_last) + xfrd->udp_waiting_last->udp_waiting_next = zone; + xfrd->udp_waiting_last = zone; + xfrd_unset_timer(zone); +} + +time_t +xfrd_time() +{ + if(!xfrd->got_time) { + xfrd->current_time = time(0); + xfrd->got_time = 1; + } + return xfrd->current_time; +} + +void +xfrd_copy_soa(xfrd_soa_t* soa, rr_type* rr) +{ + const uint8_t* rr_ns_wire = dname_name(domain_dname(rdata_atom_domain(rr->rdatas[0]))); + uint8_t rr_ns_len = domain_dname(rdata_atom_domain(rr->rdatas[0]))->name_size; + const uint8_t* rr_em_wire = dname_name(domain_dname(rdata_atom_domain(rr->rdatas[1]))); + uint8_t rr_em_len = domain_dname(rdata_atom_domain(rr->rdatas[1]))->name_size; + + if(rr->type != TYPE_SOA || rr->rdata_count != 7) { + log_msg(LOG_ERR, "xfrd: copy_soa called with bad rr, type %d rrs %u.", + rr->type, rr->rdata_count); + return; + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: copy_soa rr, type %d rrs %u, ttl %u.", + rr->type, rr->rdata_count, rr->ttl)); + soa->type = htons(rr->type); + soa->klass = htons(rr->klass); + soa->ttl = htonl(rr->ttl); + soa->rdata_count = htons(rr->rdata_count); + + /* copy dnames */ + soa->prim_ns[0] = rr_ns_len; + memcpy(soa->prim_ns+1, rr_ns_wire, rr_ns_len); + soa->email[0] = rr_em_len; + memcpy(soa->email+1, rr_em_wire, rr_em_len); + + /* already in network format */ + memcpy(&soa->serial, rdata_atom_data(rr->rdatas[2]), sizeof(uint32_t)); + memcpy(&soa->refresh, rdata_atom_data(rr->rdatas[3]), sizeof(uint32_t)); + memcpy(&soa->retry, rdata_atom_data(rr->rdatas[4]), sizeof(uint32_t)); + memcpy(&soa->expire, rdata_atom_data(rr->rdatas[5]), sizeof(uint32_t)); + 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))); +} + +static void +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) { + xfrd_send_expire_notification(zone); + } + } +} + +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", + zone->apex_str, zone->state)); +} + +void +xfrd_unset_timer(xfrd_zone_t* zone) +{ + zone->zone_handler.timeout = NULL; +} + +void +xfrd_set_timer(xfrd_zone_t* zone, time_t t) +{ + /* 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; + t = xfrd_time() + base + random()%(extra-base); + } + + zone->zone_handler.timeout = &zone->timeout; + zone->timeout.tv_sec = t; + zone->timeout.tv_nsec = 0; +} + +void +xfrd_handle_incoming_soa(xfrd_zone_t* zone, + xfrd_soa_t* soa, time_t acquired) +{ + if(soa == NULL) { + /* nsd no longer has a zone in memory */ + zone->soa_nsd_acquired = 0; + xfrd_set_zone_state(zone, xfrd_zone_refreshing); + xfrd_set_refresh_now(zone); + return; + } + 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 */ + log_msg(LOG_INFO, "Zone %s serial %u is updated to %u.", + zone->apex_str, ntohl(zone->soa_nsd.serial), + ntohl(soa->serial)); + zone->soa_nsd = zone->soa_disk; + zone->soa_nsd_acquired = zone->soa_disk_acquired; + if((uint32_t)xfrd_time() - zone->soa_disk_acquired + < ntohl(zone->soa_disk.refresh)) + { + /* zone ok, wait for refresh time */ + xfrd_set_zone_state(zone, xfrd_zone_ok); + zone->round_num = -1; + xfrd_set_timer_refresh(zone); + } else if((uint32_t)xfrd_time() - zone->soa_disk_acquired + < ntohl(zone->soa_disk.expire)) + { + /* zone refreshing */ + xfrd_set_zone_state(zone, xfrd_zone_refreshing); + xfrd_set_refresh_now(zone); + } + if((uint32_t)xfrd_time() - zone->soa_disk_acquired + >= ntohl(zone->soa_disk.expire)) { + /* zone expired */ + xfrd_set_zone_state(zone, xfrd_zone_expired); + xfrd_set_refresh_now(zone); + } + + if(zone->soa_notified_acquired != 0 && + (zone->soa_notified.serial == 0 || + compare_serial(ntohl(zone->soa_disk.serial), + ntohl(zone->soa_notified.serial)) >= 0)) + { /* read was in response to this notification */ + zone->soa_notified_acquired = 0; + } + if(zone->soa_notified_acquired && zone->state == xfrd_zone_ok) + { + /* refresh because of notification */ + xfrd_set_zone_state(zone, xfrd_zone_refreshing); + xfrd_set_refresh_now(zone); + } + xfrd_send_notify(xfrd->notify_zones, zone->apex, &zone->soa_nsd); + return; + } + + /* user must have manually provided zone data */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s serial %u from unknown source. refreshing", + zone->apex_str, ntohl(soa->serial))); + zone->soa_nsd = *soa; + zone->soa_disk = *soa; + zone->soa_nsd_acquired = acquired; + zone->soa_disk_acquired = acquired; + if(zone->soa_notified_acquired != 0 && + (zone->soa_notified.serial == 0 || + compare_serial(ntohl(zone->soa_disk.serial), + ntohl(zone->soa_notified.serial)) >= 0)) + { /* user provided in response to this notification */ + zone->soa_notified_acquired = 0; + } + xfrd_set_zone_state(zone, xfrd_zone_refreshing); + xfrd_set_refresh_now(zone); + xfrd_send_notify(xfrd->notify_zones, zone->apex, &zone->soa_nsd); +} + +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; +} + +int +xfrd_udp_read_packet(buffer_type* packet, int fd) +{ + ssize_t received; + + /* read the data */ + buffer_clear(packet); + received = recvfrom(fd, buffer_begin(packet), buffer_remaining(packet), + 0, NULL, NULL); + if(received == -1) { + log_msg(LOG_ERR, "xfrd: recvfrom failed: %s", + strerror(errno)); + return 0; + } + buffer_set_limit(packet, received); + return 1; +} + +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; + /* see if there are waiting zones */ + if(xfrd->udp_use_num == XFRD_MAX_UDP) + { + while(xfrd->udp_waiting_first) { + /* snip off waiting list */ + xfrd_zone_t* wz = xfrd->udp_waiting_first; + assert(wz->udp_waiting); + wz->udp_waiting = 0; + xfrd->udp_waiting_first = wz->udp_waiting_next; + 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) + return; + } + } + } + /* no waiting zones */ + if(xfrd->udp_use_num > 0) + xfrd->udp_use_num--; +} + +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)) { + 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_udp_release(zone); + xfrd_tcp_obtain(xfrd->tcp_set, zone); + break; + case xfrd_packet_transfer: + case xfrd_packet_newlease: + /* nothing more to do */ + assert(zone->round_num == -1); + xfrd_udp_release(zone); + break; + case xfrd_packet_notimpl: + zone->master->ixfr_disabled = time(NULL); + /* drop packet */ + xfrd_udp_release(zone); + /* query next server */ + xfrd_make_request(zone); + break; + case xfrd_packet_more: + case xfrd_packet_bad: + default: + /* drop packet */ + xfrd_udp_release(zone); + /* query next server */ + xfrd_make_request(zone); + break; + } +} + +int +xfrd_send_udp(acl_options_t* acl, buffer_type* packet, acl_options_t* ifc) +{ +#ifdef INET6 + struct sockaddr_storage to; +#else + struct sockaddr_in to; +#endif /* INET6 */ + int fd, family; + + /* this will set the remote port to acl->port or TCP_PORT */ + socklen_t to_len = xfrd_acl_sockaddr_to(acl, &to); + + /* get the address family of the remote host */ + if(acl->is_ipv6) { +#ifdef INET6 + family = PF_INET6; +#else + return -1; +#endif /* INET6 */ + } else { + family = PF_INET; + } + + fd = socket(family, SOCK_DGRAM, IPPROTO_UDP); + if(fd == -1) { + log_msg(LOG_ERR, "xfrd: cannot create udp socket to %s: %s", + acl->ip_address_spec, strerror(errno)); + return -1; + } + + /* bind it */ + if (!xfrd_bind_local_interface(fd, ifc, acl, 0)) { + log_msg(LOG_ERR, "xfrd: cannot bind outgoing interface '%s' to " + "udp socket: No matching ip addresses found", + ifc->ip_address_spec); + return -1; + } + + /* send it (udp) */ + if(sendto(fd, + buffer_current(packet), + buffer_remaining(packet), 0, + (struct sockaddr*)&to, to_len) == -1) + { + log_msg(LOG_ERR, "xfrd: sendto %s failed %s", + acl->ip_address_spec, strerror(errno)); + return -1; + } + return fd; +} + +int +xfrd_bind_local_interface(int sockd, acl_options_t* ifc, acl_options_t* acl, + int tcp) +{ + struct linger linger = {1, 0}; + socklen_t frm_len; +#ifdef INET6 + struct sockaddr_storage frm; +#else + struct sockaddr_in frm; +#endif /* INET6 */ + + if (!ifc) /* no outgoing interface set */ + return 1; + + while (ifc) { + if (ifc->is_ipv6 != acl->is_ipv6) { + /* check if we have a matching address family */ + ifc = ifc->next; + continue; + } + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: bind() %s to %s socket", + ifc->ip_address_spec, tcp? "tcp":"udp")); + + frm_len = xfrd_acl_sockaddr_frm(ifc, &frm); + + if (tcp) { +#ifdef SO_REUSEADDR + if (setsockopt(sockd, SOL_SOCKET, SO_REUSEADDR, &frm, + frm_len) < 0) { + VERBOSITY(2, (LOG_WARNING, "xfrd: setsockopt " + "SO_REUSEADDR failed: %s", strerror(errno))); + } +#else + VERBOSITY(2, (LOG_WARNING, "xfrd: setsockopt SO_REUSEADDR " + "failed: SO_REUSEADDR not defined")); +#endif /* SO_REUSEADDR */ + + if (ifc->port != 0) { +#ifdef SO_LINGER + if (setsockopt(sockd, SOL_SOCKET, SO_LINGER, + &linger, sizeof(linger)) < 0) { + VERBOSITY(2, (LOG_WARNING, "xfrd: setsockopt " + "SO_LINGER failed: %s", strerror(errno))); + } +#else + VERBOSITY(2, (LOG_WARNING, "xfrd: setsockopt SO_LINGER " + "failed: SO_LINGER not defined")); +#endif /* SO_LINGER */ + } + } + + /* found one */ + if(bind(sockd, (struct sockaddr*)&frm, frm_len) >= 0) { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfrd: bind() %s to %s " + "socket was successful", + ifc->ip_address_spec, tcp? "tcp":"udp")); + return 1; + } + + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfrd: bind() %s to %s socket" + "failed: %s", + ifc->ip_address_spec, tcp? "tcp":"udp", + strerror(errno))); + /* try another */ + ifc = ifc->next; + } + + log_msg(LOG_WARNING, "xfrd: could not bind source address:port to " + "socket: %s", strerror(errno)); + return 0; +} + +#ifdef TSIG +void +xfrd_tsig_sign_request(buffer_type* packet, tsig_record_type* tsig, + acl_options_t* acl) +{ + tsig_algorithm_type* algo; + assert(acl->key_options && acl->key_options->tsig_key); + algo = tsig_get_algorithm_by_name(acl->key_options->algorithm); + if(!algo) { + log_msg(LOG_ERR, "tsig unknown algorithm %s", + acl->key_options->algorithm); + return; + } + assert(algo); + tsig_init_record(tsig, algo, acl->key_options->tsig_key); + tsig_init_query(tsig, ID(packet)); + tsig_prepare(tsig); + tsig_update(tsig, packet, buffer_position(packet)); + tsig_sign(tsig); + tsig_append_rr(tsig, packet); + ARCOUNT_SET(packet, ARCOUNT(packet) + 1); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "appending tsig to packet")); + /* prepare for validating tsigs */ + tsig_prepare(tsig); +} +#endif + +static int +xfrd_send_ixfr_request_udp(xfrd_zone_t* zone) +{ + int fd; + + /* make sure we have a master to query the ixfr request to */ + assert(zone->master); + + if(zone->tcp_conn != -1) { + /* tcp is using the zone_handler.fd */ + log_msg(LOG_ERR, "xfrd: %s tried to send udp whilst tcp engaged", + zone->apex_str); + return -1; + } + xfrd_setup_packet(xfrd->packet, TYPE_IXFR, CLASS_IN, zone->apex); + zone->query_id = ID(xfrd->packet); + zone->msg_seq_nr = 0; + zone->msg_rr_count = 0; + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "sent query with ID %d", zone->query_id)); + NSCOUNT_SET(xfrd->packet, 1); + xfrd_write_soa_buffer(xfrd->packet, zone->apex, &zone->soa_disk); + /* if we have tsig keys, sign the ixfr query */ +#ifdef TSIG + if(zone->master->key_options && zone->master->key_options->tsig_key) { + xfrd_tsig_sign_request(xfrd->packet, &zone->tsig, zone->master); + } +#endif /* TSIG */ + buffer_flip(xfrd->packet); + xfrd_set_timer(zone, xfrd_time() + XFRD_UDP_TIMEOUT); + + if((fd = xfrd_send_udp(zone->master, xfrd->packet, + zone->zone_options->outgoing_interface)) == -1) + return -1; + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd sent udp request for ixfr=%u for zone %s to %s", + ntohl(zone->soa_disk.serial), + zone->apex_str, zone->master->ip_address_spec)); + return fd; +} + +static int xfrd_parse_soa_info(buffer_type* packet, xfrd_soa_t* soa) +{ + if(!buffer_available(packet, 10)) + return 0; + soa->type = htons(buffer_read_u16(packet)); + soa->klass = htons(buffer_read_u16(packet)); + soa->ttl = htonl(buffer_read_u32(packet)); + if(ntohs(soa->type) != TYPE_SOA || ntohs(soa->klass) != CLASS_IN) + { + return 0; + } + + if(!buffer_available(packet, buffer_read_u16(packet)) /* rdata length */ || + !(soa->prim_ns[0] = dname_make_wire_from_packet(soa->prim_ns+1, packet, 1)) || + !(soa->email[0] = dname_make_wire_from_packet(soa->email+1, packet, 1))) + { + return 0; + } + soa->serial = htonl(buffer_read_u32(packet)); + soa->refresh = htonl(buffer_read_u32(packet)); + soa->retry = htonl(buffer_read_u32(packet)); + soa->expire = htonl(buffer_read_u32(packet)); + soa->minimum = htonl(buffer_read_u32(packet)); + + return 1; +} + + +/* + * Check the RRs in an IXFR/AXFR reply. + * returns 0 on error, 1 on correct parseable packet. + * done = 1 if the last SOA in an IXFR/AXFR has been seen. + * soa then contains that soa info. + * (soa contents is modified by the routine) + */ +static int +xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, + int *done, xfrd_soa_t* soa) +{ + /* first RR has already been checked */ + uint16_t type, klass, rrlen; + uint32_t ttl; + size_t i, soapos; + for(i=0; i<count; ++i,++zone->msg_rr_count) + { + if(!packet_skip_dname(packet)) + return 0; + if(!buffer_available(packet, 10)) + return 0; + soapos = buffer_position(packet); + type = buffer_read_u16(packet); + klass = buffer_read_u16(packet); + ttl = buffer_read_u32(packet); + rrlen = buffer_read_u16(packet); + if(!buffer_available(packet, rrlen)) + 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)) + return 0; + if(zone->msg_rr_count == 1 && + ntohl(soa->serial) != zone->msg_new_serial) { + /* 2nd RR is SOA with lower serial, this is an IXFR */ + zone->msg_is_ixfr = 1; + if(!zone->soa_disk_acquired) + return 0; /* got IXFR but need AXFR */ + if(ntohl(soa->serial) != ntohl(zone->soa_disk.serial)) + return 0; /* bad start serial in IXFR */ + zone->msg_old_serial = ntohl(soa->serial); + } + else if(ntohl(soa->serial) == zone->msg_new_serial) { + /* saw another SOA of new serial. */ + if(zone->msg_is_ixfr == 1) { + zone->msg_is_ixfr = 2; /* seen middle SOA in ixfr */ + } else { + /* 2nd SOA for AXFR or 3rd newSOA for IXFR */ + *done = 1; + } + } + buffer_set_position(packet, mempos); + } + buffer_skip(packet, rrlen); + } + /* packet seems to have a valid DNS RR structure */ + return 1; +} + +#ifdef TSIG +static int +xfrd_xfr_process_tsig(xfrd_zone_t* zone, buffer_type* packet) +{ + int have_tsig = 0; + assert(zone && zone->master && zone->master->key_options + && zone->master->key_options->tsig_key && packet); + if(!tsig_find_rr(&zone->tsig, packet)) { + log_msg(LOG_ERR, "xfrd: zone %s, from %s: malformed tsig RR", + zone->apex_str, zone->master->ip_address_spec); + return 0; + } + if(zone->tsig.status == TSIG_OK) { + have_tsig = 1; + } + if(have_tsig) { + /* strip the TSIG resource record off... */ + buffer_set_limit(packet, zone->tsig.position); + ARCOUNT_SET(packet, ARCOUNT(packet) - 1); + } + + /* keep running the TSIG hash */ + tsig_update(&zone->tsig, packet, buffer_limit(packet)); + if(have_tsig) { + if (!tsig_verify(&zone->tsig)) { + log_msg(LOG_ERR, "xfrd: zone %s, from %s: bad tsig signature", + zone->apex_str, zone->master->ip_address_spec); + return 0; + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s, from %s: good tsig signature", + zone->apex_str, zone->master->ip_address_spec)); + /* prepare for next tsigs */ + tsig_prepare(&zone->tsig); + } + else if(zone->tsig.updates_since_last_prepare > XFRD_TSIG_MAX_UNSIGNED) { + /* we allow a number of non-tsig signed packets */ + log_msg(LOG_INFO, "xfrd: zone %s, from %s: too many consecutive " + "packets without TSIG", zone->apex_str, + zone->master->ip_address_spec); + return 0; + } + + if(!have_tsig && zone->msg_seq_nr == 0) { + log_msg(LOG_ERR, "xfrd: zone %s, from %s: no tsig in first packet of reply", + zone->apex_str, zone->master->ip_address_spec); + return 0; + } + return 1; +} +#endif + +/* parse the received packet. returns xfrd packet result code. */ +static enum xfrd_packet_result +xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, + xfrd_soa_t* soa) +{ + size_t rr_count; + size_t qdcount = QDCOUNT(packet); + size_t ancount = ANCOUNT(packet), ancount_todo; + int done = 0; + + /* has to be axfr / ixfr reply */ + if(!buffer_available(packet, QHEADERSZ)) { + log_msg(LOG_INFO, "packet too small"); + return xfrd_packet_bad; + } + + /* only check ID in first response message. Could also check that + * AA bit and QR bit are set, but not needed. + */ + DEBUG(DEBUG_XFRD,2, (LOG_INFO, + "got query with ID %d and %d needed", ID(packet), zone->query_id)); + if(ID(packet) != zone->query_id) { + log_msg(LOG_ERR, "xfrd: zone %s received bad query id from %s, " + "dropped", + zone->apex_str, zone->master->ip_address_spec); + return xfrd_packet_bad; + } + /* check RCODE in all response messages */ + if(RCODE(packet) != RCODE_OK) { + log_msg(LOG_ERR, "xfrd: zone %s received error code %s from " + "%s", + zone->apex_str, rcode2str(RCODE(packet)), + zone->master->ip_address_spec); + if (RCODE(packet) == RCODE_IMPL || + RCODE(packet) == RCODE_FORMAT) { + return xfrd_packet_notimpl; + } + return xfrd_packet_bad; + } +#ifdef TSIG + /* check TSIG */ + if(zone->master->key_options) { + if(!xfrd_xfr_process_tsig(zone, packet)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "dropping xfr reply due " + "to bad TSIG")); + return xfrd_packet_bad; + } + } +#endif + buffer_skip(packet, QHEADERSZ); + + /* skip question section */ + for(rr_count = 0; rr_count < qdcount; ++rr_count) { + if (!packet_skip_rr(packet, 1)) { + log_msg(LOG_ERR, "xfrd: zone %s, from %s: bad RR in " + "question section", + zone->apex_str, zone->master->ip_address_spec); + return xfrd_packet_bad; + } + } + if(zone->msg_rr_count == 0 && ancount == 0) { + if(zone->tcp_conn == -1 && TC(packet)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: TC flagged")); + return xfrd_packet_tcp; + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: too short xfr packet: no " + "answer")); + return xfrd_packet_bad; + } + ancount_todo = ancount; + + if(zone->msg_rr_count == 0) { + /* parse the first RR, see if it is a SOA */ + if(!packet_skip_dname(packet) || + !xfrd_parse_soa_info(packet, soa)) + { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s, from %s: " + "no SOA begins answer" + " section", + zone->apex_str, zone->master->ip_address_spec)); + return xfrd_packet_bad; + } + if(zone->soa_disk_acquired != 0 && + zone->state != xfrd_zone_expired /* if expired - accept anything */ && + compare_serial(ntohl(soa->serial), ntohl(zone->soa_disk.serial)) < 0) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s ignoring old serial from %s", + zone->apex_str, zone->master->ip_address_spec)); + VERBOSITY(1, (LOG_INFO, + "xfrd: zone %s ignoring old serial from %s", + zone->apex_str, zone->master->ip_address_spec)); + return xfrd_packet_bad; + } + if(zone->soa_disk_acquired != 0 && zone->soa_disk.serial == soa->serial) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s got " + "update indicating " + "current serial", + zone->apex_str)); + /* (even if notified) the lease on the current soa is renewed */ + zone->soa_disk_acquired = xfrd_time(); + if(zone->soa_nsd.serial == soa->serial) + zone->soa_nsd_acquired = xfrd_time(); + xfrd_set_zone_state(zone, xfrd_zone_ok); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s is ok", + zone->apex_str)); + if(zone->soa_notified_acquired == 0) { + /* not notified or anything, so stop asking around */ + zone->round_num = -1; /* next try start a new round */ + xfrd_set_timer_refresh(zone); + return xfrd_packet_newlease; + } + /* try next master */ + return xfrd_packet_bad; + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "IXFR reply has ok serial (have \ +%u, reply %u).", ntohl(zone->soa_disk.serial), ntohl(soa->serial))); + /* serial is newer than soa_disk */ + if(ancount == 1) { + /* single record means it is like a notify */ + (void)xfrd_handle_incoming_notify(zone, soa); + } + else if(zone->soa_notified_acquired && zone->soa_notified.serial && + compare_serial(ntohl(zone->soa_notified.serial), ntohl(soa->serial)) < 0) { + /* this AXFR/IXFR notifies me that an even newer serial exists */ + zone->soa_notified.serial = soa->serial; + } + zone->msg_new_serial = ntohl(soa->serial); + zone->msg_rr_count = 1; + zone->msg_is_ixfr = 0; + if(zone->soa_disk_acquired) + zone->msg_old_serial = ntohl(zone->soa_disk.serial); + else zone->msg_old_serial = 0; + ancount_todo = ancount - 1; + } + + if(zone->tcp_conn == -1 && TC(packet)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s received TC from %s. retry tcp.", + zone->apex_str, zone->master->ip_address_spec)); + return xfrd_packet_tcp; + } + + if(zone->tcp_conn == -1 && ancount < 2) { + /* too short to be a real ixfr/axfr data transfer: need at */ + /* least two RRs in the answer section. */ + /* The serial is newer, so try tcp to this master. */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: udp reply is short. Try " + "tcp anyway.")); + return xfrd_packet_tcp; + } + + if(!xfrd_xfr_check_rrs(zone, packet, ancount_todo, &done, soa)) + { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s sent bad xfr " + "reply.", zone->apex_str)); + return xfrd_packet_bad; + } + if(zone->tcp_conn == -1 && done == 0) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: udp reply incomplete")); + return xfrd_packet_bad; + } + if(done == 0) + return xfrd_packet_more; +#ifdef TSIG + if(zone->master->key_options) { + if(zone->tsig.updates_since_last_prepare != 0) { + log_msg(LOG_INFO, "xfrd: last packet of reply has no " + "TSIG"); + return xfrd_packet_bad; + } + } +#endif /* TSIG */ + return xfrd_packet_transfer; +} + +enum xfrd_packet_result +xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) +{ + xfrd_soa_t soa; + enum xfrd_packet_result res; + + /* parse and check the packet - see if it ends the xfr */ + switch((res=xfrd_parse_received_xfr_packet(zone, packet, &soa))) + { + case xfrd_packet_more: + case xfrd_packet_transfer: + /* continue with commit */ + break; + case xfrd_packet_newlease: + return xfrd_packet_newlease; + case xfrd_packet_tcp: + return xfrd_packet_tcp; + case xfrd_packet_notimpl: + case xfrd_packet_bad: + default: + { + /* 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 %u from %s of %u " + "parts", + zone->apex_str, + (int)zone->msg_new_serial, + (int)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))); + } + if (res == xfrd_packet_notimpl) + return res; + else + return xfrd_packet_bad; + } + } + + /* 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 " + "disk", zone->apex_str, zone->master->ip_address_spec, + (int)zone->msg_new_serial)); + zone->msg_seq_nr++; + if(res == xfrd_packet_more) { + /* wait for more */ + return xfrd_packet_more; + } + + /* done. we are completely sure of this */ + buffer_clear(packet); + buffer_printf(packet, "xfrd: zone %s received update to serial %u at " + "time %u from %s in %u parts", + zone->apex_str, (int)zone->msg_new_serial, (int)xfrd_time(), + zone->master->ip_address_spec, zone->msg_seq_nr); +#ifdef TSIG + if(zone->master->key_options) { + buffer_printf(packet, " TSIG verified with key %s", + zone->master->key_options->name); + } +#endif /* TSIG */ + 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); + VERBOSITY(1, (LOG_INFO, "xfrd: zone %s committed \"%s\"", + zone->apex_str, (char*)buffer_begin(packet))); + /* update the disk serial no. */ + zone->soa_disk_acquired = xfrd_time(); + zone->soa_disk = soa; + if(zone->soa_notified_acquired && ( + zone->soa_notified.serial == 0 || + compare_serial(htonl(zone->soa_disk.serial), + htonl(zone->soa_notified.serial)) >= 0)) + { + zone->soa_notified_acquired = 0; + } + if(!zone->soa_notified_acquired) { + /* do not set expired zone to ok: + * it would cause nsd to start answering + * bad data, since the zone is not loaded yet. + * if nsd does not reload < retry time, more + * queries (for even newer versions) are made. + * For expired zone after reload it is set ok (SOAINFO ipc). */ + if(zone->state != xfrd_zone_expired) + xfrd_set_zone_state(zone, xfrd_zone_ok); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s is waiting for reload", + zone->apex_str)); + zone->round_num = -1; /* next try start anew */ + xfrd_set_timer_refresh(zone); + xfrd_set_reload_timeout(); + return xfrd_packet_transfer; + } else { + /* try to get an even newer serial */ + /* pretend it was bad to continue queries */ + xfrd_set_reload_timeout(); + return xfrd_packet_bad; + } +} + +static void +xfrd_set_reload_timeout() +{ + if(xfrd->nsd->options->xfrd_reload_timeout == -1) + return; /* automatic reload disabled. */ + if(xfrd->reload_timeout.tv_sec == 0 || + xfrd_time() >= 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; + /* start reload wait period */ + xfrd->reload_timeout.tv_sec = xfrd_time() + + xfrd->nsd->options->xfrd_reload_timeout; + xfrd->reload_timeout.tv_nsec = 0; + return; + } + /* cannot reload now, set that after the timeout a reload has to happen */ + xfrd->reload_handler.timeout = &xfrd->reload_timeout; +} + +static void +xfrd_handle_reload(netio_type *ATTR_UNUSED(netio), + netio_handler_type *handler, netio_event_types_type event_types) +{ + /* reload timeout */ + assert(event_types & NETIO_EVENT_TIMEOUT); + /* timeout wait period after this request is sent */ + handler->timeout = NULL; + 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; +} + +void +xfrd_handle_passed_packet(buffer_type* packet, int acl_num) +{ + uint8_t qnamebuf[MAXDOMAINLEN]; + uint16_t qtype, qclass; + const dname_type* dname; + region_type* tempregion = region_create(xalloc, free); + xfrd_zone_t* zone; + + buffer_skip(packet, QHEADERSZ); + if(!packet_read_query_section(packet, qnamebuf, &qtype, &qclass)) { + region_destroy(tempregion); + return; /* drop bad 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)); + + /* 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)); + region_destroy(tempregion); + return; /* drop packet for unknown zone */ + } + region_destroy(tempregion); + + /* handle */ + if(OPCODE(packet) == OPCODE_NOTIFY) { + xfrd_soa_t soa; + int have_soa = 0; + int next; + /* get serial from a SOA */ + if(ANCOUNT(packet) == 1 && packet_skip_dname(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); + } + } + next = find_same_master_notify(zone, acl_num); + if(next != -1) { + zone->next_master = next; + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: notify set next master to query %d", + next)); + } + } + else { + /* TODO handle incoming IXFR udp reply via port 53 */ + } +} + +static int +xfrd_handle_incoming_notify(xfrd_zone_t* zone, xfrd_soa_t* soa) +{ + if(soa && zone->soa_disk_acquired && zone->state != xfrd_zone_expired && + compare_serial(ntohl(soa->serial),ntohl(zone->soa_disk.serial)) <= 0) + { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: ignored notify %s %u old serial, zone valid " + "(soa disk serial %u)", zone->apex_str, + ntohl(soa->serial), + ntohl(zone->soa_disk.serial))); + return 0; /* ignore notify with old serial, we have a valid zone */ + } + if(soa == 0) { + zone->soa_notified.serial = 0; + } + else if (zone->soa_notified_acquired == 0 || + zone->soa_notified.serial == 0 || + compare_serial(ntohl(soa->serial), + ntohl(zone->soa_notified.serial)) > 0) + { + zone->soa_notified = *soa; + } + zone->soa_notified_acquired = xfrd_time(); + if(zone->state == xfrd_zone_ok) { + xfrd_set_zone_state(zone, xfrd_zone_refreshing); + } + /* transfer right away */ + VERBOSITY(1, (LOG_INFO, "Handle incoming notify for zone %s", + zone->apex_str)); + return 1; +} + +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); + int num = 0; + acl_options_t* master = zone->zone_options->request_xfr; + if(!nfy_acl) + return -1; + while(master) + { + if(acl_same_host(nfy_acl, master)) + return num; + master = master->next; + num++; + } + return -1; +} + +void +xfrd_check_failed_updates() +{ + /* see if updates have not come through */ + xfrd_zone_t* zone; + RBTREE_FOR(zone, xfrd_zone_t*, xfrd->zones) + { + /* zone has a disk soa, and no nsd soa or a different nsd soa */ + if(zone->soa_disk_acquired != 0 && + (zone->soa_nsd_acquired == 0 || + zone->soa_disk.serial != zone->soa_nsd.serial)) + { + if(zone->soa_disk_acquired < + xfrd->reload_cmd_last_sent) + { + /* this zone should have been loaded, since its disk + 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, restarting " + "transfer (notified zone)", + zone->apex_str, 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; + /* pretend we are notified with disk soa. + This will cause a refetch of the data, and reload. */ + xfrd_handle_incoming_notify(zone, &dumped_soa); + xfrd_set_timer_refresh(zone); + } else if(zone->soa_disk_acquired >= xfrd->reload_cmd_last_sent) { + /* 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) { + log_msg(LOG_ERR, "xfrd: zone %s: needs " + "to be loaded. reload lost? " + "try again", zone->apex_str); + xfrd_set_reload_timeout(); + } + } + } + } +} + +void +xfrd_prepare_zones_for_reload() +{ + xfrd_zone_t* zone; + RBTREE_FOR(zone, xfrd_zone_t*, xfrd->zones) + { + /* zone has a disk soa, and no nsd soa or a different nsd soa */ + if(zone->soa_disk_acquired != 0 && + (zone->soa_nsd_acquired == 0 || + zone->soa_disk.serial != zone->soa_nsd.serial)) + { + if(zone->soa_disk_acquired == xfrd_time()) { + /* antedate by one second. + * this makes sure that the zone time is before + * reload, so that check_failed_zones() is + * certain of the result. + */ + zone->soa_disk_acquired--; + } + } + } +} + +struct buffer* +xfrd_get_temp_buffer() +{ + return xfrd->packet; +} diff --git a/usr.sbin/nsd/xfrd.h b/usr.sbin/nsd/xfrd.h new file mode 100644 index 00000000000..9b34322b67b --- /dev/null +++ b/usr.sbin/nsd/xfrd.h @@ -0,0 +1,295 @@ +/* + * xfrd.h - XFR (transfer) Daemon header file. Coordinates SOA updates. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef XFRD_H +#define XFRD_H + +#include <config.h> +#include "netio.h" +#include "rbtree.h" +#include "namedb.h" +#include "options.h" +#include "dns.h" +#include "tsig.h" + +struct nsd; +struct region; +struct buffer; +struct xfrd_tcp; +struct xfrd_tcp_set; +struct notify_zone_t; +typedef struct xfrd_state xfrd_state_t; +typedef struct xfrd_zone xfrd_zone_t; +typedef struct xfrd_soa xfrd_soa_t; +/* + * The global state for the xfrd daemon process. + * The time_t times are epochs in secs since 1970, absolute times. + */ +struct xfrd_state { + /* time when daemon was last started */ + time_t xfrd_start_time; + struct region* region; + netio_type* netio; + struct nsd* nsd; + + struct xfrd_tcp_set* tcp_set; + /* packet buffer for udp packets */ + struct buffer* packet; + /* udp waiting list */ + struct xfrd_zone *udp_waiting_first, *udp_waiting_last; + /* number of udp sockets (for sending queries) in use */ + size_t udp_use_num; + + /* current time is cached */ + uint8_t got_time; + time_t current_time; + + /* timer for NSD reload */ + struct timespec reload_timeout; + netio_handler_type reload_handler; + /* last reload must have caught all zone updates before this time */ + time_t reload_cmd_last_sent; + uint8_t can_send_reload; + + /* communication channel with server_main */ + netio_handler_type ipc_handler; + uint8_t ipc_is_soa; + uint8_t parent_soa_info_pass; + struct xfrd_tcp *ipc_conn; + struct buffer* ipc_pass; + /* sending ipc to server_main */ + struct xfrd_tcp *ipc_conn_write; + uint8_t need_to_send_reload; + uint8_t need_to_send_quit; + uint8_t sending_zone_state; + uint8_t ipc_send_blocked; + stack_type* dirty_zones; /* stack of xfrd_zone* */ + + /* xfrd shutdown flag */ + uint8_t shutdown; + + /* tree of zones, by apex name, contains xfrd_zone_t*. Only secondary zones. */ + rbtree_t *zones; + + /* tree of zones, by apex name, contains notify_zone_t*. All zones. */ + rbtree_t *notify_zones; + /* number of notify_zone_t active using UDP socket */ + int notify_udp_num; + /* first and last notify_zone_t* entries waiting for a UDP socket */ + struct notify_zone_t *notify_waiting_first, *notify_waiting_last; +}; + +/* + * XFR daemon SOA information kept in network format. + * This is in packet order. + */ +struct xfrd_soa { + /* name of RR is zone apex dname */ + uint16_t type; /* = TYPE_SOA */ + uint16_t klass; /* = CLASS_IN */ + uint32_t ttl; + uint16_t rdata_count; /* = 7 */ + /* format is 1 octet length, + wireformat dname. + one more octet since parse_dname_wire_from_packet needs it. + maximum size is allocated to avoid memory alloc/free. */ + uint8_t prim_ns[MAXDOMAINLEN + 2]; + uint8_t email[MAXDOMAINLEN + 2]; + uint32_t serial; + uint32_t refresh; + uint32_t retry; + uint32_t expire; + uint32_t minimum; +}; + + +/* + * XFRD state for a single zone + */ +struct xfrd_zone { + rbnode_t node; + + /* name of the zone */ + const dname_type* apex; + const char* apex_str; + + /* Three types of soas: + * NSD: in use by running server + * disk: stored on disk in db/diff file + * notified: from notification, could be available on a master. + * And the time the soa was acquired (start time for timeouts). + * If the time==0, no SOA is available. + */ + xfrd_soa_t soa_nsd; + time_t soa_nsd_acquired; + xfrd_soa_t soa_disk; + time_t soa_disk_acquired; + xfrd_soa_t soa_notified; + time_t soa_notified_acquired; + + enum xfrd_zone_state { + xfrd_zone_ok, + xfrd_zone_refreshing, + xfrd_zone_expired + } state; + + /* if state is dirty it needs to be sent to server_main. + * it is also on the dirty_stack. Not saved on disk. */ + uint8_t dirty; + + /* master to try to transfer from, number for persistence */ + acl_options_t* master; + int master_num; + int next_master; /* -1 or set by notify where to try next */ + /* round of xfrattempts, -1 is waiting for timeout */ + int round_num; + zone_options_t* zone_options; + int fresh_xfr_timeout; + + /* handler for timeouts */ + struct timespec timeout; + netio_handler_type zone_handler; + + /* tcp connection zone is using, or -1 */ + int tcp_conn; + /* zone is waiting for a tcp connection */ + uint8_t tcp_waiting; + /* next zone in waiting list */ + xfrd_zone_t* tcp_waiting_next; + /* zone is waiting for a udp connection (tcp is preferred) */ + uint8_t udp_waiting; + /* next zone in waiting list for UDP */ + xfrd_zone_t* udp_waiting_next; + + /* xfr message handling data */ + /* query id */ + uint16_t query_id; + uint32_t msg_seq_nr; /* number of messages already handled */ + uint32_t msg_old_serial, msg_new_serial; /* host byte order */ + size_t msg_rr_count; + uint8_t msg_is_ixfr; /* 1:IXFR detected. 2:middle IXFR SOA seen. */ +#ifdef TSIG + tsig_record_type tsig; /* tsig state for IXFR/AXFR */ +#endif +}; + +enum xfrd_packet_result { + xfrd_packet_bad, /* drop the packet/connection */ + xfrd_packet_more, /* more packets to follow on tcp */ + xfrd_packet_notimpl, /* server responded with NOTIMPL or FORMATERR */ + xfrd_packet_tcp, /* try tcp connection */ + xfrd_packet_transfer, /* server responded with transfer*/ + xfrd_packet_newlease /* no changes, soa OK */ +}; + +/* + Division of the (portably: 1024) max number of sockets that can be open. + The sum of the below numbers should be below the user limit for sockets + open, or you see errors in your logfile. + And it should be below FD_SETSIZE, to be able to select() on replies. + Note that also some sockets are used for writing the ixfr.db, xfrd.state + files and for the pipes to the main parent process. +*/ +#define XFRD_MAX_TCP 50 /* max number of TCP AXFR/IXFR concurrent connections.*/ + /* Each entry has 64Kb buffer preallocated.*/ +#define XFRD_MAX_UDP 100 /* max number of UDP sockets at a time for IXFR */ +#define XFRD_MAX_UDP_NOTIFY 50 /* max concurrent UDP sockets for NOTIFY */ + +extern xfrd_state_t* xfrd; + +/* start xfrd, new start. Pass socket to server_main. */ +void xfrd_init(int socket, struct nsd* nsd); + +/* get the current time epoch. Cached for speed. */ +time_t xfrd_time(); + +/* + * Handle final received packet from network. + * returns enum of packet discovery results + */ +enum xfrd_packet_result xfrd_handle_received_xfr_packet( + xfrd_zone_t* zone, buffer_type* packet); + +/* set timer to specific value */ +void xfrd_set_timer(xfrd_zone_t* zone, time_t t); +/* set refresh timer of zone to refresh at time now */ +void xfrd_set_refresh_now(xfrd_zone_t* zone); +/* unset the timer - no more timeouts, for when zone is queued */ +void xfrd_unset_timer(xfrd_zone_t* zone); + +/* + * Make a new request to next master server. + * uses next_master if set (and a fresh set of rounds). + * otherwised, starts new round of requests if none started already. + * starts next round of requests if at last master. + * if too many rounds of requests, sets timer for next retry. + */ +void xfrd_make_request(xfrd_zone_t* zone); + +/* + * send packet via udp (returns UDP fd source socket) to acl addr. + * returns -1 on failure. + */ +int xfrd_send_udp(acl_options_t* acl, buffer_type* packet, acl_options_t* ifc); + +/* + * read from udp port packet into buffer, returns 0 on failure + */ +int xfrd_udp_read_packet(buffer_type* packet, int fd); + +/* + * Release udp socket that a zone is using + */ +void xfrd_udp_release(xfrd_zone_t* zone); + +/* + * Get a static buffer for temporary use (to build a packet). + */ +struct buffer* xfrd_get_temp_buffer(); + +/* + * TSIG sign outgoing request. Call if acl has a key. + */ +#ifdef TSIG +void xfrd_tsig_sign_request(buffer_type* packet, struct tsig_record* tsig, + acl_options_t* acl); +#endif + +/* handle incoming soa information (NSD is running it, time acquired=guess). + Pass soa=NULL,acquired=now if NSD has nothing loaded for the zone + (i.e. zonefile was deleted). */ +void xfrd_handle_incoming_soa(xfrd_zone_t* zone, xfrd_soa_t* soa, + time_t acquired); +/* handle a packet passed along ipc route. acl is the one that accepted + the packet. The packet is the network blob received. */ +void xfrd_handle_passed_packet(buffer_type* packet, int acl_num); + +/* send expiry notify for all zones to nsd (sets all dirty). */ +void xfrd_send_expy_all_zones(); + +/* try to reopen the logfile. */ +void xfrd_reopen_logfile(); + +/* copy SOA info from rr to soa struct. */ +void xfrd_copy_soa(xfrd_soa_t* soa, rr_type* rr); + +/* check for failed updates - it is assumed that now the reload has + finished, and all zone SOAs have been sent. */ +void xfrd_check_failed_updates(); + +/* + * Prepare zones for a reload, this sets the times on the zones to be + * before the current time, so the reload happens after. + */ +void xfrd_prepare_zones_for_reload(); + +/* Bind a local interface to a socket descriptor, return 1 on success */ +int xfrd_bind_local_interface(int sockd, acl_options_t* ifc, + acl_options_t* acl, int tcp); + +#endif /* XFRD_H */ diff --git a/usr.sbin/nsd/zlexer.lex b/usr.sbin/nsd/zlexer.lex new file mode 100644 index 00000000000..0d5ac7e0c9e --- /dev/null +++ b/usr.sbin/nsd/zlexer.lex @@ -0,0 +1,383 @@ +%{ +/* + * zlexer.lex - lexical analyzer for (DNS) zone files + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <strings.h> + +#include "zonec.h" +#include "dname.h" +#include "zparser.h" + +#define YY_NO_UNPUT + +#if 0 +#define LEXOUT(s) printf s /* used ONLY when debugging */ +#else +#define LEXOUT(s) +#endif + +enum lexer_state { + EXPECT_OWNER, + PARSING_OWNER, + PARSING_TTL_CLASS_TYPE, + PARSING_RDATA +}; + +static int parse_token(int token, char *yytext, enum lexer_state *lexer_state); + +static YY_BUFFER_STATE include_stack[MAXINCLUDES]; +static zparser_type zparser_stack[MAXINCLUDES]; +static int include_stack_ptr = 0; + +/* + * Saves the file specific variables on the include stack. + */ +static void +push_parser_state(FILE *input) +{ + zparser_stack[include_stack_ptr].filename = parser->filename; + zparser_stack[include_stack_ptr].line = parser->line; + zparser_stack[include_stack_ptr].origin = parser->origin; + include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; + yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE)); + ++include_stack_ptr; +} + +/* + * Restores the file specific variables from the include stack. + */ +static void +pop_parser_state(void) +{ + --include_stack_ptr; + parser->filename = zparser_stack[include_stack_ptr].filename; + parser->line = zparser_stack[include_stack_ptr].line; + parser->origin = zparser_stack[include_stack_ptr].origin; + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(include_stack[include_stack_ptr]); +} + +#ifndef yy_set_bol /* compat definition, for flex 2.4.6 */ +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \ + } +#endif + +%} + +SPACE [ \t] +LETTER [a-zA-Z] +NEWLINE [\n\r] +ZONESTR [^ \t\n\r();.\"\$] +DOLLAR \$ +COMMENT ; +DOT \. +BIT [^\]\n]|\\. +ANY [^\"\n\\]|\\. + +%x incl bitlabel quotedstring + +%% + static int paren_open = 0; + static enum lexer_state lexer_state = EXPECT_OWNER; +{SPACE}*{COMMENT}.* /* ignore */ +^{DOLLAR}TTL { lexer_state = PARSING_RDATA; return DOLLAR_TTL; } +^{DOLLAR}ORIGIN { lexer_state = PARSING_RDATA; return DOLLAR_ORIGIN; } + + /* + * Handle $INCLUDE directives. See + * http://dinosaur.compilertools.net/flex/flex_12.html#SEC12. + */ +^{DOLLAR}INCLUDE { + BEGIN(incl); +} +<incl>\n | +<incl><<EOF>> { + int error_occurred = parser->error_occurred; + BEGIN(INITIAL); + zc_error("missing file name in $INCLUDE directive"); + yy_set_bol(1); /* Set beginning of line, so "^" rules match. */ + ++parser->line; + parser->error_occurred = error_occurred; +} +<incl>.+ { + char *tmp; + domain_type *origin = parser->origin; + int error_occurred = parser->error_occurred; + + BEGIN(INITIAL); + if (include_stack_ptr >= MAXINCLUDES ) { + zc_error("includes nested too deeply, skipped (>%d)", + MAXINCLUDES); + } else { + FILE *input; + + /* Remove trailing comment. */ + tmp = strrchr(yytext, ';'); + if (tmp) { + *tmp = '\0'; + } + strip_string(yytext); + + /* Parse origin for include file. */ + tmp = strrchr(yytext, ' '); + if (!tmp) { + tmp = strrchr(yytext, '\t'); + } + if (tmp) { + const dname_type *dname; + + /* split the original yytext */ + *tmp = '\0'; + strip_string(yytext); + + dname = dname_parse(parser->region, tmp + 1); + if (!dname) { + zc_error("incorrect include origin '%s'", + tmp + 1); + } else { + origin = domain_table_insert( + parser->db->domains, dname); + } + } + + if (strlen(yytext) == 0) { + zc_error("missing file name in $INCLUDE directive"); + } else if (!(input = fopen(yytext, "r"))) { + zc_error("cannot open include file '%s': %s", + yytext, strerror(errno)); + } else { + /* Initialize parser for include file. */ + char *filename = region_strdup(parser->region, yytext); + push_parser_state(input); /* Destroys yytext. */ + parser->filename = filename; + parser->line = 1; + parser->origin = origin; + lexer_state = EXPECT_OWNER; + } + } + + parser->error_occurred = error_occurred; +} +<INITIAL><<EOF>> { + yy_set_bol(1); /* Set beginning of line, so "^" rules match. */ + if (include_stack_ptr == 0) { + yyterminate(); + } else { + fclose(yyin); + pop_parser_state(); + } +} +^{DOLLAR}{LETTER}+ { zc_warning("Unknown directive: %s", yytext); } +{DOT} { + LEXOUT((". ")); + return parse_token('.', yytext, &lexer_state); +} +@ { + LEXOUT(("@ ")); + return parse_token('@', yytext, &lexer_state); +} +\\# { + LEXOUT(("\\# ")); + return parse_token(URR, yytext, &lexer_state); +} +{NEWLINE} { + ++parser->line; + if (!paren_open) { + lexer_state = EXPECT_OWNER; + LEXOUT(("NL\n")); + return NL; + } else { + LEXOUT(("SP ")); + return SP; + } +} +\( { + if (paren_open) { + zc_error("nested parentheses"); + yyterminate(); + } + LEXOUT(("( ")); + paren_open = 1; + return SP; +} +\) { + if (!paren_open) { + zc_error("closing parentheses without opening parentheses"); + yyterminate(); + } + LEXOUT((") ")); + paren_open = 0; + return SP; +} +{SPACE}+ { + if (!paren_open && lexer_state == EXPECT_OWNER) { + lexer_state = PARSING_TTL_CLASS_TYPE; + LEXOUT(("PREV ")); + return PREV; + } + if (lexer_state == PARSING_OWNER) { + lexer_state = PARSING_TTL_CLASS_TYPE; + } + LEXOUT(("SP ")); + return SP; +} + + /* Bitlabels. Strip leading and ending brackets. */ +\\\[ { BEGIN(bitlabel); } +<bitlabel><<EOF>> { + zc_error("EOF inside bitlabel"); + BEGIN(INITIAL); +} +<bitlabel>{BIT}* { yymore(); } +<bitlabel>\n { ++parser->line; yymore(); } +<bitlabel>\] { + BEGIN(INITIAL); + yytext[yyleng - 1] = '\0'; + return parse_token(BITLAB, yytext, &lexer_state); +} + + /* Quoted strings. Strip leading and ending quotes. */ +\" { BEGIN(quotedstring); LEXOUT(("\" ")); } +<quotedstring><<EOF>> { + zc_error("EOF inside quoted string"); + BEGIN(INITIAL); +} +<quotedstring>{ANY}* { LEXOUT(("STR ")); yymore(); } +<quotedstring>\n { ++parser->line; yymore(); } +<quotedstring>\" { + LEXOUT(("\" ")); + BEGIN(INITIAL); + yytext[yyleng - 1] = '\0'; + return parse_token(STR, yytext, &lexer_state); +} + +({ZONESTR}|\\.|\\\n)+ { + /* Any allowed word. */ + return parse_token(STR, yytext, &lexer_state); +} +. { + zc_error("unknown character '%c' (\\%03d) seen - is this a zonefile?", + (int) yytext[0], (int) yytext[0]); +} +%% + +/* + * Analyze "word" to see if it matches an RR type, possibly by using + * the "TYPExxx" notation. If it matches, the corresponding token is + * returned and the TYPE parameter is set to the RR type value. + */ +static int +rrtype_to_token(const char *word, uint16_t *type) +{ + uint16_t t = rrtype_from_string(word); + if (t != 0) { + rrtype_descriptor_type *entry = rrtype_descriptor_by_type(t); + *type = t; + return entry->token; + } + + return 0; +} + + +/* + * Remove \DDD constructs from the input. See RFC 1035, section 5.1. + */ +static size_t +zoctet(char *text) +{ + /* + * s follows the string, p lags behind and rebuilds the new + * string + */ + char *s; + char *p; + + for (s = p = text; *s; ++s, ++p) { + assert(p <= s); + if (s[0] != '\\') { + /* Ordinary character. */ + *p = *s; + } else if (isdigit((int)s[1]) && isdigit((int)s[2]) && isdigit((int)s[3])) { + /* \DDD escape. */ + int val = (hexdigit_to_int(s[1]) * 100 + + hexdigit_to_int(s[2]) * 10 + + hexdigit_to_int(s[3])); + if (0 <= val && val <= 255) { + s += 3; + *p = val; + } else { + zc_warning("text escape \\DDD overflow"); + *p = *++s; + } + } else if (s[1] != '\0') { + /* \X where X is any character, keep X. */ + *p = *++s; + } else { + /* Trailing backslash, ignore it. */ + zc_warning("trailing backslash ignored"); + --p; + } + } + *p = '\0'; + return p - text; +} + +static int +parse_token(int token, char *yytext, enum lexer_state *lexer_state) +{ + char *str = region_strdup(parser->rr_region, yytext); + size_t len = zoctet(str); + + if (*lexer_state == EXPECT_OWNER) { + *lexer_state = PARSING_OWNER; + } else if (*lexer_state == PARSING_TTL_CLASS_TYPE) { + const char *t; + int token; + uint16_t rrclass; + + /* type */ + token = rrtype_to_token(str, &yylval.type); + if (token != 0) { + *lexer_state = PARSING_RDATA; + LEXOUT(("%d[%s] ", token, yytext)); + return token; + } + + /* class */ + rrclass = rrclass_from_string(str); + if (rrclass != 0) { + yylval.klass = rrclass; + LEXOUT(("CLASS ")); + return T_RRCLASS; + } + + /* ttl */ + yylval.ttl = strtottl(str, &t); + if (*t == '\0') { + LEXOUT(("TTL ")); + return T_TTL; + } + } + + yylval.data.str = str; + yylval.data.len = len; + + LEXOUT(("%d[%s] ", token, yytext)); + return token; +} diff --git a/usr.sbin/nsd/zonec.8 b/usr.sbin/nsd/zonec.8 new file mode 100644 index 00000000000..b869deebced --- /dev/null +++ b/usr.sbin/nsd/zonec.8 @@ -0,0 +1,126 @@ +.TH "zonec" "8" "Jan 6, 2010" "NLnet Labs" "nsd 3.2.4" +.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. +.\" See LICENSE for the license. +.SH "NAME" +.LP +.B zonec +\- NSD zone compiler version 3.2.4. +.SH "SYNOPSIS" +.LP +.B zonec +.RB [ \-v ] +.RB [ \-h ] +.RB [ \-C ] +.RB [ \-L ] +.RB [ \-F ] +.RB [ \-c +.IR configfile ] +.RB [ \-d +.IR directory ] +.RB [ \-o +.IR origin ] +.RB [ \-z +.IR zonefile ] +.RB [ \-f +.IR database ] +.SH "DESCRIPTION" +.LP +.B Zonec +is the nsd(8) database compiler for creating name space databases +from a set of input master zone files specified in nsd.conf(5) file. +.LP +It is normally invoked via nsdc(8) rebuild command. +.B Zonec +will then parse every zone in nsd.conf(5) file and add it to the +name space database, +.I /var/db/nsd/nsd.db +by default, that is used by nsd(8) to answer incoming queries. +.SH "OPTIONS" +.TP +.B \-c\fI configfile +Read specified configfile instead of the default +.IR /etc/nsd/nsd.conf . +.TP +.B \-C +No config file is read (use with \-f, \-o and \-z). +.TP +.B \-d\fI directory +Change the working directory to +.I directory +before doing any work. Overrides zonesdir: option in config file. +.TP +.B \-f\fI database +Create the specified +.I database +instead of the file specified as database: in the config file. +.TP +.B \-o\fI origin +Use this as the first origin. Zone information is read from +zonefile specified with \-z. When reading zones from config file +this option is ignored. +.TP +.B \-z\fI zonefile +Reads all zone information from +.IR zonefile . +If +.IR zonefile +equals `\-`, then all zone information is read from stdin, making +constructs like: +.LP +.RS +.B # cat zones* +| +.B ./zonec \-C \-f nsd.db \-o example.net \-z \- +.RE +.LP +.RS +possible. When reading zones from config file this option is +ignored. +.RE +.TP +.B \-v +Increase the verbosity of zonec. This flag can be specified multiple +times to increase the level of verbosity. The first level of +verbosity will print per zone summary information. The second level +of will print progress information for each 10,000 RRs processed. +.TP +.B \-F +Set debug facilities. (If compiled with \-\-enable\-checking.) +.TP +.B \-L +Set debug level. (If compiled with \-\-enable\-checking.) +.SH "FILES" +.TP +/var/db/nsd/nsd.db +default +.B NSD +database +.TP +/etc/nsd/nsd.conf +default +.B NSD +configuration file +.SH "DIAGNOSTICS" +.LP +.B Zonec +will log all the problems via the standard error output and +progress via stdout if the +.B v +option is specified. +.SH "SEE ALSO" +.LP +nsd(8), nsdc(8), nsd.conf(5), nsd\-checkconf(8), nsd-notify(8), +nsd-patch(8), nsd-xfer(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 Zonec +has rather weak error diagnostics that will change in further +versions. +.B Zonec +expects the input files to be free of syntax errors and very little +fool proof checks are done. diff --git a/usr.sbin/nsd/zonec.c b/usr.sbin/nsd/zonec.c new file mode 100644 index 00000000000..705752b4ac7 --- /dev/null +++ b/usr.sbin/nsd/zonec.c @@ -0,0 +1,1571 @@ +/* + * zonec.c -- zone compiler. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <assert.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#include <unistd.h> +#include <stdlib.h> +#include <time.h> + +#include <netinet/in.h> + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#include "zonec.h" + +#include "dname.h" +#include "dns.h" +#include "namedb.h" +#include "rdata.h" +#include "region-allocator.h" +#include "util.h" +#include "zparser.h" +#include "options.h" +#include "nsec3.h" + +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 long int totalrrs = 0; + +extern uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE]; +extern uint16_t nsec_highest_rcode; + + +/* + * Allocate SIZE+sizeof(uint16_t) bytes and store SIZE in the first + * element. Return a pointer to the allocation. + */ +static uint16_t * +alloc_rdata(region_type *region, size_t size) +{ + uint16_t *result = region_alloc(region, sizeof(uint16_t) + size); + *result = size; + return result; +} + +uint16_t * +alloc_rdata_init(region_type *region, const void *data, size_t size) +{ + uint16_t *result = region_alloc(region, sizeof(uint16_t) + size); + *result = size; + memcpy(result + 1, data, size); + return result; +} + +/* + * These are parser function for generic zone file stuff. + */ +uint16_t * +zparser_conv_hex(region_type *region, const char *hex, size_t len) +{ + /* convert a hex value to wireformat */ + uint16_t *r = NULL; + uint8_t *t; + int i; + + if (len % 2 != 0) { + zc_error_prev_line("number of hex digits must be a multiple of 2"); + } else if (len > MAX_RDLENGTH * 2) { + zc_error_prev_line("hex data exceeds maximum rdata length (%d)", + MAX_RDLENGTH); + } else { + /* the length part */ + r = alloc_rdata(region, len/2); + t = (uint8_t *)(r + 1); + + /* Now process octet by octet... */ + while (*hex) { + *t = 0; + for (i = 16; i >= 1; i -= 15) { + if (isxdigit((int)*hex)) { + *t += hexdigit_to_int(*hex) * i; + } else { + zc_error_prev_line( + "illegal hex character '%c'", + (int) *hex); + return NULL; + } + ++hex; + } + ++t; + } + } + return r; +} + +/* convert hex, precede by a 1-byte length */ +uint16_t * +zparser_conv_hex_length(region_type *region, const char *hex, size_t len) +{ + uint16_t *r = NULL; + uint8_t *t; + int i; + if (len % 2 != 0) { + zc_error_prev_line("number of hex digits must be a multiple of 2"); + } else if (len > 255 * 2) { + zc_error_prev_line("hex data exceeds 255 bytes"); + } else { + uint8_t *l; + + /* the length part */ + r = alloc_rdata(region, len/2+1); + t = (uint8_t *)(r + 1); + + l = t++; + *l = '\0'; + + /* Now process octet by octet... */ + while (*hex) { + *t = 0; + for (i = 16; i >= 1; i -= 15) { + if (isxdigit((int)*hex)) { + *t += hexdigit_to_int(*hex) * i; + } else { + zc_error_prev_line( + "illegal hex character '%c'", + (int) *hex); + return NULL; + } + ++hex; + } + ++t; + ++*l; + } + } + return r; +} + +uint16_t * +zparser_conv_time(region_type *region, const char *time) +{ + /* convert a time YYHM to wireformat */ + uint16_t *r = NULL; + struct tm tm; + + /* Try to scan the time... */ + if (!strptime(time, "%Y%m%d%H%M%S", &tm)) { + zc_error_prev_line("date and time is expected"); + } else { + uint32_t l = htonl(mktime_from_utc(&tm)); + r = alloc_rdata_init(region, &l, sizeof(l)); + } + return r; +} + +uint16_t * +zparser_conv_services(region_type *region, const char *protostr, + char *servicestr) +{ + /* + * Convert a protocol and a list of service port numbers + * (separated by spaces) in the rdata to wireformat + */ + uint16_t *r = NULL; + uint8_t *p; + uint8_t bitmap[65536/8]; + char sep[] = " "; + char *word; + int max_port = -8; + /* convert a protocol in the rdata to wireformat */ + struct protoent *proto; + + memset(bitmap, 0, sizeof(bitmap)); + + proto = getprotobyname(protostr); + if (!proto) { + proto = getprotobynumber(atoi(protostr)); + } + if (!proto) { + zc_error_prev_line("unknown protocol '%s'", protostr); + return NULL; + } + + for (word = strtok(servicestr, sep); word; word = strtok(NULL, sep)) { + struct servent *service; + int port; + + service = getservbyname(word, proto->p_name); + if (service) { + /* Note: ntohs not ntohl! Strange but true. */ + port = ntohs((uint16_t) service->s_port); + } else { + char *end; + port = strtol(word, &end, 10); + if (*end != '\0') { + zc_error_prev_line("unknown service '%s' for protocol '%s'", + word, protostr); + continue; + } + } + + if (port < 0 || port > 65535) { + zc_error_prev_line("bad port number %d", port); + } else { + set_bit(bitmap, port); + if (port > max_port) + max_port = port; + } + } + + r = alloc_rdata(region, sizeof(uint8_t) + max_port / 8 + 1); + p = (uint8_t *) (r + 1); + *p = proto->p_proto; + memcpy(p + 1, bitmap, *r); + + return r; +} + +uint16_t * +zparser_conv_serial(region_type *region, const char *serialstr) +{ + uint16_t *r = NULL; + uint32_t serial; + const char *t; + + serial = strtoserial(serialstr, &t); + if (*t != '\0') { + zc_error_prev_line("serial is expected"); + } else { + serial = htonl(serial); + r = alloc_rdata_init(region, &serial, sizeof(serial)); + } + return r; +} + +uint16_t * +zparser_conv_period(region_type *region, const char *periodstr) +{ + /* convert a time period (think TTL's) to wireformat) */ + uint16_t *r = NULL; + uint32_t period; + const char *end; + + /* Allocate required space... */ + period = strtottl(periodstr, &end); + if (*end != '\0') { + zc_error_prev_line("time period is expected"); + } else { + period = htonl(period); + r = alloc_rdata_init(region, &period, sizeof(period)); + } + return r; +} + +uint16_t * +zparser_conv_short(region_type *region, const char *text) +{ + uint16_t *r = NULL; + uint16_t value; + char *end; + + value = htons((uint16_t) strtol(text, &end, 10)); + if (*end != '\0') { + zc_error_prev_line("integer value is expected"); + } else { + r = alloc_rdata_init(region, &value, sizeof(value)); + } + return r; +} + +uint16_t * +zparser_conv_byte(region_type *region, const char *text) +{ + uint16_t *r = NULL; + uint8_t value; + char *end; + + value = (uint8_t) strtol(text, &end, 10); + if (*end != '\0') { + zc_error_prev_line("integer value is expected"); + } else { + r = alloc_rdata_init(region, &value, sizeof(value)); + } + return r; +} + +uint16_t * +zparser_conv_algorithm(region_type *region, const char *text) +{ + const lookup_table_type *alg; + uint8_t id; + + alg = lookup_by_name(dns_algorithms, text); + if (alg) { + id = (uint8_t) alg->id; + } else { + char *end; + id = (uint8_t) strtol(text, &end, 10); + if (*end != '\0') { + zc_error_prev_line("algorithm is expected"); + return NULL; + } + } + + return alloc_rdata_init(region, &id, sizeof(id)); +} + +uint16_t * +zparser_conv_certificate_type(region_type *region, const char *text) +{ + /* convert a algoritm string to integer */ + const lookup_table_type *type; + uint16_t id; + + type = lookup_by_name(dns_certificate_types, text); + if (type) { + id = htons((uint16_t) type->id); + } else { + char *end; + id = htons((uint16_t) strtol(text, &end, 10)); + if (*end != '\0') { + zc_error_prev_line("certificate type is expected"); + return NULL; + } + } + + return alloc_rdata_init(region, &id, sizeof(id)); +} + +uint16_t * +zparser_conv_a(region_type *region, const char *text) +{ + in_addr_t address; + uint16_t *r = NULL; + + if (inet_pton(AF_INET, text, &address) != 1) { + zc_error_prev_line("invalid IPv4 address '%s'", text); + } else { + r = alloc_rdata_init(region, &address, sizeof(address)); + } + return r; +} + +uint16_t * +zparser_conv_aaaa(region_type *region, const char *text) +{ + uint8_t address[IP6ADDRLEN]; + uint16_t *r = NULL; + + if (inet_pton(AF_INET6, text, address) != 1) { + zc_error_prev_line("invalid IPv6 address '%s'", text); + } else { + r = alloc_rdata_init(region, address, sizeof(address)); + } + return r; +} + +uint16_t * +zparser_conv_text(region_type *region, const char *text, size_t len) +{ + uint16_t *r = NULL; + + if (len > 255) { + zc_error_prev_line("text string is longer than 255 characters," + " try splitting it into multiple parts"); + } else { + uint8_t *p; + r = alloc_rdata(region, len + 1); + p = (uint8_t *) (r + 1); + *p = len; + memcpy(p + 1, text, len); + } + return r; +} + +uint16_t * +zparser_conv_dns_name(region_type *region, const uint8_t* name, size_t len) +{ + uint16_t* r = NULL; + uint8_t* p = NULL; + r = alloc_rdata(region, len); + p = (uint8_t *) (r + 1); + memcpy(p, name, len); + + return r; +} + +uint16_t * +zparser_conv_b32(region_type *region, const char *b32) +{ + uint8_t buffer[B64BUFSIZE]; + uint16_t *r = NULL; + int i; + + if(strcmp(b32, "-") == 0) { + return alloc_rdata_init(region, "", 1); + } + i = b32_pton(b32, buffer+1, B64BUFSIZE-1); + if (i == -1 || i > 255) { + zc_error_prev_line("invalid base32 data"); + } else { + buffer[0] = i; /* store length byte */ + r = alloc_rdata_init(region, buffer, i+1); + } + return r; +} + +uint16_t * +zparser_conv_b64(region_type *region, const char *b64) +{ + uint8_t buffer[B64BUFSIZE]; + uint16_t *r = NULL; + int i; + + i = b64_pton(b64, buffer, B64BUFSIZE); + if (i == -1) { + zc_error_prev_line("invalid base64 data"); + } else { + r = alloc_rdata_init(region, buffer, i); + } + return r; +} + +uint16_t * +zparser_conv_rrtype(region_type *region, const char *text) +{ + uint16_t *r = NULL; + uint16_t type = rrtype_from_string(text); + + if (type == 0) { + zc_error_prev_line("unrecognized RR type '%s'", text); + } else { + type = htons(type); + r = alloc_rdata_init(region, &type, sizeof(type)); + } + return r; +} + +uint16_t * +zparser_conv_nxt(region_type *region, uint8_t nxtbits[]) +{ + /* nxtbits[] consists of 16 bytes with some zero's in it + * copy every byte with zero to r and write the length in + * the first byte + */ + uint16_t i; + uint16_t last = 0; + + for (i = 0; i < 16; i++) { + if (nxtbits[i] != 0) + last = i + 1; + } + + return alloc_rdata_init(region, nxtbits, last); +} + + +/* we potentially have 256 windows, each one is numbered. empty ones + * should be discarded + */ +uint16_t * +zparser_conv_nsec(region_type *region, + uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE]) +{ + /* nsecbits contains up to 64K of bits which represent the + * types available for a name. Walk the bits according to + * nsec++ draft from jakob + */ + uint16_t *r; + uint8_t *ptr; + size_t i,j; + uint16_t window_count = 0; + uint16_t total_size = 0; + uint16_t window_max = 0; + + /* The used windows. */ + int used[NSEC_WINDOW_COUNT]; + /* The last byte used in each the window. */ + int size[NSEC_WINDOW_COUNT]; + + window_max = 1 + (nsec_highest_rcode / 256); + + /* used[i] is the i-th window included in the nsec + * size[used[0]] is the size of window 0 + */ + + /* walk through the 256 windows */ + for (i = 0; i < window_max; ++i) { + int empty_window = 1; + /* check each of the 32 bytes */ + for (j = 0; j < NSEC_WINDOW_BITS_SIZE; ++j) { + if (nsecbits[i][j] != 0) { + size[i] = j + 1; + empty_window = 0; + } + } + if (!empty_window) { + used[window_count] = i; + window_count++; + } + } + + for (i = 0; i < window_count; ++i) { + total_size += sizeof(uint16_t) + size[used[i]]; + } + + r = alloc_rdata(region, total_size); + ptr = (uint8_t *) (r + 1); + + /* now walk used and copy it */ + for (i = 0; i < window_count; ++i) { + ptr[0] = used[i]; + ptr[1] = size[used[i]]; + memcpy(ptr + 2, &nsecbits[used[i]], size[used[i]]); + ptr += size[used[i]] + 2; + } + + return r; +} + +/* Parse an int terminated in the specified range. */ +static int +parse_int(const char *str, + char **end, + int *result, + const char *name, + int min, + int max) +{ + *result = (int) strtol(str, end, 10); + if (*result < min || *result > max) { + zc_error_prev_line("%s must be within the range [%d .. %d]", + name, + min, + max); + return 0; + } else { + return 1; + } +} + +/* RFC1876 conversion routines */ +static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000, + 1000000,10000000,100000000,1000000000}; + +/* + * Converts ascii size/precision X * 10**Y(cm) to 0xXY. + * Sets the given pointer to the last used character. + * + */ +static uint8_t +precsize_aton (char *cp, char **endptr) +{ + unsigned int mval = 0, cmval = 0; + uint8_t retval = 0; + int exponent; + int mantissa; + + while (isdigit((int)*cp)) + mval = mval * 10 + hexdigit_to_int(*cp++); + + if (*cp == '.') { /* centimeters */ + cp++; + if (isdigit((int)*cp)) { + cmval = hexdigit_to_int(*cp++) * 10; + if (isdigit((int)*cp)) { + cmval += hexdigit_to_int(*cp++); + } + } + } + + if(mval >= poweroften[7]) { + /* integer overflow possible for *100 */ + mantissa = mval / poweroften[7]; + exponent = 9; /* max */ + } + else { + cmval = (mval * 100) + cmval; + + for (exponent = 0; exponent < 9; exponent++) + if (cmval < poweroften[exponent+1]) + break; + + mantissa = cmval / poweroften[exponent]; + } + if (mantissa > 9) + mantissa = 9; + + retval = (mantissa << 4) | exponent; + + if (*cp == 'm') cp++; + + *endptr = cp; + + return (retval); +} + +/* + * Parses a specific part of rdata. + * + * Returns: + * + * number of elements parsed + * zero on error + * + */ +uint16_t * +zparser_conv_loc(region_type *region, char *str) +{ + uint16_t *r; + uint32_t *p; + int i; + int deg, min, secs; /* Secs is stored times 1000. */ + uint32_t lat = 0, lon = 0, alt = 0; + /* encoded defaults: version=0 sz=1m hp=10000m vp=10m */ + uint8_t vszhpvp[4] = {0, 0x12, 0x16, 0x13}; + char *start; + double d; + + for(;;) { + deg = min = secs = 0; + + /* Degrees */ + if (*str == '\0') { + zc_error_prev_line("unexpected end of LOC data"); + return NULL; + } + + if (!parse_int(str, &str, °, "degrees", 0, 180)) + return NULL; + if (!isspace((int)*str)) { + zc_error_prev_line("space expected after degrees"); + return NULL; + } + ++str; + + /* Minutes? */ + if (isdigit((int)*str)) { + if (!parse_int(str, &str, &min, "minutes", 0, 60)) + return NULL; + if (!isspace((int)*str)) { + zc_error_prev_line("space expected after minutes"); + return NULL; + } + ++str; + } + + /* Seconds? */ + if (isdigit((int)*str)) { + start = str; + if (!parse_int(str, &str, &i, "seconds", 0, 60)) { + return NULL; + } + + if (*str == '.' && !parse_int(str + 1, &str, &i, "seconds fraction", 0, 999)) { + return NULL; + } + + if (!isspace((int)*str)) { + zc_error_prev_line("space expected after seconds"); + return NULL; + } + + if (sscanf(start, "%lf", &d) != 1) { + zc_error_prev_line("error parsing seconds"); + } + + if (d < 0.0 || d > 60.0) { + zc_error_prev_line("seconds not in range 0.0 .. 60.0"); + } + + secs = (int) (d * 1000.0 + 0.5); + ++str; + } + + switch(*str) { + case 'N': + case 'n': + lat = ((uint32_t)1<<31) + (deg * 3600000 + min * 60000 + secs); + break; + case 'E': + case 'e': + lon = ((uint32_t)1<<31) + (deg * 3600000 + min * 60000 + secs); + break; + case 'S': + case 's': + lat = ((uint32_t)1<<31) - (deg * 3600000 + min * 60000 + secs); + break; + case 'W': + case 'w': + lon = ((uint32_t)1<<31) - (deg * 3600000 + min * 60000 + secs); + break; + default: + zc_error_prev_line("invalid latitude/longtitude: '%c'", *str); + return NULL; + } + ++str; + + if (lat != 0 && lon != 0) + break; + + if (!isspace((int)*str)) { + zc_error_prev_line("space expected after latitude/longitude"); + return NULL; + } + ++str; + } + + /* Altitude */ + if (*str == '\0') { + zc_error_prev_line("unexpected end of LOC data"); + return NULL; + } + + if (!isspace((int)*str)) { + zc_error_prev_line("space expected before altitude"); + return NULL; + } + ++str; + + start = str; + + /* Sign */ + if (*str == '+' || *str == '-') { + ++str; + } + + /* Meters of altitude... */ + (void) strtol(str, &str, 10); + switch(*str) { + case ' ': + case '\0': + case 'm': + break; + case '.': + if (!parse_int(str + 1, &str, &i, "altitude fraction", 0, 99)) { + return NULL; + } + if (!isspace((int)*str) && *str != '\0' && *str != 'm') { + zc_error_prev_line("altitude fraction must be a number"); + return NULL; + } + break; + default: + zc_error_prev_line("altitude must be expressed in meters"); + return NULL; + } + if (!isspace((int)*str) && *str != '\0') + ++str; + + if (sscanf(start, "%lf", &d) != 1) { + zc_error_prev_line("error parsing altitude"); + } + + alt = (uint32_t) (10000000.0 + d * 100 + 0.5); + + if (!isspace((int)*str) && *str != '\0') { + zc_error_prev_line("unexpected character after altitude"); + return NULL; + } + + /* Now parse size, horizontal precision and vertical precision if any */ + for(i = 1; isspace((int)*str) && i <= 3; i++) { + vszhpvp[i] = precsize_aton(str + 1, &str); + + if (!isspace((int)*str) && *str != '\0') { + zc_error_prev_line("invalid size or precision"); + return NULL; + } + } + + /* Allocate required space... */ + r = alloc_rdata(region, 16); + p = (uint32_t *) (r + 1); + + memmove(p, vszhpvp, 4); + write_uint32(p + 1, lat); + write_uint32(p + 2, lon); + write_uint32(p + 3, alt); + + return r; +} + +/* + * Convert an APL RR RDATA element. + */ +uint16_t * +zparser_conv_apl_rdata(region_type *region, char *str) +{ + int negated = 0; + uint16_t address_family; + uint8_t prefix; + uint8_t maximum_prefix; + uint8_t length; + uint8_t address[IP6ADDRLEN]; + char *colon = strchr(str, ':'); + char *slash = strchr(str, '/'); + int af; + int rc; + uint16_t rdlength; + uint16_t *r; + uint8_t *t; + char *end; + long p; + + if (!colon) { + zc_error("address family separator is missing"); + return NULL; + } + if (!slash) { + zc_error("prefix separator is missing"); + return NULL; + } + + *colon = '\0'; + *slash = '\0'; + + if (*str == '!') { + negated = 1; + ++str; + } + + if (strcmp(str, "1") == 0) { + address_family = htons(1); + af = AF_INET; + length = sizeof(in_addr_t); + maximum_prefix = length * 8; + } else if (strcmp(str, "2") == 0) { + address_family = htons(2); + af = AF_INET6; + length = IP6ADDRLEN; + maximum_prefix = length * 8; + } else { + zc_error("invalid address family '%s'", str); + return NULL; + } + + rc = inet_pton(af, colon + 1, address); + if (rc == 0) { + zc_error("invalid address '%s'", colon + 1); + return NULL; + } else if (rc == -1) { + zc_error("inet_pton failed: %s", strerror(errno)); + return NULL; + } + + /* Strip trailing zero octets. */ + while (length > 0 && address[length - 1] == 0) + --length; + + + p = strtol(slash + 1, &end, 10); + if (p < 0 || p > maximum_prefix) { + zc_error("prefix not in the range 0 .. %d", maximum_prefix); + return NULL; + } else if (*end != '\0') { + zc_error("invalid prefix '%s'", slash + 1); + return NULL; + } + prefix = (uint8_t) p; + + rdlength = (sizeof(address_family) + sizeof(prefix) + sizeof(length) + + length); + r = alloc_rdata(region, rdlength); + t = (uint8_t *) (r + 1); + + memcpy(t, &address_family, sizeof(address_family)); + t += sizeof(address_family); + memcpy(t, &prefix, sizeof(prefix)); + t += sizeof(prefix); + memcpy(t, &length, sizeof(length)); + if (negated) { + *t |= APL_NEGATION_MASK; + } + t += sizeof(length); + memcpy(t, address, length); + + return r; +} + +/* + * Below some function that also convert but not to wireformat + * but to "normal" (int,long,char) types + */ + +uint32_t +zparser_ttl2int(const char *ttlstr, int* error) +{ + /* convert a ttl value to a integer + * return the ttl in a int + * -1 on error + */ + + uint32_t ttl; + const char *t; + + ttl = strtottl(ttlstr, &t); + if (*t != 0) { + zc_error_prev_line("invalid TTL value: %s",ttlstr); + *error = 1; + } + + return ttl; +} + + +void +zadd_rdata_wireformat(uint16_t *data) +{ + if (parser->current_rr.rdata_count > MAXRDATALEN) { + zc_error_prev_line("too many rdata elements"); + } else { + parser->current_rr.rdatas[parser->current_rr.rdata_count].data + = data; + ++parser->current_rr.rdata_count; + } +} + +void +zadd_rdata_domain(domain_type *domain) +{ + if (parser->current_rr.rdata_count > MAXRDATALEN) { + zc_error_prev_line("too many rdata elements"); + } else { + parser->current_rr.rdatas[parser->current_rr.rdata_count].domain + = domain; + ++parser->current_rr.rdata_count; + } +} + +void +parse_unknown_rdata(uint16_t type, uint16_t *wireformat) +{ + buffer_type packet; + uint16_t size; + ssize_t rdata_count; + ssize_t i; + rdata_atom_type *rdatas; + + if (wireformat) { + size = *wireformat; + } else { + return; + } + + buffer_create_from(&packet, wireformat + 1, *wireformat); + rdata_count = rdata_wireformat_to_rdata_atoms(parser->region, + parser->db->domains, + type, + size, + &packet, + &rdatas); + if (rdata_count == -1) { + zc_error_prev_line("bad unknown RDATA"); + return; + } + + for (i = 0; i < rdata_count; ++i) { + if (rdata_atom_is_domain(type, i)) { + zadd_rdata_domain(rdatas[i].domain); + } else { + zadd_rdata_wireformat(rdatas[i].data); + } + } +} + + +/* + * Compares two rdata arrays. + * + * Returns: + * + * zero if they are equal + * non-zero if not + * + */ +static int +zrdatacmp(uint16_t type, rr_type *a, rr_type *b) +{ + int i = 0; + + assert(a); + assert(b); + + /* One is shorter than another */ + if (a->rdata_count != b->rdata_count) + return 1; + + /* Compare element by element */ + for (i = 0; i < a->rdata_count; ++i) { + if (rdata_atom_is_domain(type, i)) { + if (rdata_atom_domain(a->rdatas[i]) + != rdata_atom_domain(b->rdatas[i])) + { + return 1; + } + } else { + if (rdata_atom_size(a->rdatas[i]) + != rdata_atom_size(b->rdatas[i])) + { + return 1; + } + if (memcmp(rdata_atom_data(a->rdatas[i]), + rdata_atom_data(b->rdatas[i]), + rdata_atom_size(a->rdatas[i])) != 0) + { + return 1; + } + } + } + + /* Otherwise they are equal */ + return 0; +} + +/* + * + * Opens a zone file. + * + * Returns: + * + * - pointer to the parser structure + * - NULL on error and errno set + * + */ +static int +zone_open(const char *filename, uint32_t ttl, uint16_t klass, + const dname_type *origin) +{ + /* Open the zone file... */ + if (strcmp(filename, "-") == 0) { + yyin = stdin; + filename = "<stdin>"; + } else if (!(yyin = fopen(filename, "r"))) { + return 0; + } + + /* Open the network database */ + setprotoent(1); + setservent(1); + + zparser_init(filename, ttl, klass, origin); + + return 1; +} + + +void +set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE], + uint16_t index) +{ + /* + * The bits are counted from left to right, so bit #0 is the + * left most bit. + */ + uint8_t window = index / 256; + uint8_t bit = index % 256; + + bits[window][bit / 8] |= (1 << (7 - bit % 8)); +} + + +static void +cleanup_rrset(void *r) +{ + rrset_type *rrset = (rrset_type *) r; + if (rrset) { + free(rrset->rrs); + } +} + +int +process_rr(void) +{ + zone_type *zone = parser->current_zone; + rr_type *rr = &parser->current_rr; + rrset_type *rrset; + size_t max_rdlength; + int i; + rrtype_descriptor_type *descriptor + = rrtype_descriptor_by_type(rr->type); + + /* We only support IN class */ + if (rr->klass != CLASS_IN) { + zc_error_prev_line("only class IN is supported"); + return 0; + } + + /* Make sure the maximum RDLENGTH does not exceed 65535 bytes. */ + max_rdlength = rdata_maximum_wireformat_size( + descriptor, rr->rdata_count, rr->rdatas); + + if (max_rdlength > MAX_RDLENGTH) { + 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; + } + + if (rr->type == TYPE_SOA) { + /* + * This is a SOA record, start a new zone or continue + * an existing one. + */ + if (rr->owner->is_apex) + 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; + } + + /* parser part */ + parser->current_zone = zone; + } + + if (!dname_is_subdomain(domain_dname(rr->owner), + domain_dname(zone->apex))) + { + zc_error_prev_line("out of zone data"); + return 0; + } + + /* Do we have this type of rrset already? */ + rrset = domain_find_rrset(rr->owner, zone, rr->type); + if (!rrset) { + rrset = (rrset_type *) region_alloc(parser->region, + sizeof(rrset_type)); + rrset->zone = zone; + rrset->rr_count = 1; + rrset->rrs = (rr_type *) xalloc(sizeof(rr_type)); + rrset->rrs[0] = *rr; + + region_add_cleanup(parser->region, cleanup_rrset, rrset); + + /* Add it */ + domain_add_rrset(rr->owner, rrset); + } else { + if (rr->type != TYPE_RRSIG && rrset->rrs[0].ttl != rr->ttl) { + zc_warning_prev_line( + "TTL does not match the TTL of the RRset"); + } + + /* Search for possible duplicates... */ + for (i = 0; i < rrset->rr_count; i++) { + if (!zrdatacmp(rr->type, rr, &rrset->rrs[i])) { + break; + } + } + + /* Discard the duplicates... */ + if (i < rrset->rr_count) { + return 0; + } + + /* Add it... */ + rrset->rrs = (rr_type *) xrealloc( + rrset->rrs, + (rrset->rr_count + 1) * sizeof(rr_type)); + rrset->rrs[rrset->rr_count] = *rr; + ++rrset->rr_count; + } + + if(rr->type == TYPE_DNAME && rrset->rr_count > 1) { + zc_error_prev_line("multiple DNAMEs at the same name"); + } + if(rr->type == TYPE_CNAME && rrset->rr_count > 1) { + 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))) { + 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)) { + zc_error_prev_line("CNAME and other data at the same name"); + } + +#ifdef DNSSEC + if (rr->type == TYPE_RRSIG && rr_rrsig_type_covered(rr) == TYPE_SOA) { + rrset->zone->is_secure = 1; + } +#endif + + /* 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) { + 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); + } + ++totalrrs; + return 1; +} + +/* + * Find rrset type for any zone + */ +static rrset_type* +domain_find_rrset_any(domain_type *domain, uint16_t type) +{ + rrset_type *result = domain->rrsets; + while (result) { + if (rrset_rrtype(result) == type) { + return result; + } + result = result->next; + } + return NULL; +} + +/* + * Check for DNAME type. Nothing is allowed below it + */ +static void +check_dname(namedb_type* db) +{ + domain_type* domain; + RBTREE_FOR(domain, domain_type*, db->domains->names_to_domains) + { + if(domain->is_existing) { + /* there may not be DNAMEs above it */ + domain_type* parent = domain->parent; +#ifdef NSEC3 + if(domain_has_only_NSEC3(domain, NULL)) + continue; +#endif + while(parent) { + if(domain_find_rrset_any(parent, TYPE_DNAME)) { + zc_error("While checking node %s,", + dname_to_string(domain_dname(domain), NULL)); + zc_error("DNAME at %s has data below it. " + "This is not allowed (rfc 2672).", + dname_to_string(domain_dname(parent), NULL)); + exit(1); + } + parent = parent->parent; + } + } + } +} + +/* + * 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) +{ + const dname_type *dname; + + dname = dname_parse(parser->region, name); + if (!dname) { + zc_error("incorrect zone name '%s'", name); + return; + } + +#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; + } +#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; + } + + /* Parse and process all RRs. */ + yyparse(); + + /* 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)); + } + } + + fclose(yyin); + + fflush(stdout); + totalerrors += parser->errors; + parser->filename = NULL; +} + +static void +usage (void) +{ +#ifndef NDEBUG + fprintf(stderr, "usage: zonec [-v|-h|-C|-F|-L] [-c configfile] [-o origin] [-d directory] [-f database] [-z zonefile]\n\n"); +#else + fprintf(stderr, "usage: 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) +{ + 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("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, "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, "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, "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, "zonec: error creating the parser\n"); + exit(1); + } + + /* 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, "zonec: must have -z zonefile when reading single zone.\n"); + exit(1); + } + if(!origin) { + fprintf(stderr, "zonec: must have -o origin when reading single zone.\n"); + exit(1); + } + if (vflag > 0) + fprintf(stdout, "zonec: reading zone \"%s\".\n", origin); + zone_read(origin, singlefile, nsd_options); + if (vflag > 0) + fprintf(stdout, "zonec: processed %ld RRs in \"%s\".\n", totalrrs, origin); + } else { + zone_options_t* zone; + if(!nsd_options) { + fprintf(stderr, "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, "zonec: reading zone \"%s\".\n", + zone->name); + zone_read(zone->name, zone->zonefile, nsd_options); + if (vflag > 0) + fprintf(stdout, + "zonec: processed %ld RRs in \"%s\".\n", + totalrrs, zone->name); + totalrrs = 0; + } + } + check_dname(db); + +#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"); + } +#endif /* NDEBUG */ + + /* Close the database */ + if (namedb_save(db) != 0) { + fprintf(stderr, "zonec: error writing the database (%s): %s\n", db->filename, strerror(errno)); + namedb_discard(db); + exit(1); + } + + /* Print the total number of errors */ + if (vflag > 0 || totalerrors > 0) { + fprintf(stderr, "\nzonec: done with %ld errors.\n", + totalerrors); + } + + /* Disable this to save some time. */ +#if 0 + region_destroy(global_region); +#endif + + return totalerrors ? 1 : 0; +} diff --git a/usr.sbin/nsd/zonec.h b/usr.sbin/nsd/zonec.h new file mode 100644 index 00000000000..f732377740d --- /dev/null +++ b/usr.sbin/nsd/zonec.h @@ -0,0 +1,123 @@ +/* + * zonec.h -- zone compiler. + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#ifndef _ZONEC_H_ +#define _ZONEC_H_ + +#include "namedb.h" + +#define MAXTOKENSLEN 512 /* Maximum number of tokens per entry */ +#define B64BUFSIZE 65535 /* Buffer size for b64 conversion */ +#define ROOT (const uint8_t *)"\001" + +#define NSEC_WINDOW_COUNT 256 +#define NSEC_WINDOW_BITS_COUNT 256 +#define NSEC_WINDOW_BITS_SIZE (NSEC_WINDOW_BITS_COUNT / 8) + +#define IPSECKEY_NOGATEWAY 0 /* RFC 4025 */ +#define IPSECKEY_IP4 1 +#define IPSECKEY_IP6 2 +#define IPSECKEY_DNAME 3 + +#define LINEBUFSZ 1024 + +struct lex_data { + size_t len; /* holds the label length */ + char *str; /* holds the data */ +}; + +#define DEFAULT_TTL 3600 + +/* administration struct */ +typedef struct zparser zparser_type; +struct zparser { + region_type *region; /* Allocate for parser lifetime data. */ + region_type *rr_region; /* Allocate RR lifetime data. */ + namedb_type *db; + + const char *filename; + uint32_t default_ttl; + uint16_t default_class; + zone_type *current_zone; + domain_type *origin; + domain_type *prev_dname; + domain_type *default_apex; + + int error_occurred; + unsigned int errors; + unsigned int line; + + rr_type current_rr; + rdata_atom_type *temporary_rdatas; +}; + +extern zparser_type *parser; + +/* used in zonec.lex */ +extern FILE *yyin; + +/* + * Used to mark bad domains and domain names. Do not dereference + * these pointers! + */ +extern const dname_type *error_dname; +extern domain_type *error_domain; + +int yyparse(void); +int yylex(void); +/*int yyerror(const char *s);*/ +void yyrestart(FILE *); + +void zc_warning(const char *fmt, ...) ATTR_FORMAT(printf, 1, 2); +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); + +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); +uint16_t *zparser_conv_time(region_type *region, const char *time); +uint16_t *zparser_conv_services(region_type *region, const char *protostr, char *servicestr); +uint16_t *zparser_conv_serial(region_type *region, const char *periodstr); +uint16_t *zparser_conv_period(region_type *region, const char *periodstr); +uint16_t *zparser_conv_short(region_type *region, const char *text); +uint16_t *zparser_conv_long(region_type *region, const char *text); +uint16_t *zparser_conv_byte(region_type *region, const char *text); +uint16_t *zparser_conv_a(region_type *region, const char *text); +uint16_t *zparser_conv_aaaa(region_type *region, const char *text); +uint16_t *zparser_conv_text(region_type *region, const char *text, size_t len); +uint16_t *zparser_conv_dns_name(region_type *region, const uint8_t* name, size_t len); +uint16_t *zparser_conv_b32(region_type *region, const char *b32); +uint16_t *zparser_conv_b64(region_type *region, const char *b64); +uint16_t *zparser_conv_rrtype(region_type *region, const char *rr); +uint16_t *zparser_conv_nxt(region_type *region, uint8_t nxtbits[]); +uint16_t *zparser_conv_nsec(region_type *region, uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE]); +uint16_t *zparser_conv_loc(region_type *region, char *str); +uint16_t *zparser_conv_algorithm(region_type *region, const char *algstr); +uint16_t *zparser_conv_certificate_type(region_type *region, + const char *typestr); +uint16_t *zparser_conv_apl_rdata(region_type *region, char *str); + +void parse_unknown_rdata(uint16_t type, uint16_t *wireformat); + +uint32_t zparser_ttl2int(const char *ttlstr, int* error); +void zadd_rdata_wireformat(uint16_t *data); +void zadd_rdata_domain(domain_type *domain); + +void set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE], + uint16_t index); +uint16_t *alloc_rdata_init(region_type *region, const void *data, size_t size); + +/* zparser.y */ +zparser_type *zparser_create(region_type *region, region_type *rr_region, + namedb_type *db); +void zparser_init(const char *filename, uint32_t ttl, uint16_t klass, + const dname_type *origin); + +#endif /* _ZONEC_H_ */ diff --git a/usr.sbin/nsd/zparser.y b/usr.sbin/nsd/zparser.y new file mode 100644 index 00000000000..0b50b172ede --- /dev/null +++ b/usr.sbin/nsd/zparser.y @@ -0,0 +1,1095 @@ +%{ +/* + * zyparser.y -- yacc grammar for (DNS) zone files + * + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +#include <config.h> + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "dname.h" +#include "namedb.h" +#include "zonec.h" + +/* these need to be global, otherwise they cannot be used inside yacc */ +zparser_type *parser; + +#ifdef __cplusplus +extern "C" +#endif /* __cplusplus */ +int yywrap(void); + +/* this hold the nxt bits */ +static uint8_t nxtbits[16]; +static int dlv_warn = 1; + +/* 256 windows of 256 bits (32 bytes) */ +/* still need to reset the bastard somewhere */ +static uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE]; + +/* hold the highest rcode seen in a NSEC rdata , BUG #106 */ +uint16_t nsec_highest_rcode; + +void yyerror(const char *message); + +#ifdef NSEC3 +/* parse nsec3 parameters and add the (first) rdata elements */ +static void +nsec3_add_params(const char* hash_algo_str, const char* flag_str, + const char* iter_str, const char* salt_str, int salt_len); +#endif /* NSEC3 */ + +%} +%union { + domain_type *domain; + const dname_type *dname; + struct lex_data data; + uint32_t ttl; + uint16_t klass; + uint16_t type; + uint16_t *unknown; +} + +/* + * Tokens to represent the known RR types of DNS. + */ +%token <type> T_A T_NS T_MX T_TXT T_CNAME T_AAAA T_PTR T_NXT T_KEY T_SOA T_SIG +%token <type> T_SRV T_CERT T_LOC T_MD T_MF T_MB T_MG T_MR T_NULL T_WKS T_HINFO +%token <type> T_MINFO T_RP T_AFSDB T_X25 T_ISDN T_RT T_NSAP T_NSAP_PTR T_PX +%token <type> T_GPOS T_EID T_NIMLOC T_ATMA T_NAPTR T_KX T_A6 T_DNAME T_SINK +%token <type> T_OPT T_APL T_UINFO T_UID T_GID T_UNSPEC T_TKEY T_TSIG T_IXFR +%token <type> T_AXFR T_MAILB T_MAILA T_DS T_DLV T_SSHFP T_RRSIG T_NSEC T_DNSKEY +%token <type> T_SPF T_NSEC3 T_IPSECKEY T_DHCID T_NSEC3PARAM + +/* other tokens */ +%token DOLLAR_TTL DOLLAR_ORIGIN NL SP +%token <data> STR PREV BITLAB +%token <ttl> T_TTL +%token <klass> T_RRCLASS + +/* unknown RRs */ +%token URR +%token <type> T_UTYPE + +%type <type> type_and_rdata +%type <domain> owner dname abs_dname +%type <dname> rel_dname label +%type <data> wire_dname wire_abs_dname wire_rel_dname wire_label +%type <data> concatenated_str_seq str_sp_seq str_dot_seq dotted_str +%type <data> nxt_seq nsec_more +%type <unknown> rdata_unknown + +%% +lines: /* empty file */ + | lines line + ; + +line: NL + | sp NL + | PREV NL {} /* Lines containing only whitespace. */ + | ttl_directive + { + parser->error_occurred = 0; + } + | origin_directive + { + parser->error_occurred = 0; + } + | rr + { /* rr should be fully parsed */ + if (!parser->error_occurred) { + parser->current_rr.rdatas + = (rdata_atom_type *) region_alloc_init( + parser->region, + parser->current_rr.rdatas, + (parser->current_rr.rdata_count + * sizeof(rdata_atom_type))); + + process_rr(); + } + + region_free_all(parser->rr_region); + + parser->current_rr.type = 0; + parser->current_rr.rdata_count = 0; + parser->current_rr.rdatas = parser->temporary_rdatas; + parser->error_occurred = 0; + } + | error NL + ; + +/* needed to cope with ( and ) in arbitary places */ +sp: SP + | sp SP + ; + +trail: NL + | sp NL + ; + +ttl_directive: DOLLAR_TTL sp STR trail + { + parser->default_ttl = zparser_ttl2int($3.str, &(parser->error_occurred)); + if (parser->error_occurred == 1) { + parser->default_ttl = DEFAULT_TTL; + parser->error_occurred = 0; + } + } + ; + +origin_directive: DOLLAR_ORIGIN sp abs_dname trail + { + parser->origin = $3; + } + | DOLLAR_ORIGIN sp rel_dname trail + { + zc_error_prev_line("$ORIGIN directive requires absolute domain name"); + } + ; + +rr: owner classttl type_and_rdata + { + parser->current_rr.owner = $1; + parser->current_rr.type = $3; + } + ; + +owner: dname sp + { + parser->prev_dname = $1; + $$ = $1; + } + | PREV + { + $$ = parser->prev_dname; + } + ; + +classttl: /* empty - fill in the default, def. ttl and IN class */ + { + parser->current_rr.ttl = parser->default_ttl; + parser->current_rr.klass = parser->default_class; + } + | T_RRCLASS sp /* no ttl */ + { + parser->current_rr.ttl = parser->default_ttl; + parser->current_rr.klass = $1; + } + | T_TTL sp /* no class */ + { + parser->current_rr.ttl = $1; + parser->current_rr.klass = parser->default_class; + } + | T_TTL sp T_RRCLASS sp /* the lot */ + { + parser->current_rr.ttl = $1; + parser->current_rr.klass = $3; + } + | T_RRCLASS sp T_TTL sp /* the lot - reversed */ + { + parser->current_rr.ttl = $3; + parser->current_rr.klass = $1; + } + ; + +dname: abs_dname + | rel_dname + { + if ($1 == error_dname) { + $$ = error_domain; + } else if ($1->name_size + domain_dname(parser->origin)->name_size - 1 > MAXDOMAINLEN) { + zc_error("domain name exceeds %d character limit", MAXDOMAINLEN); + $$ = error_domain; + } else { + $$ = domain_table_insert( + parser->db->domains, + dname_concatenate( + parser->rr_region, + $1, + domain_dname(parser->origin))); + } + } + ; + +abs_dname: '.' + { + $$ = parser->db->domains->root; + } + | '@' + { + $$ = parser->origin; + } + | rel_dname '.' + { + if ($1 != error_dname) { + $$ = domain_table_insert(parser->db->domains, $1); + } else { + $$ = error_domain; + } + } + ; + +label: STR + { + if ($1.len > MAXLABELLEN) { + zc_error("label exceeds %d character limit", MAXLABELLEN); + $$ = error_dname; + } else { + $$ = dname_make_from_label(parser->rr_region, + (uint8_t *) $1.str, + $1.len); + } + } + | BITLAB + { + zc_error("bitlabels are not supported. RFC2673 has status experimental."); + $$ = error_dname; + } + ; + +rel_dname: label + | rel_dname '.' label + { + if ($1 == error_dname || $3 == error_dname) { + $$ = error_dname; + } else if ($1->name_size + $3->name_size - 1 > MAXDOMAINLEN) { + zc_error("domain name exceeds %d character limit", + MAXDOMAINLEN); + $$ = error_dname; + } else { + $$ = dname_concatenate(parser->rr_region, $1, $3); + } + } + ; + +/* + * Some dnames in rdata are handled as opaque blobs + */ + +wire_dname: wire_abs_dname + | wire_rel_dname + ; + +wire_abs_dname: '.' + { + char *result = (char *) region_alloc(parser->rr_region, 2); + result[0] = 0; + result[1] = '\0'; + $$.str = result; + $$.len = 1; + } + | wire_rel_dname '.' + { + char *result = (char *) region_alloc(parser->rr_region, + $1.len + 2); + memcpy(result, $1.str, $1.len); + result[$1.len] = 0; + result[$1.len+1] = '\0'; + $$.str = result; + $$.len = $1.len + 1; + } + ; + +wire_label: STR + { + char *result = (char *) region_alloc(parser->rr_region, + $1.len + 1); + + if ($1.len > MAXLABELLEN) + zc_error("label exceeds %d character limit", MAXLABELLEN); + + /* make label anyway */ + result[0] = $1.len; + memcpy(result+1, $1.str, $1.len); + + $$.str = result; + $$.len = $1.len + 1; + } + ; + +wire_rel_dname: wire_label + | wire_rel_dname '.' wire_label + { + if ($1.len + $3.len - 3 > MAXDOMAINLEN) + zc_error("domain name exceeds %d character limit", + MAXDOMAINLEN); + + /* make dname anyway */ + $$.len = $1.len + $3.len; + $$.str = (char *) region_alloc(parser->rr_region, $$.len + 1); + memcpy($$.str, $1.str, $1.len); + memcpy($$.str + $1.len, $3.str, $3.len); + $$.str[$$.len] = '\0'; + } + ; + + + +str_seq: STR + { + zadd_rdata_wireformat(zparser_conv_text(parser->region, $1.str, $1.len)); + } + | str_seq sp STR + { + zadd_rdata_wireformat(zparser_conv_text(parser->region, $3.str, $3.len)); + } + ; + +/* + * Generate a single string from multiple STR tokens, separated by + * spaces or dots. + */ +concatenated_str_seq: STR + | '.' + { + $$.len = 1; + $$.str = region_strdup(parser->rr_region, "."); + } + | concatenated_str_seq sp STR + { + $$.len = $1.len + $3.len + 1; + $$.str = (char *) region_alloc(parser->rr_region, $$.len + 1); + memcpy($$.str, $1.str, $1.len); + memcpy($$.str + $1.len, " ", 1); + memcpy($$.str + $1.len + 1, $3.str, $3.len); + $$.str[$$.len] = '\0'; + } + | concatenated_str_seq '.' STR + { + $$.len = $1.len + $3.len + 1; + $$.str = (char *) region_alloc(parser->rr_region, $$.len + 1); + memcpy($$.str, $1.str, $1.len); + memcpy($$.str + $1.len, ".", 1); + memcpy($$.str + $1.len + 1, $3.str, $3.len); + $$.str[$$.len] = '\0'; + } + ; + +/* used to convert a nxt list of types */ +nxt_seq: STR + { + uint16_t type = rrtype_from_string($1.str); + if (type != 0 && type < 128) { + set_bit(nxtbits, type); + } else { + zc_error("bad type %d in NXT record", (int) type); + } + } + | nxt_seq sp STR + { + uint16_t type = rrtype_from_string($3.str); + if (type != 0 && type < 128) { + set_bit(nxtbits, type); + } else { + zc_error("bad type %d in NXT record", (int) type); + } + } + ; + +nsec_more: SP nsec_more + { + } + | NL + { + } + | STR nsec_seq + { + uint16_t type = rrtype_from_string($1.str); + if (type != 0) { + if (type > nsec_highest_rcode) { + nsec_highest_rcode = type; + } + set_bitnsec(nsecbits, type); + } else { + zc_error("bad type %d in NSEC record", (int) type); + } + } + ; + +nsec_seq: NL + | SP nsec_more + ; + +/* + * Sequence of STR tokens separated by spaces. The spaces are not + * preserved during concatenation. + */ +str_sp_seq: STR + | str_sp_seq sp STR + { + char *result = (char *) region_alloc(parser->rr_region, + $1.len + $3.len + 1); + memcpy(result, $1.str, $1.len); + memcpy(result + $1.len, $3.str, $3.len); + $$.str = result; + $$.len = $1.len + $3.len; + $$.str[$$.len] = '\0'; + } + ; + +/* + * Sequence of STR tokens separated by dots. The dots are not + * preserved during concatenation. + */ +str_dot_seq: STR + | str_dot_seq '.' STR + { + char *result = (char *) region_alloc(parser->rr_region, + $1.len + $3.len + 1); + memcpy(result, $1.str, $1.len); + memcpy(result + $1.len, $3.str, $3.len); + $$.str = result; + $$.len = $1.len + $3.len; + $$.str[$$.len] = '\0'; + } + ; + +/* + * A string that can contain dots. + */ +dotted_str: STR + | '.' + { + $$.str = "."; + $$.len = 1; + } + | dotted_str '.' + { + char *result = (char *) region_alloc(parser->rr_region, + $1.len + 2); + memcpy(result, $1.str, $1.len); + result[$1.len] = '.'; + $$.str = result; + $$.len = $1.len + 1; + $$.str[$$.len] = '\0'; + } + | dotted_str '.' STR + { + char *result = (char *) region_alloc(parser->rr_region, + $1.len + $3.len + 2); + memcpy(result, $1.str, $1.len); + result[$1.len] = '.'; + memcpy(result + $1.len + 1, $3.str, $3.len); + $$.str = result; + $$.len = $1.len + $3.len + 1; + $$.str[$$.len] = '\0'; + } + ; + +/* define what we can parse */ +type_and_rdata: + /* + * All supported RR types. We don't support NULL and types marked obsolete. + */ + T_A sp rdata_a + | T_A sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_NS sp rdata_domain_name + | T_NS sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_MD sp rdata_domain_name { zc_warning_prev_line("MD is obsolete"); } + | T_MD sp rdata_unknown + { + zc_warning_prev_line("MD is obsolete"); + $$ = $1; parse_unknown_rdata($1, $3); + } + | T_MF sp rdata_domain_name { zc_warning_prev_line("MF is obsolete"); } + | T_MF sp rdata_unknown + { + zc_warning_prev_line("MF is obsolete"); + $$ = $1; + parse_unknown_rdata($1, $3); + } + | T_CNAME sp rdata_domain_name + | T_CNAME sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_SOA sp rdata_soa + | T_SOA sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_MB sp rdata_domain_name { zc_warning_prev_line("MB is obsolete"); } + | T_MB sp rdata_unknown + { + zc_warning_prev_line("MB is obsolete"); + $$ = $1; + parse_unknown_rdata($1, $3); + } + | T_MG sp rdata_domain_name + | T_MG sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_MR sp rdata_domain_name + | T_MR sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + /* NULL */ + | T_WKS sp rdata_wks + | T_WKS sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_PTR sp rdata_domain_name + | T_PTR sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_HINFO sp rdata_hinfo + | T_HINFO sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_MINFO sp rdata_minfo /* Experimental */ + | T_MINFO sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_MX sp rdata_mx + | T_MX sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_TXT sp rdata_txt + | T_TXT sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_SPF sp rdata_txt + | T_SPF sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_RP sp rdata_rp /* RFC 1183 */ + | T_RP sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_AFSDB sp rdata_afsdb /* RFC 1183 */ + | T_AFSDB sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_X25 sp rdata_x25 /* RFC 1183 */ + | T_X25 sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_ISDN sp rdata_isdn /* RFC 1183 */ + | T_ISDN sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_IPSECKEY sp rdata_ipseckey /* RFC 4025 */ + | T_IPSECKEY sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_DHCID sp rdata_dhcid + | T_DHCID sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_RT sp rdata_rt /* RFC 1183 */ + | T_RT sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_NSAP sp rdata_nsap /* RFC 1706 */ + | T_NSAP sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_SIG sp rdata_rrsig + | T_SIG sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_KEY sp rdata_dnskey + | T_KEY sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_PX sp rdata_px /* RFC 2163 */ + | T_PX sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_AAAA sp rdata_aaaa + | T_AAAA sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_LOC sp rdata_loc + | T_LOC sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_NXT sp rdata_nxt + | T_NXT sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_SRV sp rdata_srv + | T_SRV sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_NAPTR sp rdata_naptr /* RFC 2915 */ + | T_NAPTR sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_KX sp rdata_kx /* RFC 2230 */ + | T_KX sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_CERT sp rdata_cert /* RFC 2538 */ + | T_CERT sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_DNAME sp rdata_domain_name /* RFC 2672 */ + | T_DNAME sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_APL trail /* RFC 3123 */ + | T_APL sp rdata_apl /* RFC 3123 */ + | T_APL sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_DS sp rdata_ds + | T_DS sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_DLV sp rdata_dlv { if (dlv_warn) { dlv_warn = 0; zc_warning_prev_line("DLV is experimental"); } } + | T_DLV sp rdata_unknown { if (dlv_warn) { dlv_warn = 0; zc_warning_prev_line("DLV is experimental"); } $$ = $1; parse_unknown_rdata($1, $3); } + | T_SSHFP sp rdata_sshfp + | T_SSHFP sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_RRSIG sp rdata_rrsig + | T_RRSIG sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_NSEC sp rdata_nsec + | T_NSEC sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_NSEC3 sp rdata_nsec3 + | T_NSEC3 sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_NSEC3PARAM sp rdata_nsec3_param + | T_NSEC3PARAM sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_DNSKEY sp rdata_dnskey + | T_DNSKEY sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_UTYPE sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | STR error NL + { + zc_error_prev_line("unrecognized RR type '%s'", $1.str); + } + ; + +/* + * + * below are all the definition for all the different rdata + * + */ + +rdata_a: dotted_str trail + { + zadd_rdata_wireformat(zparser_conv_a(parser->region, $1.str)); + } + ; + +rdata_domain_name: dname trail + { + /* convert a single dname record */ + zadd_rdata_domain($1); + } + ; + +rdata_soa: dname sp dname sp STR sp STR sp STR sp STR sp STR trail + { + /* convert the soa data */ + zadd_rdata_domain($1); /* prim. ns */ + zadd_rdata_domain($3); /* email */ + zadd_rdata_wireformat(zparser_conv_serial(parser->region, $5.str)); /* serial */ + zadd_rdata_wireformat(zparser_conv_period(parser->region, $7.str)); /* refresh */ + zadd_rdata_wireformat(zparser_conv_period(parser->region, $9.str)); /* retry */ + zadd_rdata_wireformat(zparser_conv_period(parser->region, $11.str)); /* expire */ + zadd_rdata_wireformat(zparser_conv_period(parser->region, $13.str)); /* minimum */ + } + ; + +rdata_wks: dotted_str sp STR sp concatenated_str_seq trail + { + zadd_rdata_wireformat(zparser_conv_a(parser->region, $1.str)); /* address */ + zadd_rdata_wireformat(zparser_conv_services(parser->region, $3.str, $5.str)); /* protocol and services */ + } + ; + +rdata_hinfo: STR sp STR trail + { + zadd_rdata_wireformat(zparser_conv_text(parser->region, $1.str, $1.len)); /* CPU */ + zadd_rdata_wireformat(zparser_conv_text(parser->region, $3.str, $3.len)); /* OS*/ + } + ; + +rdata_minfo: dname sp dname trail + { + /* convert a single dname record */ + zadd_rdata_domain($1); + zadd_rdata_domain($3); + } + ; + +rdata_mx: STR sp dname trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* priority */ + zadd_rdata_domain($3); /* MX host */ + } + ; + +rdata_txt: str_seq trail + ; + +/* RFC 1183 */ +rdata_rp: dname sp dname trail + { + zadd_rdata_domain($1); /* mbox d-name */ + zadd_rdata_domain($3); /* txt d-name */ + } + ; + +/* RFC 1183 */ +rdata_afsdb: STR sp dname trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* subtype */ + zadd_rdata_domain($3); /* domain name */ + } + ; + +/* RFC 1183 */ +rdata_x25: STR trail + { + zadd_rdata_wireformat(zparser_conv_text(parser->region, $1.str, $1.len)); /* X.25 address. */ + } + ; + +/* RFC 1183 */ +rdata_isdn: STR trail + { + zadd_rdata_wireformat(zparser_conv_text(parser->region, $1.str, $1.len)); /* address */ + } + | STR sp STR trail + { + zadd_rdata_wireformat(zparser_conv_text(parser->region, $1.str, $1.len)); /* address */ + zadd_rdata_wireformat(zparser_conv_text(parser->region, $3.str, $3.len)); /* sub-address */ + } + ; + +/* RFC 1183 */ +rdata_rt: STR sp dname trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* preference */ + zadd_rdata_domain($3); /* intermediate host */ + } + ; + +/* RFC 1706 */ +rdata_nsap: str_dot_seq trail + { + /* String must start with "0x" or "0X". */ + if (strncasecmp($1.str, "0x", 2) != 0) { + zc_error_prev_line("NSAP rdata must start with '0x'"); + } else { + zadd_rdata_wireformat(zparser_conv_hex(parser->region, $1.str + 2, $1.len - 2)); /* NSAP */ + } + } + ; + +/* RFC 2163 */ +rdata_px: STR sp dname sp dname trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* preference */ + zadd_rdata_domain($3); /* MAP822 */ + zadd_rdata_domain($5); /* MAPX400 */ + } + ; + +rdata_aaaa: dotted_str trail + { + zadd_rdata_wireformat(zparser_conv_aaaa(parser->region, $1.str)); /* IPv6 address */ + } + ; + +rdata_loc: concatenated_str_seq trail + { + zadd_rdata_wireformat(zparser_conv_loc(parser->region, $1.str)); /* Location */ + } + ; + +rdata_nxt: dname sp nxt_seq trail + { + zadd_rdata_domain($1); /* nxt name */ + zadd_rdata_wireformat(zparser_conv_nxt(parser->region, nxtbits)); /* nxt bitlist */ + memset(nxtbits, 0, sizeof(nxtbits)); + } + ; + +rdata_srv: STR sp STR sp STR sp dname trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* prio */ + zadd_rdata_wireformat(zparser_conv_short(parser->region, $3.str)); /* weight */ + zadd_rdata_wireformat(zparser_conv_short(parser->region, $5.str)); /* port */ + zadd_rdata_domain($7); /* target name */ + } + ; + +/* RFC 2915 */ +rdata_naptr: STR sp STR sp STR sp STR sp STR sp dname trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* order */ + zadd_rdata_wireformat(zparser_conv_short(parser->region, $3.str)); /* preference */ + zadd_rdata_wireformat(zparser_conv_text(parser->region, $5.str, $5.len)); /* flags */ + zadd_rdata_wireformat(zparser_conv_text(parser->region, $7.str, $7.len)); /* service */ + zadd_rdata_wireformat(zparser_conv_text(parser->region, $9.str, $9.len)); /* regexp */ + zadd_rdata_domain($11); /* target name */ + } + ; + +/* RFC 2230 */ +rdata_kx: STR sp dname trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* preference */ + zadd_rdata_domain($3); /* exchanger */ + } + ; + +/* RFC 2538 */ +rdata_cert: STR sp STR sp STR sp str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_certificate_type(parser->region, $1.str)); /* type */ + zadd_rdata_wireformat(zparser_conv_short(parser->region, $3.str)); /* key tag */ + zadd_rdata_wireformat(zparser_conv_algorithm(parser->region, $5.str)); /* algorithm */ + zadd_rdata_wireformat(zparser_conv_b64(parser->region, $7.str)); /* certificate or CRL */ + } + ; + +/* RFC 3123 */ +rdata_apl: rdata_apl_seq trail + ; + +rdata_apl_seq: dotted_str + { + zadd_rdata_wireformat(zparser_conv_apl_rdata(parser->region, $1.str)); + } + | rdata_apl_seq sp dotted_str + { + zadd_rdata_wireformat(zparser_conv_apl_rdata(parser->region, $3.str)); + } + ; + +rdata_ds: STR sp STR sp STR sp str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* keytag */ + zadd_rdata_wireformat(zparser_conv_algorithm(parser->region, $3.str)); /* alg */ + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $5.str)); /* type */ + zadd_rdata_wireformat(zparser_conv_hex(parser->region, $7.str, $7.len)); /* hash */ + } + ; + +rdata_dlv: STR sp STR sp STR sp str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* keytag */ + zadd_rdata_wireformat(zparser_conv_algorithm(parser->region, $3.str)); /* alg */ + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $5.str)); /* type */ + zadd_rdata_wireformat(zparser_conv_hex(parser->region, $7.str, $7.len)); /* hash */ + } + ; + +rdata_sshfp: STR sp STR sp str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $1.str)); /* alg */ + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $3.str)); /* fp type */ + zadd_rdata_wireformat(zparser_conv_hex(parser->region, $5.str, $5.len)); /* hash */ + } + ; + +rdata_dhcid: str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_b64(parser->region, $1.str)); /* data blob */ + } + ; + +rdata_rrsig: STR sp STR sp STR sp STR sp STR sp STR sp STR sp wire_dname sp str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_rrtype(parser->region, $1.str)); /* rr covered */ + zadd_rdata_wireformat(zparser_conv_algorithm(parser->region, $3.str)); /* alg */ + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $5.str)); /* # labels */ + zadd_rdata_wireformat(zparser_conv_period(parser->region, $7.str)); /* # orig TTL */ + zadd_rdata_wireformat(zparser_conv_time(parser->region, $9.str)); /* sig exp */ + zadd_rdata_wireformat(zparser_conv_time(parser->region, $11.str)); /* sig inc */ + zadd_rdata_wireformat(zparser_conv_short(parser->region, $13.str)); /* key id */ + zadd_rdata_wireformat(zparser_conv_dns_name(parser->region, + (const uint8_t*) $15.str,$15.len)); /* sig name */ + zadd_rdata_wireformat(zparser_conv_b64(parser->region, $17.str)); /* sig data */ + } + ; + +rdata_nsec: wire_dname nsec_seq + { + zadd_rdata_wireformat(zparser_conv_dns_name(parser->region, + (const uint8_t*) $1.str, $1.len)); /* nsec name */ + zadd_rdata_wireformat(zparser_conv_nsec(parser->region, nsecbits)); /* nsec bitlist */ + memset(nsecbits, 0, sizeof(nsecbits)); + nsec_highest_rcode = 0; + } + ; + +rdata_nsec3: STR sp STR sp STR sp STR sp STR nsec_seq + { +#ifdef NSEC3 + nsec3_add_params($1.str, $3.str, $5.str, $7.str, $7.len); + + zadd_rdata_wireformat(zparser_conv_b32(parser->region, $9.str)); /* next hashed name */ + zadd_rdata_wireformat(zparser_conv_nsec(parser->region, nsecbits)); /* nsec bitlist */ + memset(nsecbits, 0, sizeof(nsecbits)); + nsec_highest_rcode = 0; +#else + zc_error_prev_line("nsec3 not supported"); +#endif /* NSEC3 */ + } + ; + +rdata_nsec3_param: STR sp STR sp STR sp STR trail + { +#ifdef NSEC3 + nsec3_add_params($1.str, $3.str, $5.str, $7.str, $7.len); +#else + zc_error_prev_line("nsec3 not supported"); +#endif /* NSEC3 */ + } + ; + +rdata_dnskey: STR sp STR sp STR sp str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_short(parser->region, $1.str)); /* flags */ + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $3.str)); /* proto */ + zadd_rdata_wireformat(zparser_conv_algorithm(parser->region, $5.str)); /* alg */ + zadd_rdata_wireformat(zparser_conv_b64(parser->region, $7.str)); /* hash */ + } + ; + +rdata_ipsec_base: STR sp STR sp STR sp dotted_str + { + const dname_type* name = 0; + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $1.str)); /* precedence */ + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $3.str)); /* gateway type */ + zadd_rdata_wireformat(zparser_conv_byte(parser->region, $5.str)); /* algorithm */ + switch(atoi($3.str)) { + case IPSECKEY_NOGATEWAY: + zadd_rdata_wireformat(alloc_rdata_init(parser->region, "", 0)); + break; + case IPSECKEY_IP4: + zadd_rdata_wireformat(zparser_conv_a(parser->region, $7.str)); + break; + case IPSECKEY_IP6: + zadd_rdata_wireformat(zparser_conv_aaaa(parser->region, $7.str)); + break; + case IPSECKEY_DNAME: + /* convert and insert the dname */ + if(strlen($7.str) == 0) + zc_error_prev_line("IPSECKEY must specify gateway name"); + if(!(name = dname_parse(parser->region, $7.str))) + zc_error_prev_line("IPSECKEY bad gateway dname %s", $7.str); + if($7.str[strlen($7.str)-1] != '.') + name = dname_concatenate(parser->rr_region, name, + domain_dname(parser->origin)); + zadd_rdata_wireformat(alloc_rdata_init(parser->region, + dname_name(name), name->name_size)); + break; + default: + zc_error_prev_line("unknown IPSECKEY gateway type"); + } + } + ; + +rdata_ipseckey: rdata_ipsec_base sp str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_b64(parser->region, $3.str)); /* public key */ + } + | rdata_ipsec_base trail + ; + +rdata_unknown: URR sp STR sp str_sp_seq trail + { + /* $2 is the number of octects, currently ignored */ + $$ = zparser_conv_hex(parser->region, $5.str, $5.len); + + } + | URR sp STR trail + { + $$ = zparser_conv_hex(parser->region, "", 0); + } + | URR error NL + { + $$ = zparser_conv_hex(parser->region, "", 0); + } + ; +%% + +int +yywrap(void) +{ + return 1; +} + +/* + * Create the parser. + */ +zparser_type * +zparser_create(region_type *region, region_type *rr_region, namedb_type *db) +{ + zparser_type *result; + + result = (zparser_type *) region_alloc(region, sizeof(zparser_type)); + result->region = region; + result->rr_region = rr_region; + result->db = db; + + result->filename = NULL; + result->current_zone = NULL; + result->origin = NULL; + result->prev_dname = NULL; + result->default_apex = NULL; + + result->temporary_rdatas = (rdata_atom_type *) region_alloc( + result->region, MAXRDATALEN * sizeof(rdata_atom_type)); + + return result; +} + +/* + * Initialize the parser for a new zone file. + */ +void +zparser_init(const char *filename, uint32_t ttl, uint16_t klass, + const dname_type *origin) +{ + memset(nxtbits, 0, sizeof(nxtbits)); + memset(nsecbits, 0, sizeof(nsecbits)); + nsec_highest_rcode = 0; + + parser->default_ttl = ttl; + parser->default_class = klass; + parser->current_zone = NULL; + parser->origin = domain_table_insert(parser->db->domains, origin); + parser->prev_dname = parser->origin; + parser->default_apex = parser->origin; + parser->error_occurred = 0; + parser->errors = 0; + parser->line = 1; + parser->filename = filename; + parser->current_rr.rdata_count = 0; + parser->current_rr.rdatas = parser->temporary_rdatas; +} + +void +yyerror(const char *message) +{ + zc_error("%s", message); +} + +static void +error_va_list(unsigned line, const char *fmt, va_list args) +{ + if (parser->filename) { + fprintf(stderr, "%s:%u: ", parser->filename, line); + } + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + + ++parser->errors; + parser->error_occurred = 1; +} + +/* the line counting sux, to say the least + * with this grose hack we try do give sane + * numbers back */ +void +zc_error_prev_line(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + error_va_list(parser->line - 1, fmt, args); + va_end(args); +} + +void +zc_error(const char *fmt, ...) +{ + /* send an error message to stderr */ + va_list args; + va_start(args, fmt); + error_va_list(parser->line, fmt, args); + va_end(args); +} + +static void +warning_va_list(unsigned line, const char *fmt, va_list args) +{ + if (parser->filename) { + fprintf(stderr, "%s:%u: ", parser->filename, line); + } + fprintf(stderr, "warning: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} + +void +zc_warning_prev_line(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + warning_va_list(parser->line - 1, fmt, args); + va_end(args); +} + +void +zc_warning(const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + warning_va_list(parser->line, fmt, args); + va_end(args); +} + +#ifdef NSEC3 +static void +nsec3_add_params(const char* hashalgo_str, const char* flag_str, + const char* iter_str, const char* salt_str, int salt_len) +{ + zadd_rdata_wireformat(zparser_conv_byte(parser->region, hashalgo_str)); + zadd_rdata_wireformat(zparser_conv_byte(parser->region, flag_str)); + zadd_rdata_wireformat(zparser_conv_short(parser->region, iter_str)); + + /* salt */ + if(strcmp(salt_str, "-") != 0) + zadd_rdata_wireformat(zparser_conv_hex_length(parser->region, + salt_str, salt_len)); + else + zadd_rdata_wireformat(alloc_rdata_init(parser->region, "", 1)); +} +#endif /* NSEC3 */ |