summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/nsd/LICENSE30
-rw-r--r--usr.sbin/nsd/Makefile.in623
-rw-r--r--usr.sbin/nsd/acx_nlnetlabs.m41152
-rw-r--r--usr.sbin/nsd/answer.c91
-rw-r--r--usr.sbin/nsd/answer.h47
-rw-r--r--usr.sbin/nsd/axfr.c201
-rw-r--r--usr.sbin/nsd/axfr.h25
-rw-r--r--usr.sbin/nsd/buffer.c131
-rw-r--r--usr.sbin/nsd/buffer.h385
-rw-r--r--usr.sbin/nsd/compat/b64_ntop.c183
-rw-r--r--usr.sbin/nsd/compat/b64_pton.c250
-rw-r--r--usr.sbin/nsd/compat/basename.c18
-rw-r--r--usr.sbin/nsd/compat/fake-rfc2553.c227
-rw-r--r--usr.sbin/nsd/compat/fake-rfc2553.h181
-rw-r--r--usr.sbin/nsd/compat/inet_aton.c178
-rw-r--r--usr.sbin/nsd/compat/inet_ntop.c208
-rw-r--r--usr.sbin/nsd/compat/inet_pton.c226
-rw-r--r--usr.sbin/nsd/compat/malloc.c22
-rw-r--r--usr.sbin/nsd/compat/memmove.c43
-rw-r--r--usr.sbin/nsd/compat/pselect.c44
-rw-r--r--usr.sbin/nsd/compat/snprintf.c770
-rw-r--r--usr.sbin/nsd/compat/strlcpy.c57
-rw-r--r--usr.sbin/nsd/compat/strptime.c349
-rw-r--r--usr.sbin/nsd/config.h.in629
-rw-r--r--usr.sbin/nsd/configlexer.lex196
-rw-r--r--usr.sbin/nsd/configparser.y470
-rw-r--r--usr.sbin/nsd/configure12143
-rw-r--r--usr.sbin/nsd/configure.ac792
-rw-r--r--usr.sbin/nsd/configyyrename.h89
-rw-r--r--usr.sbin/nsd/dbaccess.c460
-rw-r--r--usr.sbin/nsd/dbcreate.c287
-rw-r--r--usr.sbin/nsd/difffile.c1490
-rw-r--r--usr.sbin/nsd/difffile.h70
-rw-r--r--usr.sbin/nsd/dname.c497
-rw-r--r--usr.sbin/nsd/dname.h377
-rw-r--r--usr.sbin/nsd/dns.c561
-rw-r--r--usr.sbin/nsd/dns.h257
-rw-r--r--usr.sbin/nsd/edns.c116
-rw-r--r--usr.sbin/nsd/edns.h61
-rw-r--r--usr.sbin/nsd/install-sh251
-rw-r--r--usr.sbin/nsd/ipc.c839
-rw-r--r--usr.sbin/nsd/ipc.h93
-rw-r--r--usr.sbin/nsd/iterated_hash.c43
-rw-r--r--usr.sbin/nsd/iterated_hash.h22
-rw-r--r--usr.sbin/nsd/mkinstalldirs40
-rw-r--r--usr.sbin/nsd/namedb.c383
-rw-r--r--usr.sbin/nsd/namedb.h330
-rw-r--r--usr.sbin/nsd/netio.c268
-rw-r--r--usr.sbin/nsd/netio.h182
-rw-r--r--usr.sbin/nsd/nsd-checkconf.894
-rw-r--r--usr.sbin/nsd/nsd-checkconf.c507
-rw-r--r--usr.sbin/nsd/nsd-notify.866
-rw-r--r--usr.sbin/nsd/nsd-notify.c476
-rw-r--r--usr.sbin/nsd/nsd-patch.869
-rw-r--r--usr.sbin/nsd/nsd-patch.c429
-rw-r--r--usr.sbin/nsd/nsd-xfer.882
-rw-r--r--usr.sbin/nsd/nsd-xfer.c1078
-rw-r--r--usr.sbin/nsd/nsd.8266
-rw-r--r--usr.sbin/nsd/nsd.c1043
-rw-r--r--usr.sbin/nsd/nsd.conf.5514
-rw-r--r--usr.sbin/nsd/nsd.conf.sample.in180
-rw-r--r--usr.sbin/nsd/nsd.h231
-rw-r--r--usr.sbin/nsd/nsdc.8167
-rw-r--r--usr.sbin/nsd/nsdc.sh.in438
-rw-r--r--usr.sbin/nsd/nsec3.c624
-rw-r--r--usr.sbin/nsd/nsec3.h83
-rw-r--r--usr.sbin/nsd/options.c666
-rw-r--r--usr.sbin/nsd/options.h214
-rw-r--r--usr.sbin/nsd/packet.c298
-rw-r--r--usr.sbin/nsd/packet.h189
-rw-r--r--usr.sbin/nsd/query.c1432
-rw-r--r--usr.sbin/nsd/query.h217
-rw-r--r--usr.sbin/nsd/rbtree.c553
-rw-r--r--usr.sbin/nsd/rbtree.h76
-rw-r--r--usr.sbin/nsd/rdata.c765
-rw-r--r--usr.sbin/nsd/rdata.h60
-rw-r--r--usr.sbin/nsd/region-allocator.c459
-rw-r--r--usr.sbin/nsd/region-allocator.h114
-rw-r--r--usr.sbin/nsd/server.c1900
-rw-r--r--usr.sbin/nsd/tsig-openssl.c127
-rw-r--r--usr.sbin/nsd/tsig-openssl.h29
-rw-r--r--usr.sbin/nsd/tsig.c674
-rw-r--r--usr.sbin/nsd/tsig.h293
-rw-r--r--usr.sbin/nsd/util.c960
-rw-r--r--usr.sbin/nsd/util.h368
-rw-r--r--usr.sbin/nsd/xfrd-disk.c458
-rw-r--r--usr.sbin/nsd/xfrd-disk.h24
-rw-r--r--usr.sbin/nsd/xfrd-notify.c315
-rw-r--r--usr.sbin/nsd/xfrd-notify.h72
-rw-r--r--usr.sbin/nsd/xfrd-tcp.c585
-rw-r--r--usr.sbin/nsd/xfrd-tcp.h125
-rw-r--r--usr.sbin/nsd/xfrd.c1684
-rw-r--r--usr.sbin/nsd/xfrd.h295
-rw-r--r--usr.sbin/nsd/zlexer.lex383
-rw-r--r--usr.sbin/nsd/zonec.8126
-rw-r--r--usr.sbin/nsd/zonec.c1571
-rw-r--r--usr.sbin/nsd/zonec.h123
-rw-r--r--usr.sbin/nsd/zparser.y1095
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, &timestamp[0]) ||
+ !diff_read_32(in, &timestamp[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, &timestamp[0]) ||
+ !diff_read_32(df, &timestamp[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, &timestamp[0]) ||
+ !diff_read_32(df, &timestamp[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(&current_timeval, NULL) == -1) {
+ log_msg(LOG_CRIT, "gettimeofday: %s, aborting.", strerror(errno));
+ abort();
+ }
+ timeval_to_timespec(&netio->cached_current_time, &current_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, &timestamp[0]) ||
+ !diff_read_32(in, &timestamp[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, &timestamp[0]) ||
+ !diff_read_32(in, &timestamp[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 = &paramset->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), &paramset->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, &not->notify_send_handler);
+
+#ifdef TSIG
+ tsig_create_record_custom(&not->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, &deg, "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 */