summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2014-12-10 03:05:47 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2014-12-10 03:05:47 +0000
commit416ef5e2bf59bb2e45066a1d5d91ac677c0f48e5 (patch)
tree1c746d673dcec7a8488c6ac51db8245411034376
downloads6-dns-416ef5e2bf59bb2e45066a1d5d91ac677c0f48e5.tar.xz
Initial commit
-rw-r--r--.gitignore5
-rw-r--r--AUTHORS6
-rw-r--r--COPYING13
-rw-r--r--INSTALL132
-rw-r--r--Makefile121
-rw-r--r--README23
-rw-r--r--README.macosx4
-rw-r--r--README.solaris12
-rwxr-xr-xconfigure378
-rw-r--r--doc/getaddrinfo.html94
-rw-r--r--doc/index.html175
-rw-r--r--doc/libresolv.html140
-rw-r--r--doc/libs6dns/index.html104
-rw-r--r--doc/libs6dns/s6dns-domain.html137
-rw-r--r--doc/libs6dns/s6dns-engine.html255
-rw-r--r--doc/libs6dns/s6dns-fmt.html92
-rw-r--r--doc/libs6dns/s6dns-ip46.html69
-rw-r--r--doc/libs6dns/s6dns-message.html261
-rw-r--r--doc/libs6dns/s6dns-rci.html109
-rw-r--r--doc/libs6dns/s6dns-resolve.html387
-rw-r--r--doc/s6-dnsip4-filter.html111
-rw-r--r--doc/s6-dnsip4.html63
-rw-r--r--doc/s6-dnsip6-filter.html99
-rw-r--r--doc/s6-dnsip6.html73
-rw-r--r--doc/s6-dnsmx.html64
-rw-r--r--doc/s6-dnsname-filter.html103
-rw-r--r--doc/s6-dnsname.html72
-rw-r--r--doc/s6-dnsns.html63
-rw-r--r--doc/s6-dnsq.html82
-rw-r--r--doc/s6-dnsqr.html80
-rw-r--r--doc/s6-dnsqualify.html41
-rw-r--r--doc/s6-dnssoa.html65
-rw-r--r--doc/s6-dnssrv.html65
-rw-r--r--doc/s6-dnstxt.html77
-rw-r--r--doc/s6-randomip.html57
-rw-r--r--doc/skadns/index.html262
-rw-r--r--doc/skadns/skadnsd.html120
-rw-r--r--doc/upgrade.html32
-rw-r--r--package/deps-build1
-rw-r--r--package/deps.mak150
-rw-r--r--package/info4
-rw-r--r--package/modes16
-rw-r--r--package/targets.mak27
-rwxr-xr-xpatch-for-solaris17
-rw-r--r--src/clients/deps-exe/s6-dnsip44
-rw-r--r--src/clients/deps-exe/s6-dnsip4-filter6
-rw-r--r--src/clients/deps-exe/s6-dnsip64
-rw-r--r--src/clients/deps-exe/s6-dnsip6-filter6
-rw-r--r--src/clients/deps-exe/s6-dnsmx4
-rw-r--r--src/clients/deps-exe/s6-dnsname4
-rw-r--r--src/clients/deps-exe/s6-dnsname-filter6
-rw-r--r--src/clients/deps-exe/s6-dnsns4
-rw-r--r--src/clients/deps-exe/s6-dnsq4
-rw-r--r--src/clients/deps-exe/s6-dnsqr4
-rw-r--r--src/clients/deps-exe/s6-dnsqualify2
-rw-r--r--src/clients/deps-exe/s6-dnssoa4
-rw-r--r--src/clients/deps-exe/s6-dnssrv4
-rw-r--r--src/clients/deps-exe/s6-dnstxt4
-rw-r--r--src/clients/deps-exe/s6-randomip3
-rw-r--r--src/clients/deps-lib/s6dnsgenericfilter2
-rw-r--r--src/clients/s6-dnsip4-filter.c52
-rw-r--r--src/clients/s6-dnsip4.c62
-rw-r--r--src/clients/s6-dnsip6-filter.c52
-rw-r--r--src/clients/s6-dnsip6.c62
-rw-r--r--src/clients/s6-dnsmx.c64
-rw-r--r--src/clients/s6-dnsname-filter.c83
-rw-r--r--src/clients/s6-dnsname.c75
-rw-r--r--src/clients/s6-dnsns.c61
-rw-r--r--src/clients/s6-dnsq.c99
-rw-r--r--src/clients/s6-dnsqr.c74
-rw-r--r--src/clients/s6-dnsqualify.c34
-rw-r--r--src/clients/s6-dnssoa.c63
-rw-r--r--src/clients/s6-dnssrv.c75
-rw-r--r--src/clients/s6-dnstxt.c78
-rw-r--r--src/clients/s6-randomip.c62
-rw-r--r--src/clients/s6dns-generic-filter.h22
-rw-r--r--src/clients/s6dns_generic_filter_main.c230
-rw-r--r--src/clients/s6dns_namescanner.c12
-rw-r--r--src/include/s6-dns/s6dns-analyze.h39
-rw-r--r--src/include/s6-dns/s6dns-constants.h52
-rw-r--r--src/include/s6-dns/s6dns-debug.h16
-rw-r--r--src/include/s6-dns/s6dns-domain.h47
-rw-r--r--src/include/s6-dns/s6dns-engine.h108
-rw-r--r--src/include/s6-dns/s6dns-fmt.h29
-rw-r--r--src/include/s6-dns/s6dns-ip46.h29
-rw-r--r--src/include/s6-dns/s6dns-message.h175
-rw-r--r--src/include/s6-dns/s6dns-rci.h29
-rw-r--r--src/include/s6-dns/s6dns-resolve.h242
-rw-r--r--src/include/s6-dns/s6dns.h18
-rw-r--r--src/include/s6-dns/skadns.h73
-rwxr-xr-xsrc/libs6dns/deps-lib/s6dns88
-rw-r--r--src/libs6dns/s6dns-message-internal.h12
-rw-r--r--src/libs6dns/s6dns_analyze_packet.c127
-rw-r--r--src/libs6dns/s6dns_analyze_qtype_parse.c46
-rw-r--r--src/libs6dns/s6dns_analyze_record.c37
-rw-r--r--src/libs6dns/s6dns_analyze_record_a.c17
-rw-r--r--src/libs6dns/s6dns_analyze_record_aaaa.c17
-rw-r--r--src/libs6dns/s6dns_analyze_record_domain.c23
-rw-r--r--src/libs6dns/s6dns_analyze_record_hinfo.c22
-rw-r--r--src/libs6dns/s6dns_analyze_record_mx.c22
-rw-r--r--src/libs6dns/s6dns_analyze_record_soa.c22
-rw-r--r--src/libs6dns/s6dns_analyze_record_srv.c22
-rw-r--r--src/libs6dns/s6dns_analyze_record_strings.c23
-rw-r--r--src/libs6dns/s6dns_analyze_record_unknown.c25
-rw-r--r--src/libs6dns/s6dns_analyze_rtypetable.c20
-rw-r--r--src/libs6dns/s6dns_constants_error.c23
-rw-r--r--src/libs6dns/s6dns_constants_error_str.c11
-rw-r--r--src/libs6dns/s6dns_debug_dumpdt_post_recv.c14
-rw-r--r--src/libs6dns/s6dns_debug_dumpdt_post_send.c30
-rw-r--r--src/libs6dns/s6dns_debug_dumpdt_pre_send.c59
-rw-r--r--src/libs6dns/s6dns_debug_dumpdt_stderr.c7
-rw-r--r--src/libs6dns/s6dns_debug_dumpdt_stdout.c7
-rw-r--r--src/libs6dns/s6dns_debughook_zero.c5
-rw-r--r--src/libs6dns/s6dns_domain_arpafromip4.c19
-rw-r--r--src/libs6dns/s6dns_domain_arpafromip6.c21
-rw-r--r--src/libs6dns/s6dns_domain_decode.c30
-rw-r--r--src/libs6dns/s6dns_domain_encode.c21
-rw-r--r--src/libs6dns/s6dns_domain_encodelist.c11
-rw-r--r--src/libs6dns/s6dns_domain_fromstring.c30
-rw-r--r--src/libs6dns/s6dns_domain_fromstring_noqualify_encode.c10
-rw-r--r--src/libs6dns/s6dns_domain_fromstring_qualify_encode.c10
-rw-r--r--src/libs6dns/s6dns_domain_noqualify.c14
-rw-r--r--src/libs6dns/s6dns_domain_qualify.c30
-rw-r--r--src/libs6dns/s6dns_domain_tostring.c21
-rw-r--r--src/libs6dns/s6dns_engine.c372
-rw-r--r--src/libs6dns/s6dns_engine_free.c11
-rw-r--r--src/libs6dns/s6dns_engine_freen.c8
-rw-r--r--src/libs6dns/s6dns_engine_here.c7
-rw-r--r--src/libs6dns/s6dns_engine_nextdeadline.c10
-rw-r--r--src/libs6dns/s6dns_engine_zero.c5
-rw-r--r--src/libs6dns/s6dns_fmt_domainlist.c25
-rw-r--r--src/libs6dns/s6dns_fmt_hinfo.c15
-rw-r--r--src/libs6dns/s6dns_fmt_mx.c19
-rw-r--r--src/libs6dns/s6dns_fmt_soa.c44
-rw-r--r--src/libs6dns/s6dns_fmt_srv.c29
-rw-r--r--src/libs6dns/s6dns_message_counts_next.c12
-rw-r--r--src/libs6dns/s6dns_message_counts_pack.c12
-rw-r--r--src/libs6dns/s6dns_message_counts_unpack.c12
-rw-r--r--src/libs6dns/s6dns_message_counts_zero.c5
-rw-r--r--src/libs6dns/s6dns_message_get_domain.c11
-rw-r--r--src/libs6dns/s6dns_message_get_domain_internal.c47
-rw-r--r--src/libs6dns/s6dns_message_get_hinfo.c9
-rw-r--r--src/libs6dns/s6dns_message_get_mx.c14
-rw-r--r--src/libs6dns/s6dns_message_get_soa.c19
-rw-r--r--src/libs6dns/s6dns_message_get_srv.c15
-rw-r--r--src/libs6dns/s6dns_message_get_string.c13
-rw-r--r--src/libs6dns/s6dns_message_get_string_internal.c16
-rw-r--r--src/libs6dns/s6dns_message_get_strings.c17
-rw-r--r--src/libs6dns/s6dns_message_header_pack.c12
-rw-r--r--src/libs6dns/s6dns_message_header_unpack.c18
-rw-r--r--src/libs6dns/s6dns_message_header_zero.c5
-rw-r--r--src/libs6dns/s6dns_message_parse.c37
-rw-r--r--src/libs6dns/s6dns_message_parse_answer_a.c16
-rw-r--r--src/libs6dns/s6dns_message_parse_answer_aaaa.c16
-rw-r--r--src/libs6dns/s6dns_message_parse_answer_domain.c21
-rw-r--r--src/libs6dns/s6dns_message_parse_answer_hinfo.c21
-rw-r--r--src/libs6dns/s6dns_message_parse_answer_mx.c21
-rw-r--r--src/libs6dns/s6dns_message_parse_answer_soa.c21
-rw-r--r--src/libs6dns/s6dns_message_parse_answer_srv.c21
-rw-r--r--src/libs6dns/s6dns_message_parse_answer_strings.c36
-rw-r--r--src/libs6dns/s6dns_message_parse_getrr.c19
-rw-r--r--src/libs6dns/s6dns_message_parse_init.c13
-rw-r--r--src/libs6dns/s6dns_message_parse_next.c11
-rw-r--r--src/libs6dns/s6dns_message_parse_skipqd.c15
-rw-r--r--src/libs6dns/s6dns_rci_free.c10
-rw-r--r--src/libs6dns/s6dns_rci_here.c7
-rw-r--r--src/libs6dns/s6dns_rci_init.c173
-rw-r--r--src/libs6dns/s6dns_rci_zero.c7
-rw-r--r--src/libs6dns/s6dns_resolve_core.c16
-rw-r--r--src/libs6dns/s6dns_resolve_dpag.c20
-rw-r--r--src/libs6dns/s6dns_resolve_mpag.c23
-rw-r--r--src/libs6dns/s6dns_resolve_name4.c23
-rw-r--r--src/libs6dns/s6dns_resolve_name6.c24
-rw-r--r--src/libs6dns/s6dns_resolve_parse.c21
-rw-r--r--src/libs6dns/s6dns_resolven_loop.c54
-rw-r--r--src/libs6dns/s6dns_resolven_parse.c47
-rw-r--r--src/libs6dns/s6dns_resolvenoq.c16
-rw-r--r--src/libs6dns/s6dns_resolvenoq_aaaaa.c50
-rw-r--r--src/libs6dns/s6dns_resolveq.c89
-rw-r--r--src/libs6dns/s6dns_resolveq_aaaaa.c96
-rwxr-xr-xsrc/skadns/deps-exe/skadnsd4
-rw-r--r--src/skadns/deps-lib/skadns10
-rw-r--r--src/skadns/skadns_cancel.c23
-rw-r--r--src/skadns/skadns_end.c24
-rw-r--r--src/skadns/skadns_packet.c16
-rw-r--r--src/skadns/skadns_packetlen.c16
-rw-r--r--src/skadns/skadns_release.c26
-rw-r--r--src/skadns/skadns_send.c41
-rw-r--r--src/skadns/skadns_start.c10
-rw-r--r--src/skadns/skadns_startf.c12
-rw-r--r--src/skadns/skadns_update.c45
-rw-r--r--src/skadns/skadns_zero.c5
-rw-r--r--src/skadns/skadnsd.c199
-rwxr-xr-xtools/gen-deps.sh79
-rwxr-xr-xtools/install.sh64
195 files changed, 9639 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5c6415e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.o
+*.a
+*.lo
+*.so
+*.so.*
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..b3b2e27
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,6 @@
+Main author:
+ Laurent Bercot <ska-skaware@skarnet.org>
+
+Thanks to:
+ Dan J. Bernstein <djb@cr.yp.to>
+ Vallo Kallaste <kalts@estpak.ee>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..63309ba
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,13 @@
+Copyright (c) 2011-2014 Laurent Bercot <ska-skaware@skarnet.org>
+
+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.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..1978a5a
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,132 @@
+Build Instructions
+------------------
+
+* Requirements
+ ------------
+
+ - A POSIX-compliant C development environment
+ - GNU make version 3.81 or later
+ - skalibs version 2.0.0.0 or later: http://skarnet.org/software/skalibs/
+
+ This software will run on any operating system that implements
+POSIX.1-2008, available at:
+ http://pubs.opengroup.org/onlinepubs/9699919799/
+
+
+* Standard usage
+ --------------
+
+ ./configure && make && sudo make install
+
+ will work for most users.
+ It will install the binaries in /bin and the static libraries in
+/usr/lib/s6-dns.
+
+ You can strip the binaries and libraries of their extra symbols via
+"make strip" before the "make install" phase. It will shave a few bytes
+off them.
+
+
+* Customization
+ -------------
+
+ You can customize paths via flags given to configure.
+ See ./configure --help for a list of all available configure options.
+
+
+* Environment variables
+ ---------------------
+
+ Controlling a build process via environment variables is a big and
+dangerous hammer. You should try and pass flags to configure instead;
+nevertheless, the standard environment variables are recognized.
+
+ The value of the CROSS_COMPILE environment variable will prefix the
+building tools' names. The --enable-cross option is preferred, see
+"Cross-compilation" below.
+
+ If the CC environment variable is set, its value will override compiler
+detection by configure.
+
+ The values of CFLAGS, CPPFLAGS and LDFLAGS will be appended to flags
+auto-detected by configure. To entirely override the flags set by
+configure, use make -e.
+
+ The value of LDLIBS will be appended by make to command lines that link
+an executable, even without the -e option.
+
+ The Makefile supports the DESTDIR convention for staging.
+
+
+* Shared libraries
+ ----------------
+
+ Software from skarnet.org is small enough that shared libraries are
+generally not worth using. Static linking is simpler and incurs less
+runtime overhead and less points of failure: so by default, shared
+libraries are not built and binaries are linked against the static
+versions of the skarnet.org libraries. Nevertheless, you can:
+ * build shared libraries: --enable-shared
+ * link binaries against shared libraries: --disable-allstatic
+
+
+* Static binaries
+ ---------------
+
+ By default, binaries are linked against static versions of all the
+libraries they depend on, except for the libc. You can enforce
+linking against the static libc with --enable-static-libc.
+
+ (If you are using a GNU/Linux system, be aware that the GNU libc
+behaves badly with static linking and produces huge executables,
+which is why it is not the default. Other libcs are better suited
+to static linking, for instance musl: http://musl-libc.org/)
+
+
+* Cross-compilation
+ -----------------
+
+ skarnet.org packages centralize all the difficulty of
+cross-compilation in one place: skalibs. Once you have
+cross-compiled skalibs, the rest is easy.
+
+ Use the --enable-cross=PREFIX option to configure, or simply
+--enable-cross if your default toolchain is a cross-compiling
+toolchain. And make sure to use the correct version of skalibs
+for your target, and the correct sysdeps directory, making use
+of the --with-include, --with-lib, --with-dynlib and --with-sysdeps
+options as necessary.
+
+
+* The slashpackage convention
+ ---------------------------
+
+ The slashpackage convention (http://cr.yp.to/slashpackage.html)
+is a package installation scheme that provides a few guarantees
+over other conventions such as the FHS, for instance fixed
+absolute pathnames. skarnet.org packages support it: use the
+--enable-slashpackage option to configure, or
+--enable-slashpackage=DIR for a prefixed DIR/package tree.
+This option will activate slashpackage support during the build
+and set slashpackage-compatible installation directories.
+Other options setting individual installation directories will be
+ignored.
+
+ When using slashpackage, two additional Makefile targets are
+available after "make install":
+ - "make update" changes the default version of the software to the
+freshly installed one. (This is useful when you have several installed
+versions of the same software, which slashpackage supports.)
+ - "make -L global-links" adds links from /command and /library.so to the
+default version of the binaries and shared libraries. The "-L" option to
+make is necessary because targets are symbolic links, and the default make
+behaviour is to check the pointed file's timestamp and not the symlink's
+timestamp.
+
+
+* Out-of-tree builds
+ ------------------
+
+ skarnet.org packages do not support out-of-tree builds. They
+are small, so it does not cost much to duplicate the entire
+source tree if parallel builds are needed.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7d8901c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,121 @@
+#
+# This Makefile requires GNU make.
+#
+# Do not make changes here.
+# Use the included .mak files.
+#
+
+it: all
+
+CC = $(error Please use ./configure first)
+
+include package/targets.mak
+include package/deps.mak
+-include config.mak
+
+version_m := $(basename $(version))
+version_M := $(basename $(version_m))
+version_l := $(basename $(version_M))
+CPPFLAGS_ALL := -iquote src/include-local -Isrc/include $(CPPFLAGS)
+CFLAGS_ALL := $(CFLAGS) -pipe -Wall
+CFLAGS_SHARED := -fPIC
+LDFLAGS_ALL := $(LDFLAGS)
+LDFLAGS_SHARED := -shared
+LDLIBS_ALL := $(LDLIBS)
+REALCC = $(CROSS_COMPILE)$(CC)
+AR := $(CROSS_COMPILE)ar
+RANLIB := $(CROSS_COMPILE)ranlib
+STRIP := $(CROSS_COMPILE)strip
+INSTALL := ./tools/install.sh
+
+ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS) $(SBIN_TARGETS)
+ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS)
+ALL_INCLUDES := $(wildcard src/include/$(package)/*.h)
+
+all: $(ALL_LIBS) $(ALL_BINS) $(ALL_INCLUDES)
+
+clean:
+ @exec rm -f $(ALL_LIBS) $(ALL_BINS) $(wildcard src/*/*.o src/*/*.lo)
+
+distclean: clean
+ @exec rm -f config.mak src/include/${package}/config.h
+
+tgz: distclean
+ @. package/info && \
+ rm -rf /tmp/$$package-$$version && \
+ cp -a . /tmp/$$package-$$version && \
+ cd /tmp && \
+ tar -zpcv --owner=0 --group=0 --numeric-owner --exclude=.git* -f /tmp/$$package-$$version.tar.gz $$package-$$version && \
+ exec rm -rf /tmp/$$package-$$version
+
+strip: $(ALL_LIBS) $(ALL_BINS)
+ifneq ($(strip $(ALL_LIBS)),)
+ exec ${STRIP} -x -R .note -R .comment -R .note.GNU-stack $(ALL_LIBS)
+endif
+ifneq ($(strip $(ALL_BINS)),)
+ exec ${STRIP} -R .note -R .comment -R .note.GNU-stack $(ALL_BINS)
+endif
+
+install: install-dynlib install-libexec install-bin install-sbin install-lib install-include
+install-dynlib: $(SHARED_LIBS:lib%.so=$(DESTDIR)$(dynlibdir)/lib%.so)
+install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%)
+install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%)
+install-sbin: $(SBIN_TARGETS:%=$(DESTDIR)$(sbindir)/%)
+install-lib: $(STATIC_LIBS:lib%.a=$(DESTDIR)$(libdir)/lib%.a)
+install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h)
+
+ifneq ($(exthome),)
+
+update:
+ exec $(INSTALL) -l $(notdir $(home)) $(DESTDIR)$(exthome)
+
+global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so=$(DESTDIR)$(sproot)/library.so/lib%.so) $(BIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) $(SBIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%)
+
+$(DESTDIR)$(sproot)/command/%: $(DESTDIR)$(home)/command/%
+ exec $(INSTALL) -D -l ..$(exthome)/command/$(<F) $@
+
+$(DESTDIR)$(sproot)/library.so/lib%.so: $(DESTDIR)$(dynlibdir)/lib%.so
+ exec $(INSTALL) -D -l ..$(exthome)/library.so/$(<F) $@
+
+.PHONY: update global-links
+
+endif
+
+$(DESTDIR)$(dynlibdir)/lib%.so: lib%.so
+ $(INSTALL) -D -m 755 $< $@.$(version) && \
+ $(INSTALL) -l $<.$(version) $@.$(version_m) && \
+ $(INSTALL) -l $<.$(version_m) $@.$(version_M) && \
+ $(INSTALL) -l $<.$(version_M) $@.$(version_l) && \
+ exec $(INSTALL) -l $<.$(version_l) $@
+
+$(DESTDIR)$(libexecdir)/% $(DESTDIR)$(bindir)/% $(DESTDIR)$(sbindir)/%: % package/modes
+ exec $(INSTALL) -D -m 600 $< $@
+ grep -- ^$(@F) < package/modes | { read name mode owner && \
+ if [ x$$owner != x ] ; then chown -- $$owner $@ ; fi && \
+ chmod $$mode $@ ; }
+
+$(DESTDIR)$(libdir)/lib%.a: lib%.a
+ $(INSTALL) -D -m 644 $< $@
+
+$(DESTDIR)$(includedir)/$(package)/%.h: src/include/$(package)/%.h
+ exec $(INSTALL) -D -m 644 $< $@
+
+%.o: %.c
+ exec $(REALCC) $(CPPFLAGS_ALL) $(CFLAGS_ALL) -c -o $@ $<
+
+%.lo: %.c
+ exec $(REALCC) $(CPPFLAGS_ALL) $(CFLAGS_ALL) $(CFLAGS_SHARED) -c -o $@ $<
+
+$(ALL_BINS):
+ exec $(REALCC) -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL) $(LDFLAGS_NOSHARED) $^ $(LDLIBS_ALL)
+
+lib%.a:
+ exec $(AR) rc $@ $^
+ exec $(RANLIB) $@
+
+lib%.so:
+ exec $(REALCC) -o $@ $(CFLAGS_ALL) $(CFLAGS_SHARED) $(LDFLAGS_ALL) $(LDFLAGS_SHARED) -Wl,-soname,$@.$(version_l) $^
+
+.PHONY: it all clean distclean tgz strip install install-dynlib install-bin install-sbin install-lib install-include
+
+.DELETE_ON_ERROR:
diff --git a/README b/README
new file mode 100644
index 0000000..b6bd67b
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+s6-dns - a DNS client suite
+---------------------------
+
+ s6-dns is a suite of DNS client programs and libraries for
+Unix systems, as an alternative to the BIND, djbdns, or
+other DNS clients.
+
+ See http://skarnet.org/software/s6-dns/ for details.
+
+
+* Installation
+ ------------
+
+ See the INSTALL file.
+
+
+* Contact information
+ -------------------
+
+ Laurent Bercot <ska-skaware at skarnet.org>
+
+ Please use the <skaware at list.skarnet.org> mailing-list for
+questions about s6-dns.
diff --git a/README.macosx b/README.macosx
new file mode 100644
index 0000000..d71a096
--- /dev/null
+++ b/README.macosx
@@ -0,0 +1,4 @@
+
+ This package will compile and run on Darwin (MacOS X), but the building of
+shared libraries is not supported.
+ Make sure you use the --disable-shared option to configure.
diff --git a/README.solaris b/README.solaris
new file mode 100644
index 0000000..91a5b26
--- /dev/null
+++ b/README.solaris
@@ -0,0 +1,12 @@
+
+ This package assumes the existence of a POSIX shell in /bin/sh.
+ On Solaris, /bin/sh is not POSIX. Most versions of Solaris provide
+a POSIX shell in /usr/xpg4/bin/sh.
+
+ To compile this package on Solaris, you will need to run
+
+ ./patch-for-solaris
+
+ before you run ./configure. This script will change the #! invocation
+of the configure script and various tools so that a POSIX shell is used
+for the compilation process.
diff --git a/configure b/configure
new file mode 100755
index 0000000..5ca8165
--- /dev/null
+++ b/configure
@@ -0,0 +1,378 @@
+#!/bin/sh
+
+usage () {
+cat <<EOF
+Usage: $0 [OPTION]... [VAR=VALUE]... [TARGET]
+
+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.
+
+System types:
+ --target=TARGET configure to run on target TARGET [detected]
+ --host=TARGET same as --target
+
+Installation directories:
+ --prefix=PREFIX main installation prefix [/]
+ --exec-prefix=EPREFIX installation prefix for executable files [PREFIX]
+
+Fine tuning of the installation directories:
+ --dynlibdir=DIR shared library files [PREFIX/lib]
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR admin executables [EPREFIX/sbin]
+ --libexecdir=DIR package-scoped executables [EPREFIX/libexec]
+ --libdir=DIR static library files [PREFIX/lib]
+ --includedir=DIR include files for the C compiler [PREFIX/include]
+
+Dependencies:
+ --with-sysdeps=DIR use sysdeps in DIR [/usr/lib/skalibs/sysdeps]
+ --with-include=DIR add DIR to the list of searched directories for headers
+ --with-lib=DIR add DIR to the list of searched directories for static libraries
+ --with-dynlib=DIR add DIR to the list of searched directories for shared libraries
+
+Optional features:
+ --enable-shared build shared libraries [disabled]
+ --disable-static do not build static libraries [enabled]
+ --disable-allstatic do not prefer linking against static libraries [enabled]
+ --enable-static-libc make entirely static binaries [disabled]
+ --enable-slashpackage[=ROOT] assume /package installation at ROOT [disabled]
+ --enable-cross=PREFIX prefix toolchain executable names with PREFIX [none]
+
+EOF
+exit 0
+}
+
+
+# Helper functions
+
+# If your system does not have printf, you can comment this, but it is
+# generally not a good idea to use echo.
+# See http://www.etalabs.net/sh_tricks.html
+echo () {
+ IFS=" "
+ printf %s\\n "$*"
+}
+
+quote () {
+ tr '\n' ' ' <<EOF | grep '^[-[:alnum:]_=,./:]* $' >/dev/null 2>&1 && { echo "$1" ; return 0 ; }
+$1
+EOF
+ echo "$1" | sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" -e "s#^'\([-[:alnum:]_,./:]*\)=\(.*\)\$#\1='\2#" -e "s|\*/|* /|g"
+}
+
+fail () {
+ echo "$*"
+ exit 1
+}
+
+fnmatch () {
+ eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac"
+}
+
+cmdexists () {
+ type "$1" >/dev/null 2>&1
+}
+
+trycc () {
+ test -z "$CC_AUTO" && cmdexists "$1" && CC_AUTO=$1
+}
+
+stripdir () {
+ while eval "fnmatch '*/' \"\${$1}\"" ; do
+ eval "$1=\${$1%/}"
+ done
+}
+
+tryflag () {
+ echo "checking whether compiler accepts $2 ..."
+ echo "typedef int x;" > "$tmpc"
+ if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO "$2" -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+ echo " ... yes"
+ eval "$1=\"\${$1} \$2\""
+ eval "$1=\${$1# }"
+ return 0
+ else
+ echo " ... no"
+ return 1
+ fi
+}
+
+tryldflag () {
+ echo "checking whether linker accepts $2 ..."
+ echo "typedef int x;" > "$tmpc"
+ if $CC_AUTO $CFLAGS_AUTO $LDFLAGS_AUTO -nostdlib "$2" -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+ echo " ... yes"
+ eval "$1=\"\${$1} \$2\""
+ eval "$1=\${$1# }"
+ return 0
+ else
+ echo " ... no"
+ return 1
+ fi
+}
+
+
+# Actual script
+
+. package/info
+
+CC_AUTO="$CC"
+CFLAGS_AUTO="$CFLAGS"
+CPPFLAGS_AUTO="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=600 $CPPFLAGS"
+LDFLAGS_AUTO="$LDFLAGS"
+LDFLAGS_NOSHARED=
+prefix=
+exec_prefix='$prefix'
+dynlibdir='$prefix/lib'
+libexecdir='$exec_prefix/libexec'
+bindir='$exec_prefix/bin'
+sbindir='$exec_prefix/sbin'
+libdir='$prefix/usr/lib/'$package
+includedir='$prefix/usr/include'
+sysdeps='$prefix/usr/lib/skalibs/sysdeps'
+manualsysdeps=false
+shared=false
+static=true
+slashpackage=false
+sproot=
+home=
+exthome=
+allstatic=true
+evenmorestatic=false
+addincpath=''
+addlibspath=''
+addlibdpath=''
+vpaths=''
+vpathd=''
+cross="$CROSS_COMPILE"
+
+for arg ; do
+ case "$arg" in
+ --help) usage ;;
+ --prefix=*) prefix=${arg#*=} ;;
+ --exec-prefix=*) exec_prefix=${arg#*=} ;;
+ --dynlibdir=*) dynlibdir=${arg#*=} ;;
+ --libexecdir=*) libexecdir=${arg#*=} ;;
+ --bindir=*) bindir=${arg#*=} ;;
+ --sbindir=*) sbindir=${arg#*=} ;;
+ --libdir=*) libdir=${arg#*=} ;;
+ --includedir=*) includedir=${arg#*=} ;;
+ --with-sysdeps=*) sysdeps=${arg#*=} manualsysdeps=true ;;
+ --with-include=*) var=${arg#*=} ; stripdir var ; addincpath="$addincpath -I$var" ;;
+ --with-lib=*) var=${arg#*=} ; stripdir var ; addlibspath="$addlibspath -L$var" ; vpaths="$vpaths $var" ;;
+ --with-dynlib=*) var=${arg#*=} ; stripdir var ; addlibdpath="$addlibdpath -L$var" ; vpathd="$vpathd $var" ;;
+ --enable-shared|--enable-shared=yes) shared=true ;;
+ --disable-shared|--enable-shared=no) shared=false ;;
+ --enable-static|--enable-static=yes) static=true ;;
+ --disable-static|--enable-static=no) static=false ;;
+ --enable-allstatic|--enable-allstatic=yes) allstatic=true ;;
+ --disable-allstatic|--enable-allstatic=no) allstatic=false ;;
+ --enable-static-libc|--enable-static-libc=yes) evenmorestatic=true ;;
+ --disable-static-libc|--enable-static-libc=no) evenmorestatic=false ;;
+ --enable-slashpackage=*) sproot=${arg#*=} ; slashpackage=true ; ;;
+ --enable-slashpackage) sproot= ; slashpackage=true ;;
+ --disable-slashpackage) sproot= ; slashpackage=false ;;
+ --enable-cross=*) cross=${arg#*=} ;;
+ --enable-cross) cross= ;;
+ --disable-cross) cross= ;;
+ --enable-*|--disable-*|--with-*|--without-*|--*dir=*|--build=*) ;;
+ --host=*|--target=*) target=${arg#*=} ;;
+ -* ) echo "$0: unknown option $arg" ;;
+ *=*) ;;
+ *) target=$arg ;;
+ esac
+done
+
+for i in prefix exec_prefix dynlibdir libexecdir bindir sbindir libdir includedir linkdynlibdir linkbindir linksbindir sysdeps sproot skalibs ; do
+ eval tmp=\${$i}
+ eval $i=$tmp
+ stripdir $i
+done
+
+# Get usable temp filenames
+i=0
+set -C
+while : ; do
+ i=$(($i+1))
+ tmpc="./tmp-configure-$$-$PPID-$i.c"
+ tmpe="./tmp-configure-$$-$PPID-$i.tmp"
+ 2>|/dev/null > "$tmpc" && break
+ 2>|/dev/null > "$tmpe" && break
+ test "$i" -gt 50 && fail "$0: cannot create temporary files"
+done
+set +C
+trap 'rm -f "$tmpc" "$tmpe"' EXIT ABRT INT QUIT TERM HUP
+
+# Set slashpackage values
+if $slashpackage ; then
+ home=${sproot}/package/${category}/${package}-${version}
+ exthome=${sproot}/package/${category}/${package}
+ if $manualsysdeps ; then
+ :
+ else
+ sysdeps=${sproot}/package/prog/skalibs/sysdeps
+ fi
+ binprefix=${home}/command
+ extbinprefix=${exthome}/command
+ dynlibdir=${home}/library.so
+ libexecdir=$binprefix
+ bindir=$binprefix
+ sbindir=$binprefix
+ libdir=${home}/library
+ includedir=${home}/include
+ while read dep ; do
+ addincpath="$addincpath -I${sproot}${dep}/include"
+ vpaths="$vpaths ${sproot}${dep}/library"
+ addlibspath="$addlibspath -L${sproot}${dep}/library"
+ if $allstatic ; then : ; else
+ vpathd="$vpathd ${sproot}${dep}/library.so"
+ addlibdpath="$addlibdpath -L${sproot}${dep}/library.so"
+ fi
+ done < package/deps-build
+fi
+
+# Find a C compiler to use
+echo "checking for C compiler..."
+trycc ${cross}gcc
+trycc ${cross}c99
+trycc ${cross}cc
+test -n "$CC_AUTO" || { echo "$0: cannot find a C compiler" ; exit 1 ; }
+echo " ... $CC_AUTO"
+echo "checking whether C compiler works... "
+echo "typedef int x;" > "$tmpc"
+if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO -c -o /dev/null "$tmpc" 2>"$tmpe" ; then
+ echo " ... yes"
+else
+ echo " ... no. Compiler output follows:"
+ cat < "$tmpe"
+ exit 1
+fi
+
+echo "checking target system type..."
+test -n "$target" || target=$($CC_AUTO -dumpmachine 2>/dev/null) || target=unknown
+echo " ... $target"
+if test ! -d $sysdeps || test ! -f $sysdeps/target ; then
+ echo "$0: error: $sysdeps is not a valid sysdeps directory"
+ exit 1
+fi
+if [ "x$target" != "x$(cat $sysdeps/target)" ] ; then
+ echo "$0: error: target $target does not match the contents of $sysdeps/target"
+ exit 1
+fi
+
+rt_lib=$(cat $sysdeps/rt.lib)
+socket_lib=$(cat $sysdeps/socket.lib)
+sysclock_lib=$(cat $sysdeps/sysclock.lib)
+tainnow_lib=$(cat $sysdeps/tainnow.lib)
+util_lib=$(cat $sysdeps/util.lib)
+
+tryflag CFLAGS_AUTO -std=c99
+tryflag CFLAGS_AUTO -fomit-frame-pointer
+tryflag CFLAGS_AUTO -fno-exceptions
+tryflag CFLAGS_AUTO -fno-unwind-tables
+tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables
+tryflag CFLAGS_AUTO -Wa,--noexecstack
+tryflag CFLAGS_AUTO -fno-stack-protector
+tryflag CPPFLAGS_AUTO -Werror=implicit-function-declaration
+tryflag CPPFLAGS_AUTO -Werror=implicit-int
+tryflag CPPFLAGS_AUTO -Werror=pointer-sign
+tryflag CPPFLAGS_AUTO -Werror=pointer-arith
+
+if $evenmorestatic ; then
+ LDFLAGS_NOSHARED=-static
+fi
+
+if $shared ; then
+ tryldflag LDFLAGS_AUTO -Wl,--hash-style=both
+fi
+
+if test -z "$vpaths" ; then
+ while read dep ; do
+ base=$(basename $dep) ;
+ vpaths="$vpaths /usr/lib/$base"
+ addlibspath="$addlibspath -L/usr/lib/$base"
+ done < package/deps-build
+fi
+
+CPPFLAGS_AUTO="$CPPFLAGS_AUTO $addincpath"
+LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibspath"
+$allstatic || LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibdpath"
+
+echo "creating config.mak..."
+cmdline=$(quote "$0")
+for i ; do cmdline="$cmdline $(quote "$i")" ; done
+exec 3>&1 1>config.mak
+cat << EOF
+# This file was generated by:
+# $cmdline
+# Any changes made here will be lost if configure is re-run.
+
+target := $target
+package := $package
+prefix := $prefix
+exec_prefix := $exec_prefix
+dynlibdir := $dynlibdir
+libexecdir := $libexecdir
+bindir := $bindir
+sbindir := $sbindir
+libdir := $libdir
+includedir := $includedir
+sysdeps := $sysdeps
+slashpackage := $slashpackage
+sproot := $sproot
+version := $version
+home := $home
+exthome := $exthome
+RT_LIB := ${rt_lib}
+SOCKET_LIB := ${socket_lib}
+SYSCLOCK_LIB := ${sysclock_lib}
+TAINNOW_LIB := ${tainnow_lib}
+UTIL_LIB := ${util_lib}
+
+CC := $CC_AUTO
+CFLAGS := $CFLAGS_AUTO
+CPPFLAGS := $CPPFLAGS_AUTO
+LDFLAGS := $LDFLAGS_AUTO
+LDFLAGS_NOSHARED := $LDFLAGS_NOSHARED
+CROSS_COMPILE := $cross
+
+vpath lib%a$vpaths
+EOF
+if $allstatic ; then
+ echo ".LIBPATTERNS := lib%.a"
+ vpathd=
+fi
+ echo "vpath lib%.so$vpathd"
+echo
+$static || echo "STATIC_LIBS :="
+$shared || echo "SHARED_LIBS :="
+exec 1>&3 3>&-
+echo " ... done."
+
+echo "creating src/include/${package}/config.h..."
+mkdir -p -m 0755 src/include/${package}
+exec 3>&1 1> src/include/${package}/config.h
+cat <<EOF
+/* ISC license. */
+
+/* Generated by: $cmdline */
+
+#ifndef ${package_macro_name}_CONFIG_H
+#define ${package_macro_name}_CONFIG_H
+
+#define ${package_macro_name}_VERSION "$version"
+EOF
+if $slashpackage ; then
+ echo "#define ${package_macro_name}_BINPREFIX \"$binprefix/\""
+ echo "#define ${package_macro_name}_EXTBINPREFIX \"$extbinprefix/\""
+ echo "#define ${package_macro_name}_LIBEXECPREFIX \"$binprefix/\""
+else
+ echo "#define ${package_macro_name}_BINPREFIX \"\""
+ echo "#define ${package_macro_name}_EXTBINPREFIX \"\""
+ echo "#define ${package_macro_name}_LIBEXECPREFIX \"$libexecdir/\""
+fi
+echo
+echo "#endif"
+exec 1>&3 3>&-
+echo " ... done."
diff --git a/doc/getaddrinfo.html b/doc/getaddrinfo.html
new file mode 100644
index 0000000..da0469c
--- /dev/null
+++ b/doc/getaddrinfo.html
@@ -0,0 +1,94 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the problem with getaddrinfo()</title>
+ <meta name="Description" content="s6-dns: the problem with getaddrinfo()" />
+ <meta name="Keywords" content="s6-dns client library getaddrinfo API interface" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The problem with getaddrinfo() </h1>
+
+<p>
+ The standard C library provides an API to perform name
+resolution:
+<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html">getaddrinfo()</a>,
+formerly <tt>gethostbyname()</tt>. However, for DNS resolution as well as
+implementation in the libc, this interface is very impractical, to the point of
+being unusable. Here are a few reasons why.
+</p>
+
+<h2> getaddrinfo() performs NSS resolution, not DNS resolution. </h2>
+
+<p>
+ I explained this point in a message to the
+<a href="http://busybox.net/">Busybox</a> mailing-list. You can
+<a href="http://lists.busybox.net/pipermail/busybox/2012-July/078123.html">read
+the post here</a>.
+(There is a mistake in that post about <tt>/etc/nsswitch.conf</tt> and
+<tt>/etc/host.conf</tt> syntax; the following two messages in the thread
+correct that mistake.)
+</p>
+
+<p>
+ TLDR: depending on the machine configuration, it is possible that
+<tt>getaddrinfo()</tt> will not use DNS at all.
+</p>
+
+<h2> It is unboundedly synchronous. </h2>
+
+<p>
+ DNS resolution performs network I/O, which can take a non-negligible
+amount of time. <tt>getaddrinfo()</tt> is a blocking call and there is
+no way to specify a timeout to make it return early, so it may block
+indefinitely. This is bad design.
+</p>
+
+<p>
+ Also, network operations being asynchronous by nature, even a
+synchronous API should provide a way to perform several queries at
+once and return when one of them, or all of them, get an answer.
+<tt>getaddrinfo()</tt> does not even offer that.
+</p>
+
+<h2> It focuses on the wrong details. </h2>
+
+<p>
+ Because it's generic, <tt>getaddrinfo()</tt> is cumbersome to use.
+ The data structures are impractical, requiring the user to fill in
+information that is irrelevant to DNS resolution. The details of the
+network transport protocols are of no interest to the user who just
+wants answers to his DNS queries!
+</p>
+
+<p>
+ But at the same time, <tt>getaddrinfo()</tt> does not allow the
+user to provide the details he wants or refine his search. It's a very
+basic and monolithic entry point, with no DNS-specific knobs. For
+instance, only A and AAAA queries are supported, which is clearly
+insufficient.
+</p>
+
+<h2> Conclusion </h2>
+
+<p>
+<tt>getaddrinfo()</tt> is a toy interface. For any half-serious DNS work,
+another API must be used.
+</p>
+
+<p>
+ Most people who need a real DNS client library use BIND's libresolv.
+<a href="libresolv.html">This page</a> explains what is wrong with it,
+and what s6-dns tries to do better.
+</p>
+
+</body>
+</html>
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..5263664
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,175 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns - a complete DNS client suite</title>
+ <meta name="Description" content="s6-dns - a complete DNS client suite" />
+ <meta name="Keywords" content="s6-dns s6dns s6 DNS resolution unix linux laurent bercot ska skarnet" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> s6-dns </h1>
+
+<h2> What is it&nbsp;? </h2>
+
+<p>
+ s6-dns is a suite of DNS client programs and libraries for Unix
+systems, as an alternative to the BIND, djbdns or other DNS clients.
+</p>
+
+<p>
+ s6-dns may include its own series of DNS caches and servers at some
+point in the future.
+</p>
+
+<hr />
+
+<ul>
+<li> <a href="getaddrinfo.html">Why a separate client library&nbsp;?</a> Why not just use <tt>getaddrinfo()</tt>&nbsp;?</li>
+<li> <a href="libresolv.html">What is wrong with libresolv, BIND's client library&nbsp;?</a></li>
+</ul>
+
+<hr />
+
+<h2> Installation </h2>
+
+<h3> Requirements </h3>
+
+<ul>
+ <li> A POSIX-compliant system with a standard C development environment </li>
+ <li> GNU make, version 3.81 or later </li>
+ <li> <a href="http://skarnet.org/software/skalibs/">skalibs</a> version
+2.0.0.0 or later </li>
+</ul>
+
+<h3> Licensing </h3>
+
+<p>
+ s6-dns is free software. It is available under the
+<a href="http://opensource.org/licenses/ISC">ISC license</a>.
+</p>
+
+<h3> Download </h3>
+
+<ul>
+ <li> The current released version of s6-dns is <a href="s6-dns-2.0.0.0.tar.gz">2.0.0.0</a>. </li>
+ <li> Alternatively, you can checkout a copy of the s6-dns git repository:
+<pre> git clone git://git.skarnet.org/s6-dns </pre> </li>
+</ul>
+
+<h3> Compilation </h3>
+
+<ul>
+ <li> See the enclosed INSTALL file for installation details. </li>
+</ul>
+
+<h3> Upgrade notes </h3>
+
+<ul>
+ <li> <a href="upgrade.html">This page</a> lists the differences to be aware of between
+the previous versions of s6-dns and the current one. </li>
+</ul>
+
+<h2> Reference </h2>
+
+<h3> Commands </h3>
+
+<p>
+ All these commands exit 111 if they encounter a temporary error or
+hardware error, and
+100 if they encounter a permanent error - such as a misuse. Short-lived
+commands exit 0 on success. Other exit codes are documented in the
+relevant page.
+</p>
+
+<h4> Command-line DNS clients programs </h4>
+
+<ul>
+<li><a href="s6-dnsip4.html">The <tt>s6-dnsip4</tt> program</a></li>
+<li><a href="s6-dnsip6.html">The <tt>s6-dnsip6</tt> program</a></li>
+<li><a href="s6-dnsname.html">The <tt>s6-dnsname</tt> program</a></li>
+<li><a href="s6-dnsmx.html">The <tt>s6-dnsmx</tt> program</a></li>
+<li><a href="s6-dnsns.html">The <tt>s6-dnsns</tt> program</a></li>
+<li><a href="s6-dnssoa.html">The <tt>s6-dnssoa</tt> program</a></li>
+<li><a href="s6-dnssrv.html">The <tt>s6-dnssrv</tt> program</a></li>
+<li><a href="s6-dnstxt.html">The <tt>s6-dnstxt</tt> program</a></li>
+</ul>
+
+<h4> Filtering tools </h4>
+
+<ul>
+<li><a href="s6-dnsip4-filter.html">The <tt>s6-dnsip4-filter</tt> program</a></li>
+<li><a href="s6-dnsip6-filter.html">The <tt>s6-dnsip6-filter</tt> program</a></li>
+<li><a href="s6-dnsname-filter.html">The <tt>s6-dnsname-filter</tt> program</a></li>
+</ul>
+
+<h4> Command-line qualification </h4>
+
+<ul>
+<li><a href="s6-dnsqualify.html">The <tt>s6-dnsqualify</tt> program</a></li>
+</ul>
+
+<h4> DNS analysis and debug tools </h4>
+
+<ul>
+<li><a href="s6-dnsq.html">The <tt>s6-dnsq</tt> program</a></li>
+<li><a href="s6-dnsqr.html">The <tt>s6-dnsqr</tt> program</a></li>
+</ul>
+
+<h4> Miscellaneous utilities </h4>
+
+<ul>
+<li><a href="s6-randomip.html">The <tt>s6-randomip</tt> program</a></li>
+</ul>
+
+
+<h3> Libraries </h3>
+
+<h4> Protocol implementation and synchronous resolution </h4>
+
+<ul>
+<li><a href="libs6dns/">The <tt>s6dns</tt> library interface</a></li>
+</ul>
+
+<h4> Asynchronous resolution </h4>
+
+<ul>
+<li><a href="skadns/">The <tt>skadns</tt> library interface</a></li>
+</ul>
+
+<hr />
+
+<a name="related">
+<h2> Related resources </h2>
+</a>
+
+<h3> s6-dns discussion </h3>
+
+<ul>
+ <li> <tt>s6-dns</tt> is discussed on the
+<a href="http://skarnet.org/lists.html#skaware">skaware</a> mailing-list. </li>
+ <li> It can also be discussed on the
+<a href="http://cr.yp.to/lists.html#dns">cr.yp.to dns mailing-list</a>. </li>
+</ul>
+
+<h3> Similar work </h3>
+
+<ul>
+ <li> <a href="http://www.isc.org/software/bind">BIND</a> is the most widely
+used DNS software suite on the Internet. It's also arguably the most
+<a href="http://cr.yp.to/djbdns/blurb/unbind.html">buggy</a>,
+convoluted and impossible to understand. </li>
+ <li> <a href="http://cr.yp.to/djbdns.html">djbdns</a> is DJB's DNS suite.
+It works very well, but is unfortunately unmaintained by the author. s6-dns
+follows <a href="http://skarnet.org/software/skalibs/djblegacy.html">the
+same design principles</a>. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/libresolv.html b/doc/libresolv.html
new file mode 100644
index 0000000..7a9fd31
--- /dev/null
+++ b/doc/libresolv.html
@@ -0,0 +1,140 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the problem with libresolv</title>
+ <meta name="Description" content="s6-dns: the problem with libresolv" />
+ <meta name="Keywords" content="s6-dns client library libresolv BIND API interface" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The problem with libresolv </h1>
+
+<p>
+ The BIND name server software comes with its own client library,
+named <em>libresolv</em>.
+</p>
+
+<p>
+ As can be expected from an ISC product, libresolv is not good software.
+Here are a few reasons why.
+</p>
+
+<h2> libresolv's security model is flawed. </h2>
+
+<p>
+ The same people who wrote BIND wrote libresolv. That is the amount of
+trust you can place in libresolv. Ten years ago, the security status
+of libresolv looked like
+<a href="http://cr.yp.to/djbdns/res-disaster.html">this</a>. I am not
+confident that is has improved: bugs in the software may have been
+fixed, but new ones will appear, and most importantly, the security
+management policy at ISC is still the same: security holes will be
+denied instead of acknowledged and worked upon.
+</p>
+
+<p>
+ If you find a bug, and <em>a fortiori</em> a security hole, in
+s6-dns, you can be sure it will be fixed promptly with apologies
+from the author. skarnet.org doesn't do obfuscation, and never lets
+politics get in the way of technical quality.
+</p>
+
+<h2> libresolv is unboundedly synchronous. </h2>
+
+<p>
+ You'd expect a real DNS client library to do better in this aspect
+than <a href="getaddrinfo.html">getaddrinfo()</a>, but no: libresolv's
+function calls are still purely synchronous and may uncontrollably
+block if the network is unresponsive.
+</p>
+
+<p>
+ Additionally, libresolv <em>only</em> provides a synchronous
+interface to clients. Despite the fundamentally asynchronous nature
+of DNS, and the need to implement asynchronous primitives
+internally, only blocking calls are made available in the API.
+This forces users to stack yet another piece of software on top
+of their dependencies if they need asynchronous DNS resolution.
+</p>
+
+<p>
+ libs6dns provides several layers of asynchronous interfaces.
+The user has access to
+<a href="libs6dns/s6dns_engine.html">low-level packet sending
+and receiving</a>, to
+<a href="libs6dns/s6dns_resolve.html#parallel">synchronous
+resolution of several queries at once</a>, and to a
+<a href="skadns/">real high-level asynchronous DNS library</a>.
+</p>
+
+<h2> It is too big for what it does. </h2>
+
+<p>
+ The <tt>libresolv-2.13.so</tt> binary file compiled for an i386
+Debian Linux system is roughly 71k bytes. The <tt>libs6dns.so.2.0</tt>
+file, for the same system, is roughly 41k bytes, while offering
+more functionality. libresolv does not do any high-level answer
+parsing, so the user still has to do some work after the libresolv
+calls. s6-dns tries to be small and still provide the user comfortable
+interfaces.
+</p>
+
+<h2> The API is cumbersome to use. </h2>
+
+<p>
+ Some examples of less-than-ideal interfaces for the users:
+</p>
+
+<ul>
+ <li> There is a flag in libresolv's global state structure that
+determines if queries are to be sent recursive or iterative. Which
+means that a program that needs to make both recursive and iterative
+queries must duplicate this state structure and use one for recursive
+queries, one for iterative queries. Why this complexity&nbsp;? The
+recursive flag should be given for every query, not be a part
+of the resolver state. </li>
+ <li> When a libresolv answer arrives but doesn't fit into the
+user-supplied buffer, the query is discarded and has to be retried.
+Does "network efficiency" ring a bell&nbsp;? Also, the length of
+the original answer is returned by the first call, which is a good
+thing: the user can now provide a large enough buffer so the call
+succeeds the next time, right&nbsp;? Wrong. The next answer can
+be different from the first, and in particular, longer, which
+means that the next query can <em>still fail</em>. </li>
+</ul>
+
+<p>
+ There is a reason why system calls and local functions take
+user-supplied buffers as arguments. They are relatively fast, it is
+not too costly to call them again if the buffer is too small the
+first time, and the result is consistent, i.e. after the first call,
+the right buffer length is <em>known</em>. But functions making
+network exchanges with variable-length results from one call to
+another&nbsp;? Those <em>need</em> heap-allocated storage. It is
+good design to avoid using the heap whenever possible, but it is
+not good design to waste network round-trips to save a <tt>malloc()</tt>.
+</p>
+
+<h2> Conclusion </h2>
+
+<p>
+ Like many other "standards", and C library interfaces in particular,
+<tt>libresolv</tt> is at best a mediocre one, that people use because
+there has been nothing better so far.
+</p>
+
+<p>
+ s6-dns tries to be an alternative solution - not as ambitious,
+but based on solid design principles and a reliable code base.
+</p>
+
+</body>
+</html>
diff --git a/doc/libs6dns/index.html b/doc/libs6dns/index.html
new file mode 100644
index 0000000..db7ee35
--- /dev/null
+++ b/doc/libs6dns/index.html
@@ -0,0 +1,104 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6dns library interface</title>
+ <meta name="Description" content="s6-dns: the s6dns library interface" />
+ <meta name="Keywords" content="s6-dns dns s6dns library libs6dns" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="../">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6dns</tt> library interface </h1>
+
+<h2> General information </h2>
+
+<p>
+ <tt>libs6dns</tt> is a DNS client library, designed for clarity
+and simplicity - which translates into smallness of the code.
+</p>
+
+<p>
+ A major focus of <tt>libs6dns</tt> is to avoid unnecessary use
+of heap memory. Memory is only allocated in the heap to store
+queries and response packets during a DNS resolution process, and
+to store the final answers into a user-provided
+<a href="http://skarnet.org/software/skalibs/libstddjb/stralloc.html">
+stralloc</a>; all the other operations use stack memory, and perform
+as few copies as possible.
+</p>
+
+<h2> Compiling </h2>
+
+<ul>
+ <li> Make sure the s6-dns headers, as well as the skalibs headers,
+are visible in your header search path. </li>
+ <li> Use <tt>#include &lt;s6-dns/s6dns.h&gt;</tt> </li>
+</ul>
+
+<h2> Linking </h2>
+
+<ul>
+ <li> Make sure the s6-dns libraries, as well as the skalibs libraries,
+are visible in your library search path. </li>
+ <li> Link against <tt>-ls6dns</tt>, <tt>-lskarnet</tt>, </li>
+<tt>`cat $SYSDEPS/socket.lib`</tt> and
+<tt>`cat $SYSDEPS/tainnow.lib`</tt>, $SYSDEPS being your skalibs
+sysdeps directory. </li>
+</ul>
+
+<h2> Programming </h2>
+
+<p>
+ The <tt>s6dns.h</tt> header is actually a concatenation of other headers:
+the libs6dns is separated into several modules, each of them with its
+own header.
+</p>
+
+<ul>
+ <li> The <tt>s6dns-constants.h</tt> header provides constants used in
+other parts of the library. </li>
+ <li> The <a href="s6dns-ip46.html">s6dns-ip46.h</a> header provides an
+abstraction for IPv4 and IPv6 transports. </li>
+ <li> The <a href="s6dns-domain.html">s6dns-domain.h</a> header provides
+basic string manipulation primitives for domain names. </li>
+ <li> The <a href="s6dns-message.html">s6dns-message.h</a> header provides
+function to parse a message following the DNS protocol. </li>
+ <li> The <a href="s6dns-engine.html">s6dns-engine.h</a> header provides
+the low-level asynchronous networking functions. </li>
+ <li> The <a href="s6dns-rci.html">s6dns-rci.h</a> header provides an
+interface to <tt>resolv.conf</tt> reading. </li>
+ <li> The <a href="s6dns-resolve.html">s6dns-resolve.h</a> header provides
+the user-level synchronous resolution functions. </li>
+ <li> The <a href="s6dns-fmt.html">s6dns-fmt.h</a> header provides
+formatting primitives to display RR contents. </li>
+</ul>
+
+<p>
+ (User-level asynchronous resolution functions are provided in the
+<a href="../skadns/">skadns</a> library.)
+</p>
+
+<p>
+ Two functional macros are actually directly declared in the <tt>s6dns.h</tt>
+header:
+</p>
+
+<ul>
+ <li> Call <tt>s6dns_init()</tt> before all your s6dns operations.
+s6dns_init() calls <a href="s6dns-rci.html">s6dns_rci_init()</a>,
+extracting <tt>resolv.conf</tt> information to an internal global
+variable. The function returns 1 on success, and 0 (and sets errno)
+on failure. </li>
+ <li> Call <tt>s6dns_finish()</tt> when you're done with the libs6dns.
+It frees the resources used. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/libs6dns/s6dns-domain.html b/doc/libs6dns/s6dns-domain.html
new file mode 100644
index 0000000..b1492c3
--- /dev/null
+++ b/doc/libs6dns/s6dns-domain.html
@@ -0,0 +1,137 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6dns_domain library interface</title>
+ <meta name="Description" content="s6-dns: the s6dns_domain library interface" />
+ <meta name="Keywords" content="s6-dns dns s6dns_domain library libs6dns" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">libs6dns</a><br />
+<a href="../">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6dns_domain</tt> library interface </h1>
+
+<p>
+ The following functions and structures are declared in the <tt>s6-dns/s6dns-domain.h</tt> header,
+and implemented in the <tt>libs6dns.a</tt> or <tt>libs6dns.so</tt> library.
+</p>
+
+<h2> General information </h2>
+
+<p>
+ <tt>s6dns_domain</tt> provides primitives to perform basic operations
+on domain names.
+</p>
+
+<h2> Data structures </h2>
+
+<p>
+ A <tt>s6dns_domain_t</tt> is a structure that s6dns uses internally to
+represent a domain name. It can be in <em>string form</em> (or
+<em>decoded form</em>), which is close to the printable form seen
+by users, or in <em>packet form</em> (or <em>encoded form</em>), which
+is the format used in the DNS protocol.
+</p>
+
+<p>
+ A <tt>s6dns_domain_t</tt> is a flat structure and can be declared
+on the stack.
+</p>
+
+<h2> Functions </h2>
+
+<p>
+<code> int s6dns_domain_fromstring (s6dns_domain_t *d, char const *s, unsigned int len) </code> <br />
+Makes a (string form) domain from string <em>s</em> of length <em>len</em>,
+and stores it into *<em>d</em>. Returns 1 if it succeeds, otherwise it
+returns 0 and sets errno appropriately - most likely ENAMETOOLONG, i.e.
+the name in <em>s</em> is not a well-formed domain name.
+</p>
+
+<p>
+<code> unsigned int s6dns_domain_tostring (char *s, unsigned int max, s6dns_domain_t const *d) </code> <br />
+Writes into string <em>s</em> the domain contained in *<em>d</em> (in string
+form). Returns the number of bytes written, or 0 in case of failure. If
+the output would be more than <em>max</em> bytes, 0 ENAMETOOLONG is returned.
+</p>
+
+<p>
+<code> int s6dns_domain_noqualify (s6dns_domain_t *d) </code> <br />
+Makes sure that *<em>d</em> is fully qualified. This is done without using
+qualification: a trailing dot is simply appended if the domain
+doesn't already have one. Returns 1 if it succeeds, or 0 if it fails.
+</p>
+
+<p>
+<code> unsigned int s6dns_domain_qualify (s6dns_domain_t *list, s6dns_domain_t const *d, char const *rules, unsigned int rulesnum) </code> <br />
+Performs qualification on domain *<em>d</em> according to the first
+<em>rulesnum</em> rules stored in string <em>rules</em>. Stores the output
+(at most <em>rulesnum</em> domains) into the array pointed to by <em>list</em>.
+Returns 0 on failure, or the number of written domains if it succeeds. This
+number can be lesser than <em>rulesnum</em>, for instance if *<em>d</em> is
+already fully qualified. <br />
+Valid <em>rules</em> and <em>rulesnum</em> can be obtained via functions
+declared in <a href="s6dns-rci.html">s6dns-rci.h</a>.
+</p>
+
+<p>
+<code> int s6dns_domain_encode (s6dns_domain_t *d) </code> <br />
+Encodes domain *<em>d</em> from string form to packet form. Returns 1
+if it succeeds or 0 if it fails - for instance, *<em>d</em> is not a
+valid string form (EINVAL).
+</p>
+
+<p>
+<code> unsigned int s6dns_domain_encodelist (s6dns_domain_t *list, unsigned int n) </code> <br />
+Encodes <em>n</em> domains pointed to by <em>list</em> from string form to packet form,
+stopping at the first failure.
+Returns the number of successfully encoded domains, normally <em>n</em>.
+</p>
+
+<p>
+<code> int s6dns_domain_decode (s6dns_domain_t *d) </code> <br />
+Decodes domain *<em>d</em> from packet form to string form. Returns 1
+if it succeeds or 0 if it fails - for instance, *<em>d</em> is not a
+valid packet form (EPROTO).
+</p>
+
+<p>
+<code> int s6dns_domain_fromstring_noqualify_encode (s6dns_domain_t *d, char const *s, unsigned int len) </code> <br />
+Higher-level function wrapping some of the above. Makes an encoded, fully qualified
+(without resorting to qualification) domain from string <em>s</em> of
+length <em>len</em>. Returns 1 if it succeeds and 0 if it fails.
+</p>
+
+<p>
+<code> unsigned int s6dns_domain_fromstring_qualify_encode (s6dns_domain_t *list, char const *s, unsigned int len, char const *rules, unsigned int rulesnum) </code> <br />
+Another wrapping function. It makes a list of encoded, fully qualified domains,
+from string <em>s</em> of length <em>len</em> using <em>rulesnum</em> qualification
+rules in <em>rules</em>. It writes at most <em>rulesnum</em> domains into the array
+pointed to by <em>list</em> and returns the number of written domains, or 0 on an
+error.
+</p>
+
+<p>
+<code> void s6dns_domain_arpafromip4 (s6dns_domain_t *d, char const *ip) </code> <br />
+Writes into <em>d</em> the <tt>in-addr.arpa.</tt> domain corresponding to IPv4 address
+<em>ip</em> (4 bytes, in network byte order), in string form.
+</p>
+
+<p>
+<code> void s6dns_domain_arpafromip6 (s6dns_domain_t *d, char const *ip, unsigned int mask) </code> <br />
+Writes into <em>d</em> the <tt>ip6.arpa.</tt> domain corresponding to the first
+<em>mask</em> bits of IPv6 address <em>ip</em> (16 bytes, in network byte order),
+in string form.
+Only multiples of 4 count for <em>mask</em>, i.e. the output will be the same
+if mask is (for instance) 125, 126, 127 or 128.
+</p>
+
+</body>
+</html>
diff --git a/doc/libs6dns/s6dns-engine.html b/doc/libs6dns/s6dns-engine.html
new file mode 100644
index 0000000..f0f9573
--- /dev/null
+++ b/doc/libs6dns/s6dns-engine.html
@@ -0,0 +1,255 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6dns_engine library interface</title>
+ <meta name="Description" content="s6-dns: the s6dns_engine library interface" />
+ <meta name="Keywords" content="s6-dns dns s6dns_engine library libs6dns" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">libs6dns</a><br />
+<a href="../">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6dns_engine</tt> library interface </h1>
+
+<p>
+ The following functions are declared in the <tt>s6-dns/s6dns-engine.h</tt> header,
+and implemented in the <tt>libs6dns.a</tt> or <tt>libs6dns.so</tt> library.
+</p>
+
+<h2> General information </h2>
+
+<p>
+ <tt>s6dns_engine</tt> is the nitty-gritty of DNS query management. These
+are the low-level asynchronous primitives sending DNS queries over the
+network, and getting answers.
+</p>
+
+<p>
+ <tt>s6dns_engine</tt> has been inspired by Dan J. Bernstein's
+<a href="http://cr.yp.to/djbdns/dns_transmit.html">dns_transmit</a>
+library, but does not borrow any code from it. Unlike
+<tt>dns_transmit</tt>, <tt>s6dns_engine</tt> does not assume that
+network send operations are instant successes; <tt>s6dns_engine</tt>
+descriptors can be selected for writing as well as for reading.
+Also, if the underlying <a href="http://skarnet.org/software/skalibs">
+skalibs</a> has been compiled with IPv6 support, <tt>s6dns_engine</tt>
+supports native IPv6 transport.
+</p>
+
+<p>
+ The <tt>s6dns_engine</tt> functions are used in the implementation of the
+<tt>s6dns_resolven_loop</tt> function - which is nothing more than a
+simple event loop around the <tt>s6dns_engine</tt> primitives - and the
+<a href="../skadns/skadnsd.html">skadnsd</a> daemon. Both pieces of code are
+good examples of how to use <tt>s6dns_engine</tt>.
+</p>
+
+<p>
+ However, unless you're implementing a DNS cache, you probably should
+not call the
+<tt>s6dns_engine</tt> functions directly: they are very low-level. If you
+need synchronous resolution, use the
+<a href="s6dns-resolve.html">s6dns_resolve</a> functions. If you need
+asynchronous DNS operations, use the
+<a href="../skadns/index.html">skadns</a> functions, which are
+designed to provide a higher level interface to multiple asynchronous
+DNS queries.
+</p>
+
+<h2> Data structures </h2>
+
+<p>
+ A <tt>s6dns_engine_t</tt> structure holds all the data necessary to
+manage a DNS query (and response). It must be initialized to S6DNS_ENGINE_ZERO
+when first declared, and recycled with <tt>s6dns_engine_recycle()</tt>
+after each use. It contains a stralloc, so it must be freed with
+<tt>s6dns_engine_free()</tt> before being discarded, to avoid memory leaks.
+</p>
+
+<h2> Functions </h2>
+
+<h3> <tt>s6dns_engine_t</tt> life cycle </h3>
+
+<p>
+<code> int s6dns_engine_init (s6dns_engine_t *dt, s6dns_ip46list_t const *servers, uint32 options, char const *q, unsigned int qlen, uint16 qtype, struct taia const *deadline, struct taia const *stamp) </code>
+</p>
+
+<p>
+Initializes <em>dt</em> with query <em>q</em> of length <em>qlen</em>
+and type <em>qtype</em>. If <tt>d</tt> is an
+encoded <tt>s6dns_domain_t</tt>, then <tt>d.s</tt> and <tt>d.len</tt>
+are appropriate candidates for arguments <em>q</em> and <em>qlen</em>
+respectively.
+</p>
+
+<p>
+<em>options</em> can be 0 or an OR'ed
+combination of the following, defined in <tt>s6-dns/s6dns-constants.h</tt>:
+</p>
+
+<ul>
+ <li> S6DNS_O_RECURSIVE: the query will be recursive and assuming it is
+sent to a DNS cache, instead of iterative and assuming it is sent to a
+DNS server. </li>
+ <li> S6DNS_O_STRICT: the library will only accept authoritative answers
+to iterative queries. This is normally the sane behaviour, but badly
+configured DNS software around the world - notably, <a href="../bind.html">
+BIND</a> when it's configured to be both a cache and a server - often
+serve <em>non-</em>authoritative data even when they could, so it
+breaks things, hence why the option isn't set by default. </li>
+</ul>
+
+<p>
+<em>servers</em> must point to a list of IP addresses as defined in
+<a href="s6dns-ip46.html">s6-dns/s6dns-ip46.h</a>. Such a list can be
+obtained from the <tt>/etc/resolv.conf</tt> file via the
+<a href="s6dns-rci.html">s6dns_rci_fill()</a> call when performing a
+recursive query, or it must be constructed from a list of relevant
+NS addresses when performing an iterative query.
+</p>
+
+<p>
+<em>stamp</em> must be an accurate enough timestamp. <em>deadline</em>
+sets up a deadline for the query: if the query hasn't been
+satisfactorily answered by <em>deadline</em> - no matter how many
+round-trips to network servers the library performs internally - then
+it will be considered failed, and a timeout will be reported.
+</p>
+
+<p>
+The function returns 1 if all went well, and 0 if an error occurred.
+It returns instantly; it <em>does not</em> perform any network operation,
+it just prepares <em>dt</em> to send a query. The actual data sending
+will take place on the next <tt>s6dns_engine_event()</tt> call.
+</p>
+
+<p>
+<code> void s6dns_engine_recycle (s6dns_engine_t *dt) </code>
+</p>
+
+<p>
+Recycles <tt>dt</tt>, making it ready for another use. This function
+does not deallocate the heap memory used by dt, so it's faster than
+<tt>s6dns_engine_free()</tt> and does not cause heap fragmentation.
+</p>
+
+<p>
+<code> void s6dns_engine_free (s6dns_engine_t *dt) </code>
+</p>
+
+<p>
+Frees the heap memory used by <tt>dt</tt>. Also makes <tt>dt</tt>
+ready for another use. It's advised to only use this function when
+certain that <em>dt</em> will not be reused.
+</p>
+
+<h3> Before the <tt>iopause()</tt> </h3>
+
+<p>
+ The descriptor to select on is available as the <tt>fd</tt> field in
+the <tt>s6dns_engine_t</tt> structure.
+<em>dt</em>&rarr;fd should be read every iteration, because it can
+change between iterations even if no event or timeout is reported
+for <em>dt</em>.
+</p>
+
+<p>
+<code> void s6dns_engine_nextdeadline (s6dns_engine_t const *dt, struct taia *a) </code>
+</p>
+
+<p>
+If <em>dt</em> needs handling before the absolute date *<em>a</em>,
+then *<em>a</em> is updated
+so it contains the earlier date. This is useful to compute the next
+deadline in an <tt>iopause()</tt> loop.
+</p>
+
+<p>
+<code> int s6dns_engine_isreadable (s6dns_engine_t const *dt) </code>
+</p>
+
+<p>
+Returns nonzero iff <em>dt</em>&rarr;fd is to be selected for reading.
+Should be called in every iteration.
+</p>
+
+<p>
+<code> int s6dns_engine_iswritable (s6dns_engine_t const *dt) </code>
+</p>
+
+<p>
+Returns nonzero iff <em>dt</em>&rarr;fd is to be selected for writing.
+Should be called in every iteration.
+</p>
+
+<h3> After the <tt>iopause()</tt> </h3>
+
+<p>
+<code> int s6dns_engine_timeout (s6dns_engine_t *dt, struct taia const *stamp) </code>
+</p>
+
+<p>
+This function should be called if your selecting function returned 0, which
+means that an event timed out.
+<em>stamp</em> should contain the current time. The function returns -1 if
+an error occurred, 1 if <em>dt</em> actually timed out, and 0 if nothing
+special happened to <em>dt</em> (and your iopause timeout was caused by
+something else). If the return value is not 0, <em>dt</em> is automatically
+recycled.
+</p>
+
+<p>
+<code> int s6dns_engine_event (s6dns_engine_t *dt, struct taia const *stamp) </code>
+</p>
+
+<p>
+This function should be called if your selecting function returned a positive
+number, which means that some event got triggered.
+<em>stamp</em> should contain the current time. The function returns
+-1 if an error occurred (and <em>dt</em> is automatically recycled). It
+returns 0 if no answer has arrived yet, and 1 if an answer is available.
+</p>
+
+<p>
+The <tt>s6dns_engine_timeout()</tt> and <tt>s6dns_engine_event()</tt> functions,
+when returning -1, make use of the following error codes:
+</p>
+<ul>
+ <li> EINVAL: Invalid <em>dt</em>.
+ <li> ENETUNREACH: All the servers in the <em>servers</em> list have been
+unsuccessfully tried. </li>
+ <li> EPROTO: An answer arrived, but it didn't follow the DNS protocol. </li>
+ <li> Other error codes reporting socket or system failures. </li>
+</ul>
+
+<p>
+<code> char *s6dns_engine_packet (s6dns_engine_t const *dt) </code>
+</p>
+
+<p>
+Points to the response packet received from the network,
+if <tt>s6dns_engine_event()</tt> returned 1.
+</p>
+
+<p>
+<code> unsigned int s6dns_engine_packetlen (s6dns_engine_t const *dt) </code>
+</p>
+
+<p>
+Gives the length of the response packet,
+if <tt>s6dns_engine_event()</tt> returned 1.
+</p>
+
+<p>
+ You should recycle or free <em>dt</em> after reading the response packet.
+</p>
+
+</body>
+</html>
diff --git a/doc/libs6dns/s6dns-fmt.html b/doc/libs6dns/s6dns-fmt.html
new file mode 100644
index 0000000..a586846
--- /dev/null
+++ b/doc/libs6dns/s6dns-fmt.html
@@ -0,0 +1,92 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6dns_fmt library interface</title>
+ <meta name="Description" content="s6-dns: the s6dns_fmt library interface" />
+ <meta name="Keywords" content="s6-dns dns s6dns_fmt library libs6dns" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">libs6dns</a><br />
+<a href="../">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6dns_fmt</tt> library interface </h1>
+
+<p>
+ The following functions and structures are declared in the <tt>s6-dns/s6dns-fmt.h</tt> header,
+and implemented in the <tt>libs6dns.a</tt> or <tt>libs6dns.so</tt> library.
+</p>
+
+<h2> General information </h2>
+
+<p>
+ <tt>s6dns_fmt</tt> provides primitives to format RR contents into
+printable strings.
+</p>
+
+<h2> Functions </h2>
+
+<p>
+<code> unsigned int s6dns_fmt_domain (char *s, unsigned int max, s6dns_domain_t const *d) </code> <br />
+Writes into string <em>s</em> the domain contained in *<em>d</em> (in string
+form). Returns the number of bytes written, or 0 in case of failure. If
+the output would be more than <em>max</em> bytes, 0 ENAMETOOLONG is returned.
+To avoid that, S6DNS_FMT_DOMAIN is a suitable number of bytes to preallocate <em>s</em>.
+This function is actually an alias to <tt>s6dns_domain_tostring</tt>.
+</p>
+
+<p>
+<code> unsigned int s6dns_fmt_domainlist (char *s, unsigned int max, s6dns_domain_t const *list, unsigned int n, char const *delin, unsigned int delimlen) </code> <br />
+Writes into string <em>s</em> the list of <em>n</em> domains (in string form)
+pointed to by <em>list</em>. Between each domain (and not after the last one),
+string <em>delim</em> of length <em>delimlen</em> is appended.
+The function returns the number of bytes written, or 0 in case of failure. If
+the output would be more than <em>max</em> bytes, 0 ENAMETOOLONG is returned.
+To avoid that, S6DNS_FMT_DOMAINLIST(n) is a suitable number of bytes to preallocate <em>s</em>.
+</p>
+
+<p>
+<code> unsigned int s6dns_fmt_hinfo (char *s, unsigned int max, s6dns_message_rr_hinfo_t const *p) </code> <br />
+Writes into string <em>s</em> the HINFO contained in *<em>p</em>: cpu, then os,
+separated by a space.
+Returns the number of bytes written, or 0 in case of failure. If
+the output would be more than <em>max</em> bytes, 0 ENAMETOOLONG is returned.
+To avoid that, S6DNS_FMT_HINFO is a suitable number of bytes to preallocate <em>s</em>.
+</p>
+
+<p>
+<code> unsigned int s6dns_fmt_mx (char *s, unsigned int max, s6dns_message_rr_mx_t const *p) </code> <br />
+Writes into string <em>s</em> the MX contained in *<em>p</em>: preference, then
+exchanger name, separated by a space.
+Returns the number of bytes written, or 0 in case of failure. If
+the output would be more than <em>max</em> bytes, 0 ENAMETOOLONG is returned.
+To avoid that, S6DNS_FMT_MX is a suitable number of bytes to preallocate <em>s</em>.
+</p>
+
+<p>
+<code> unsigned int s6dns_fmt_soa (char *s, unsigned int max, s6dns_message_rr_soa_t const *p) </code> <br />
+Writes into string <em>s</em> the SOA contained in *<em>p</em>:
+mname, then rname, then serial number, refresh time, retry time, expiration time
+and minimum time, separated by spaces.
+Returns the number of bytes written, or 0 in case of failure. If
+the output would be more than <em>max</em> bytes, 0 ENAMETOOLONG is returned.
+To avoid that, S6DNS_FMT_SOA is a suitable number of bytes to preallocate <em>s</em>.
+</p>
+
+<p>
+<code> unsigned int s6dns_fmt_srv (char *s, unsigned int max, s6dns_message_rr_srv_t const *p) </code> <br />
+Writes into string <em>s</em> the SRV contained in *<em>p</em>: priority,
+then weight, then port, then target, separated by spaces.
+Returns the number of bytes written, or 0 in case of failure. If
+the output would be more than <em>max</em> bytes, 0 ENAMETOOLONG is returned.
+To avoid that, S6DNS_FMT_SRV is a suitable number of bytes to preallocate <em>s</em>.
+</p>
+
+</body>
+</html>
diff --git a/doc/libs6dns/s6dns-ip46.html b/doc/libs6dns/s6dns-ip46.html
new file mode 100644
index 0000000..27cbe2c
--- /dev/null
+++ b/doc/libs6dns/s6dns-ip46.html
@@ -0,0 +1,69 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6dns_ip46 library interface</title>
+ <meta name="Description" content="s6-dns: the s6dns_ip46 library interface" />
+ <meta name="Keywords" content="s6-dns dns s6dns_ip46 library libs6dns" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">libs6dns</a><br />
+<a href="../">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6dns_ip46</tt> library interface </h1>
+
+<p>
+ The following functions are declared in the <tt>s6-dns/s6dns-ip46.h</tt> header,
+and implemented as macros.
+</p>
+
+<h2> General information </h2>
+
+<p>
+ <tt>s6dns_ip46</tt> is the transport abstraction layer. It allows
+the functions declared in <a href="s6dns-engine.html">s6-dns/s6dns-engine.h</a>
+and <a href="s6dns-rci.html">s6-dns/s6dns-rci.h</a> to be transport-agnostic,
+i.e. to be able to work with both IPv4 and IPv6.
+</p>
+
+<p>
+ If the underlying <a href="http://skarnet.org/software/skalibs/">skalibs</a>
+has been compiled with
+ <a href="http://skarnet.org/software/skalibs/flags.html#noipv6">flag-noipv6</a>,
+or if it has detected at build time that the target host does not support
+IPv6, then the s6dns-ip46 abstraction will be totally transparent and use
+no resources at all.
+</p>
+
+<h2> Data structures </h2>
+
+<p>
+ A <tt>s6dns_ip46list_t</tt> structure holds a list of S6DNS_MAX_SERVERS (16)
+IPv4 <em>or</em> IPv6 addresses. Such a mixed list can be constructed, for
+instance, if the <tt>/etc/resolv.conf</tt> file contains both v4 and v6
+<tt>nameserver</tt> lines.
+</p>
+
+<h2> Functions </h2>
+
+<p>
+ If <em>list</em> is a <tt>s6dns_ip46list_t</tt> and <em>i</em> an integer
+between 0 and DNS_MAX_SERVERS-1, then
+</p>
+
+<ul>
+ <li> <code>s6dns_ip46list_is6(&amp;list, i)</code> is 1 if the <em>i</em>th
+address in <em>list</em> is IPv6 and 0 if it is IPv4. </li>
+ <li> <code>s6dns_ip46list_ip(&amp;list, i)</code> is a <tt>char *</tt> pointer to
+16 (if IPv6) or 4 (IPv4) bytes representing the <em>i</em>th address in
+<em>list</em>, in network byte order. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/libs6dns/s6dns-message.html b/doc/libs6dns/s6dns-message.html
new file mode 100644
index 0000000..e06aa91
--- /dev/null
+++ b/doc/libs6dns/s6dns-message.html
@@ -0,0 +1,261 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6dns_message library interface</title>
+ <meta name="Description" content="s6-dns: the s6dns_message library interface" />
+ <meta name="Keywords" content="s6-dns dns s6dns_message library libs6dns" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">libs6dns</a><br />
+<a href="../">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6dns_message</tt> library interface </h1>
+
+<p>
+ The following functions are declared in the <tt>s6-dns/s6dns-message.h</tt> header,
+and implemented in the <tt>libs6dns.a</tt> or <tt>libs6dns.so</tt> library.
+</p>
+
+<h2> General information </h2>
+
+<p>
+ <tt>s6dns_message</tt> provides functions to read and parse DNS messages
+sent by servers and caches and containing answers to queries.
+</p>
+
+<h2> Data structures </h2>
+
+<p>
+ A <tt>s6dns_message_header_t</tt> is a structure containing the header
+of a received DNS packet, broken down for easy access to the bits.
+</p>
+
+<p>
+ A <tt>s6dns_message_rr_t</tt> is a structure containing the information
+about a resource record given by an answer packet - all of it, except the
+value of the answer itself, which is rtype-specific and has to be decoded
+by rtype-specific functions.
+</p>
+
+<p>
+ A <tt>s6dns_message_rr_func_t</tt> is the type of such a function. The
+prototype is <br />
+<code> int f (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *data) </code> <br />
+</p>
+
+<ul>
+ <li> *<em>rr</em> contains the header information of the resource record,
+including the domain to which it applies, the rtype, and the ttl. </li>
+ <li> <em>packet</em> points to the <strong>whole DNS packet</strong> the RR
+is a part of. </li>
+ <li> <em>packetlen</em> is the total length of said packet. </li>
+ <li> <em>pos</em> is the offset of the RR in the DNS packet, i.e. <em>f</em>
+should start parsing at <em>packet + pos</em>. </li>
+ <li> <em>section</em> is the section the RR belongs to: 2 for answer, 3 for
+authority, 4 for additional. </li>
+ <li> <em>data</em> is a user-specified pointer; it is typically used to
+write the decoded output, in a rtype-specific way. </li>
+ <li> <em>f</em> must return 1 or more if it succeeds in decoding the packet.
+Else it must set errno appropriately, and return -1 if the error is local,
+unrelated to the packet - for instance, it has run out of memory - or 0 if it
+cannot decode the RR because of the packet contents. </li>
+</ul>
+
+<p>
+ Various structures designed to store specific resource record types are
+also provided. The list includes:
+</p>
+
+<ul>
+ <li> <tt>s6dns_message_rr_hinfo</tt> for HINFO RRs </li>
+ <li> <tt>s6dns_message_rr_mx</tt> for MX RRs </li>
+ <li> <tt>s6dns_message_rr_soa</tt> for SOA RRs </li>
+ <li> <tt>s6dns_message_rr_srv</tt> for SRV RRs </li>
+</ul>
+
+<h2> Functions </h2>
+
+<h3> Header management </h3>
+
+<p>
+<code> void s6dns_message_header_pack (char *s, s6dns_message_header_t const *h) </code> <br />
+Packs the header *<em>h</em> into the 12 bytes pointed to by <em>s</em>.
+</p>
+
+<p>
+<code> void s6dns_message_header_unpack (char const *s, s6dns_message_header_t *h) </code> <br />
+Unpacks the 12 bytes pointed to by <em>s</em> into the structure *<em>h</em>.
+</p>
+
+<h3> Low-level RR decoding </h3>
+
+<p>
+ The following primitives are used in the implementation of
+<tt>s6dns_message_rr_func_t</tt>-typed functions, to read and decode information
+stored in a DNS packet. Their arguments are:
+</p>
+
+<ol>
+ <li> A pointer to the structure where the information is going to be stored </li>
+ <li> A read-only pointer to the beginning of the DNS packet </li>
+ <li> The length of the DNS packet </li>
+ <li> A pointer to an integer containing the current position in the
+DNS packet, i.e. where the information is going to be read. If the functions
+succeed, they automatically update the position so information can be read
+sequentially. </li>
+</ol>
+
+<p>
+<code> int s6dns_message_get_string (s6dns_domain_t *d, char const *packet, unsigned int packetlen, unsigned int *pos) </code> <br />
+Reads a character-string and stores it into *<em>d</em>. Returns 1 on success
+and 0 on failure. Note that *<em>d</em> does not contain a domain, but the
+<tt>s6dns_domain_t</tt> structure is adapted to store strings that do not
+exceed 255 characters. <em>d</em>&rarr;s can be used to access the string,
+and <em>d</em>&rarr;len its length.
+</p>
+
+<p>
+<code> int s6dns_message_get_strings (char *s, unsigned int rdlength, char const *packet, unsigned int packetlen, unsigned int *pos </code> <br />
+This function takes an additional parameter <em>rdlength</em>. It reads a series of
+character-strings and stores their concatenation into the string <em>s</em>, which
+must be preallocated; it can never store more than <em>rdlength</em> bytes. It returns
+-1 if it fails; on success, it returns the number of bytes written. The
+<em>rdlength</em> parameter must be the length of the resource record containing
+the series of character-strings.
+</p>
+
+<p>
+<code> unsigned int s6dns_message_get_domain (s6dns_domain_t *d, char const *packet, unsigned int packetlen, unsigned int *pos) </code> <br />
+Reads a domain and stores it, in string form, into *<em>d</em>.
+Returns 1 on success and 0 on failure, and sets errno:
+</p>
+
+<ul>
+ <li> EPROTO: there is no proper domain to be read in position *<em>pos</em> of
+<em>packet</em>. Either the packet is malformed or the function is being
+misused. </li>
+ <li> EPROTONOSUPPORT: the domain encoding uses an extension that the function
+does not recognize. </li>
+</ul>
+
+<p>
+<code> int s6dns_message_get_hinfo (s6dns_message_rr_hinfo_t *p, char const *packet, unsigned int packetlen, unsigned int *pos) </code> <br />
+Reads a HINFO RR and stores it into *<em>p</em>. Returns 1 on success or 0
+on failure.
+</p>
+
+<p>
+<code> int s6dns_message_get_mx (s6dns_message_rr_mx_t *p, char const *packet, unsigned int packetlen, unsigned int *pos) </code> <br />
+Reads a MX RR and stores it into *<em>p</em>. Returns 1 on success or 0 on failure.
+</p>
+
+<p>
+<code> int s6dns_message_get_soa (s6dns_message_rr_soa_t *p, char const *packet, unsigned int packetlen, unsigned int *pos) </code> <br />
+Reads a SOA RR and stores it into *<em>p</em>. Returns 1 on success or 0 on failure.
+</p>
+
+<p>
+<code> int s6dns_message_get_srv (s6dns_message_rr_srv_t *p, char const *packet, unsigned int packetlen, unsigned int *pos) </code> <br />
+Reads a SRV RR and stores it into *<em>p</em>. Returns 1 on success or 0 on failure.
+</p>
+
+<h3> High-level RR-specific parsing functions </h3>
+
+<p>
+<code> s6dns_message_func_t s6dns_message_parse_answer_strings </code> <br />
+Parses character-strings located in the answer section of the packet. The
+<em>data</em> argument is interpreted as a pointer to a <tt>s6dns_mpag_t</tt>,
+which is a structure defined in the <tt>s6-dns/s6dns-message.h</tt> header
+and used to store multiple character-strings.
+</p>
+
+<p>
+<code> s6dns_message_func_t s6dns_message_parse_answer_domain </code> <br />
+Parses domains located in the answer section of the packet. The
+<em>data</em> argument is interpreted as a pointer to a <tt>s6dns_dpag_t</tt>,
+which is a structure defined in the <tt>s6-dns/s6dns-message.h</tt> header
+and used to store multiple domains.
+</p>
+
+<p>
+<code> s6dns_message_func_t s6dns_message_parse_answer_a </code> <br />
+Parses A RRs located in the answer section of the packet. The
+<em>data</em> argument is interpreted as a pointer to a
+<a href="http://skarnet.org/software/skalibs/libstddjb/stralloc.html">stralloc</a>,
+and 4 bytes are appended to this stralloc for every IPv4 address found.
+</p>
+
+
+<code> s6dns_message_func_t s6dns_message_parse_answer_aaaa </code> <br />
+Parses AAAA RRs located in the answer section of the packet. The
+<em>data</em> argument is interpreted as a pointer to a
+<a href="http://skarnet.org/software/skalibs/libstddjb/stralloc.html">stralloc</a>,
+and 16 bytes are appended to this stralloc for every IPv6 address found.
+</p>
+
+<p>
+<code> s6dns_message_func_t s6dns_message_parse_answer_hinfo </code> <br />
+Parses HINFO RRs located in the answer section of the packet. The
+<em>data</em> argument is interpreted as a pointer to a
+<a href="http://skarnet.org/software/skalibs/libstddjb/genalloc.html">genalloc</a>
+containing <tt>s6dns_message_rr_hinfo_t</tt> structures.
+</p>
+
+<p>
+<code> s6dns_message_func_t s6dns_message_parse_answer_mx </code> <br />
+Parses MX RRs located in the answer section of the packet. The
+<em>data</em> argument is interpreted as a pointer to a
+<a href="http://skarnet.org/software/skalibs/libstddjb/genalloc.html">genalloc</a>
+containing <tt>s6dns_message_rr_mx_t</tt> structures.
+</p>
+
+<p>
+<code> s6dns_message_func_t s6dns_message_parse_answer_soa </code> <br />
+Parses SOA RRs located in the answer section of the packet. The
+<em>data</em> argument is interpreted as a pointer to a
+<a href="http://skarnet.org/software/skalibs/libstddjb/genalloc.html">genalloc</a>
+containing <tt>s6dns_message_rr_soa_t</tt> structures.
+</p>
+
+<p>
+<code> s6dns_message_func_t s6dns_message_parse_answer_srv </code> <br />
+Parses SRV RRs located in the answer section of the packet. The
+<em>data</em> argument is interpreted as a pointer to a
+<a href="http://skarnet.org/software/skalibs/libstddjb/genalloc.html">genalloc</a>
+containing <tt>s6dns_message_rr_srv_t</tt> structures.
+</p>
+
+<h3> High-level packet parsing </h3>
+
+<p>
+<code> int s6dns_message_parse (s6dns_message_header_t *h, char const *packet, unsigned int packetlen, s6dns_message_rr_func_t *f, void *data) </code> <br />
+ This function parses the DNS packet <em>packet</em> of length <em>packetlen</em>.
+It stores the packet header into *<em>h</em>. Then, for every RR in the answer,
+authority or additional section of the packet, it calls <em>f</em> with the
+relevant parameters. <em>data</em> is the extra pointer given to <em>f</em> to
+store information. The function returns 1 if the parsing succeeds. Otherwise it
+returns -1 if there is a local error unrelated to the packet, or 0 if no
+appropriate answer can be decoded from the packet. errno then contains one
+of the following values:
+</p>
+
+<ul>
+ <li> EPROTO: the packet is malformed, syntax error </li>
+ <li> EBADMSG: rcode 1, query format error </li>
+ <li> EBUSY: rcode 2, server failure </li>
+ <li> ENOENT: rcode 3, nxdomain </li>
+ <li> ENOTSUP: rcode 4, query type not implemented </li>
+ <li> ECONNREFUSED: rcode 5, operation refused by server </li>
+ <li> EIO: unknown rcode </li>
+ <li> any other value being set by <em>f</em> if it returns a failure code. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/libs6dns/s6dns-rci.html b/doc/libs6dns/s6dns-rci.html
new file mode 100644
index 0000000..1cf8a0e
--- /dev/null
+++ b/doc/libs6dns/s6dns-rci.html
@@ -0,0 +1,109 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6dns_rci library interface</title>
+ <meta name="Description" content="s6-dns: the s6dns_rci library interface" />
+ <meta name="Keywords" content="s6-dns dns s6dns_rci resolv.conf library libs6dns" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">libs6dns</a><br />
+<a href="../">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6dns_rci</tt> library interface </h1>
+
+<p>
+ The following functions and structures are declared in the <tt>s6-dns/s6dns-rci.h</tt> header,
+and implemented in the <tt>libs6dns.a</tt> or <tt>libs6dns.so</tt> library.
+</p>
+
+<h2> General information </h2>
+
+<p>
+ <tt>s6dns_rci</tt> provides functions to get information from
+the <tt>/etc/resolv.conf</tt> file.
+</p>
+
+<h2> Data structures </h2>
+
+<p>
+ A <tt>s6dns_rci_t</tt> is a structure storing information
+provided by the <tt>/etc/resolv.conf</tt> file, i.e.
+</p>
+
+<ul>
+ <li> The list of "nameserver" addresses, i.e. IP addresses of DNS caches
+contacted by clients for recursive queries </li>
+ <li> The list of qualification rules provided by "domain" or "search"
+lines </li>
+</ul>
+
+<p>
+ Nameserver addresses are stored in a
+<a href="s6dns-ip46.html">s6dns_ip46list_t</a>. Qualification rules are
+stored in a <a href="http://skarnet.org/software/skalibs/libstddjb/stralloc.html">stralloc</a>
+with an additional integer storing the number of rules.
+</p>
+
+<p>
+ Most programs won't need more than one <tt>s6dns_rci_t</tt>, so
+the library provides the global variable <tt>s6dns_rci_here</tt>, used
+by default in simple resolution macros.
+</p>
+
+<h2> Functions </h2>
+
+<p>
+<code> int s6dns_rci_init (s6dns_rci_t *rci, char const *file) </code> <br />
+Extracts information from <em>file</em>, which must be in <tt>/etc/resolv.conf</tt>
+format, and stores it into *<em>rci</em>. <em>rci</em> must be previously
+initialized to the <tt>S6DNS_RCI_ZERO</tt> constant. The function returns 1 if
+it succeeds, or 0 (and sets errno) if it fails.
+</p>
+
+<p>
+ If the DNSCACHEIP environment variable is set, and contains a list of
+IP addresses separated by commas, semicolons, spaces, tabs, newlines or
+carriage returns, then this list overrides any nameserver information
+from <em>file</em>. If the variable is empty, <em>file</em> will be used
+as the source of the information.
+</p>
+
+<p>
+ If the DNSQUALIFY environment variable is set, a list of domain suffixes,
+separated by spaces, tabs, newlines or carriage returns, is read from it,
+and overrides any qualification information from <em>file</em>. If the
+variable is empty, it amounts to one rule saying "no qualification".
+</p>
+
+<p>
+ <tt>s6dns_init()</tt> is an alias to
+<tt>s6dns_rci_init(&amp;s6dns_rci_here, "/etc/resolv.conf")</tt>.
+</p>
+
+<p>
+<code> void s6dns_rci_free (s6dns_rci_t *rci) </code> <br />
+Frees the memory used by *<em>rci</em>. <em>rci</em> is then suitable to
+be reused in a <tt>s6dns_rci_init</tt> call.
+</p>
+
+<p>
+ <tt>s6dns_finish()</tt> calls <tt>s6dns_rci_free(&amp;s6dns_rci_here)</tt>.
+</p>
+
+<p>
+<code> unsigned int s6dns_qualify (s6dns_domain_t *list, s6dns_domain_t const *d) </code> <br />
+Qualifies domain *<em>d</em> into the list of domains pointed to by <em>list</em>
+according to the rules stored in <tt>s6dns_rci_here</tt>. Returns the number of
+written domains (0 if it fails); this number cannot exceed
+<tt>s6dns_rci_here.rulesnum</tt>.
+</p>
+
+</body>
+</html>
diff --git a/doc/libs6dns/s6dns-resolve.html b/doc/libs6dns/s6dns-resolve.html
new file mode 100644
index 0000000..bbb3f57
--- /dev/null
+++ b/doc/libs6dns/s6dns-resolve.html
@@ -0,0 +1,387 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6dns_resolve library interface</title>
+ <meta name="Description" content="s6-dns: the s6dns_resolve library interface" />
+ <meta name="Keywords" content="s6-dns dns s6dns_resolve library libs6dns" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">libs6dns</a><br />
+<a href="../">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6dns_resolve</tt> library interface </h1>
+
+<p>
+ The following functions are declared in the <tt>s6-dns/s6dns-resolve.h</tt> header,
+and implemented in the <tt>libs6dns.a</tt> or <tt>libs6dns.so</tt> library.
+</p>
+
+<h2> General information </h2>
+
+<p>
+ <tt>s6dns_resolve</tt> provides functions and macros - mostly macros -
+to perform high level synchronous DNS resolution.
+</p>
+
+<p>
+ All the functions declared here make synchronous calls to the network, so
+they can block for a non-negligible amount of time. To avoid unbounded
+waiting times, they always take 2 arguments at the end, <em>deadline</em>
+and <em>stamp</em>. <em>deadline</em> is the read-only address of a
+<a href="http://skarnet.org/software/skalibs/libstddjb/tai.h">struct taia</a>
+containing an absolute time which is the deadline for the function, and
+<em>stamp</em> is the read-write address of a <tt>struct taia</tt> being
+an accurate enough representation of the current absolute time. If
+the function has not returned by *<em>deadline</em>, then it immediately
+returns with a failure code and errno set to ETIMEDOUT. In every case,
+*<em>stamp</em> is automatically updated so it always represents the
+absolute time accurately enough.
+</p>
+
+<p>
+ In a single-threaded program, the STAMP global variable can be used to
+store the current time. Macros ending with <tt>_g</tt> use this variable
+automatically so you don't have to provide the <em>stamp</em> argument
+everytime. Additionally, several resolution functions make implicit use
+of global variables such as:
+</p>
+
+<ul>
+ <li> <tt>s6dns_engine_here</tt>: a global
+<a href="s6dns-engine.html">s6dns_engine_t</a> storing the current
+query, for sequential queries. </li>
+ <li> <tt>s6dns_debughook_zero</tt>: a global <tt>s6dns_debughook_t</tt>
+meaning no debugging is needed. </li>
+ <li> <tt>s6dns_rci_here</tt>: a global
+<a href="s6dns-rci.html">s6dns_rci_t</a> containing the current
+resolv.conf information. </li>
+</ul>
+
+<p>
+ Reentrant, non-global-using functions are also provided, with the <tt>_r</tt>
+suffix. In other words, if <em>foobarfunc</em> is a resolution function,
+the following prototypes are generally provided, from the simplest to the
+most complex:
+</p>
+
+<ul>
+ <li> <em>foobarfunc_g</em>: only the relevant parameters and a deadline
+must be given, the function uses the STAMP global and maybe DNS-specific
+globals </li>
+ <li> <em>foobarfunc</em>: a deadline and stamp must be given, the function
+uses all the DNS-specific globals </li>
+ <li> <em>foobarfunc_r_g</em>: No DNS-specific globals are used, the
+information must be given via parameters, but the STAMP global is used </li>
+ <li> <em>foobarfunc_r</em>: fully reentrant function using no globals
+at all </li>
+</ul>
+
+<p>
+ For each set of four functions, only one is documented here.
+The other prototypes can be found in the <tt>s6-dns/s6dns-resolve.h</tt> file.
+</p>
+
+<p>
+ Some <tt>errno</tt> codes reported by these functions do not have
+exactly the system meaning given by
+<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html">strerror()</a>.
+To get a user-friendly error message, use
+<tt>s6dns_constants_error_str(errno)</tt> instead. The <tt>s6dns_constants_error_str</tt>
+function is declared in the <tt>s6dns-constants.h</tt> header.
+</p>
+
+<h2> Functions </h2>
+
+<h3> Single query resolution </h3>
+
+<p>
+ These functions are ordered from the lowest level to the highest level.
+</p>
+
+<h4> Basic wrapper around s6dns_engine </h4>
+
+<p>
+<code> int s6dns_resolve_loop_r_g (s6dns_engine_t *dt, struct taia const *deadline) </code> <br />
+Resolves the query stored in <tt>dt</tt>.
+Returns 1 on success or 0 on failure.
+</p>
+
+<h4> Generic resolution functions </h4>
+
+<p>
+<code> int s6dns_resolve_core_g (s6dns_domain_t const *d, uint16 qtype, struct taia const *deadline) </code> <br />
+Resolves the query on domain *<em>d</em> (in packet form), of type <em>qtype</em>.
+Returns 0 on failure, or 1 on success, in which case
+<tt>s6dns_engine_here</tt> contains the answer.
+</p>
+
+<p>
+<code> int s6dns_resolve_parse_g (s6dns_domain_t const *d, uint16 qtype, s6dns_message_rr_func_t *f, void *data, struct taia const *deadline) </code> <br />
+Resolves the query on domain *<em>d</em> (in packet form), of type <em>qtype</em>,
+then parses the answer with function <em>f</em> and stores the result into <em>data</em>.
+Returns 1 if it succeeds, 0 if no data can be extracted from the answer, or -1 if an
+error occurs. Sets errno in the last two cases.
+</p>
+
+<p>
+ Note that the function can return 1 without appending anything to <em>data</em>.
+This means that the servers confirmed that the domain exists, but <em>f</em>
+has not been able to find any data relevant to the query in the answer.
+This is very different from NXDOMAIN, which means that
+the servers deny the actual existence of the domain, and which is reported
+here as a return code of 0 with errno set to ENOENT.
+</p>
+
+<p>
+<code> int s6dns_resolvenoq_g (char const *name, unsigned int len, uint16 qtype, s6dns_message_rr_func_t *f, void *data, struct taia const *deadline) </code> <br />
+Performs a query of type <em>qtype</em> on name <em>name</em> of length <em>len</em>,
+without qualifying it. Parses the answer with function <em>f</em> and stores the
+result into <em>data</em>. Returns 1 if it succeeds, 0 if no data can be extracted,
+or -1 if an error occurs. Sets errno in the last two cases.
+</p>
+
+<p>
+<code> int s6dns_resolveq_g (char const *name, unsigned int len, uint16 qtype, s6dns_message_rr_func_t *f, void *data, struct taia const *deadline) </code> <br />
+Performs a query of type <em>qtype</em> on name <em>name</em> of length <em>len</em>,
+qualifying it first. Parses the answer with function <em>f</em> and stores the
+result into <em>data</em>. Returns 1 if it succeeds, 0 if none of the FQDNs can
+get a positive answer, or -1 if an error occurs. Sets errno in the last two cases.
+</p>
+
+<p>
+<code> int s6dns_resolve_g (char const *name, unsigned int len, uint16 qtype, s6dns_message_rr_func_t *f, void *data, int qualif, struct taia const *deadline) </code> <br />
+Performs a query of type <em>qtype</em> on name <em>name</em> of length <em>len</em>.
+Qualifies <em>name</em> first if <em>qualif</em> is nonzero; else, does not
+qualify it. Parses the answer with function <em>f</em> and stores the
+result into <em>data</em>. Returns 1 if it succeeds, 0 if none of the FQDNs can
+get a positive answer, or -1 if an error occurs. Sets errno in the last two cases.
+</p>
+
+<h4> High-level type-specific functions </h4>
+
+<p>
+<code> int s6dns_resolve_a_g (stralloc *ips, char const *name, unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+Performs an A query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the IPs are
+appended to the stralloc *<em>ips</em>, using 4 bytes per answer.
+</p>
+
+<p>
+<code> int s6dns_resolve_aaaa_g (stralloc *ips, char const *name, unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+Performs an AAAA query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the IPs are
+appended to the stralloc *<em>ips</em>, using 16 bytes per answer.
+</p>
+
+<p>
+<code> int s6dns_resolve_aaaaa_g (genalloc *ips, char const *name, unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+Performs an AAAA query and an A query at the same time on name <em>name</em>
+of length <em>len</em>, qualifying it first iff <em>qualif</em> is nonzero.
+Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or a positive number if it succeeds: 1 if IPv4 addresses
+were found, 2 if IPv6 addresses were found, and 3 if both were found.
+The IPs are appended to the genalloc *<em>ips</em>, which contains an array of
+<tt>ip46_t</tt>, the skalibs structure used to store IPv4 and IPv6 addresses
+indiscriminately.
+</p>
+
+<p>
+<code> int s6dns_resolve_ptr_g (genalloc *ds, char const *name, unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+Performs a PTR query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>ds</em>, which contains an array of <tt>s6dns_domain_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_name4_g (genalloc *ds, char const *ip, struct taia const *deadline) </code> <br />
+Performs a PTR query on the <tt>in-addr.arpa.</tt> name representing the IPv4
+address <em>ip</em> (4 network-order bytes).
+Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>ds</em>, which contains an array of <tt>s6dns_domain_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_name6_g (genalloc *ds, char const *ip, struct taia const *deadline) </code> <br />
+Performs a PTR query on the <tt>ip6.arpa.</tt> name representing the IPv6
+address <em>ip</em> (16 network-order bytes).
+Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>ds</em>, which contains an array of <tt>s6dns_domain_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_name46_g (genalloc *ds, ip46_t const *ip, struct taia const *deadline) </code> <br />
+Calls <tt>s6dns_resolve_name6_g()</tt> or <tt>s6dns_resolve_name4_g()</tt>
+depending on which <em>ip</em> is an IPv6 or IPv4 address.
+</p>
+
+<p>
+<code> int s6dns_resolve_ns_g (genalloc *ds, char const *name, unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+Performs a NS query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>ds</em>, which contains an array of <tt>s6dns_domain_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_cname_g (genalloc *ds, char const *name, unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+Performs a CNAME query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>ds</em>, which contains an array of <tt>s6dns_domain_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_hinfo_g (genalloc *hinfos, char const *name,
+unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+ Performs an HINFO query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>hinfos</em>, which contains an array of
+<tt>s6dns_message_rr_hinfo_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_mx_g (genalloc *mxs, char const *name,
+unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+ Performs an MX query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>mxs</em>, which contains an array of
+<tt>s6dns_message_rr_mx_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_soa_g (genalloc *soas, char const *name,
+unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+ Performs an SOA query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>soas</em>, which contains an array of
+<tt>s6dns_message_rr_soa_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_srv_g (genalloc *srvs, char const *name,
+unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+ Performs an SRV query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case the domains are
+appended to the genalloc *<em>srvs</em>, which contains an array of
+<tt>s6dns_message_rr_srv_t</tt>.
+</p>
+
+<p>
+<code> int s6dns_resolve_txt_g (stralloc *sa, genalloc *offsets, char const *name,
+unsigned int len, int qualif, struct taia const *deadline) </code> <br />
+ Performs an TXT query on name <em>name</em> of length <em>len</em>, qualifying it
+iff <em>qualif</em> is nonzero. Returns -1 if an error occurs, or 0 if no answer
+can be obtained from servers, or 1 if it succeeds, in which case:
+</p>
+
+<ul>
+ <li> The resulting strings are all stored into stralloc *<em>sa</em>.
+Every string is terminated by a null character. </li>
+ <li> A series of unsigned ints is appended to genalloc *<em>offsets</em>.
+Every integer represents the offset in *<em>sa</em> at which a string is stored.
+The number of appended integers is the total number of answers. </li>
+</ul>
+
+<h4> Parallel resolution </h4>
+
+<p>
+<code> int s6dns_resolven_loop_g (s6dns_engine_t *dtl, unsigned int n,
+unsigned int or, struct taia const *deadline) </code> <br />
+ Resolves the <em>n</em> queries stored in the array pointed to by <em>dtl</em>,
+in parallel. If <em>or</em> is zero, it does not return before all answers
+have arrived. If <em>or</em> is 1, it returns when an answer arrives, but does
+not return if a query generates an error (unless all queries do so). If
+<em>or</em> is 2, it returns when an answer arrives or an error occurs.
+Other values of <em>or</em> are unspecified yet.
+
+<p>
+ The return code is as follows:
+</p>
+
+<ul>
+ <li> If <em>or</em> is 0: -1 on a global error (i.e. not specific to
+a query), or a non-negative number which is the total of successful
+queries. </li>
+ <li> If <em>or</em> is 1 or 2: -1 on a global error, or a non-negative
+number which is the index of the query that triggered the event. </li>
+</ul>
+
+<p>
+ If <em>or</em> is 1, a return code of -1 with errno set to ENOENT
+means that all the queries failed.
+</p>
+
+<p>
+ After the function returns, the <tt>status</tt> field of each
+<tt>s6dns_engine_t</tt> contains the error code relative to the query.
+A status of 0 means that an answer has properly arrived; EAGAIN means
+that the query is still pending (and the s6dns_engine_t has not been
+recycled); ECONNABORTED means that the query has not been properly
+initialized. Other codes report various network problems.
+</p>
+
+<p>
+<code> int s6dns_resolven_parse_g (s6dns_resolve_t const *list, unsigned int n,
+struct taia const *deadline) </code> <br />
+Performs <em>n</em> complete resolutions in parallel, parsing the results.
+Returns 1 in case of success or 0 if a global error occurred.
+</p>
+
+<p>
+<em>list</em> is a pointer to an array of <em>n</em> <tt>s6dns_resolve_t</tt>,
+which is a structure containing at least the following fields:
+</p>
+
+<ul>
+ <li> <tt>q</tt>&nbsp;: a <tt>s6dns_domain_t</tt> containing the query.
+It must be in encoded form. </li>
+ <li> <tt>qtype</tt>&nbsp;: a <tt>uint16</tt> containing the query type.
+A list of valid query types can be found in the <tt>s6dns-constants.h</tt>
+header. </li>
+ <li> <tt>options</tt>&nbsp;: a <tt>uint32</tt> containing options passed
+to <a href="s6dns_engine.html">s6dns_engine_init()</a>. </li>
+ <li> <tt>deadline</tt>&nbsp;: a <tt>struct taia</tt> containing the
+deadline for this query, i.e. the query will fail with an ETIMEDOUT code
+if no answer has arrived by then. Note that the <em>deadline</em>
+argument given to s6dns_resolven_parse() is a global deadline that
+can make the function return with 0 ETIMEDOUT, but is independent from
+this field, which is local to every query. </li>
+ <li> <tt>parsefunc</tt>&nbsp;: a <tt>s6dns_message_rr_func_t</tt>
+function that will be used to parse the answer. </li>
+ <li> <tt>data</tt>&nbsp;: a <tt>void *</tt> that will be passed as an
+additional pointer to the <tt>parsefunc</tt> function; it is generally
+used to store the parsing result. </li>
+ <li> <tt>status</tt>&nbsp;: an <tt>int</tt>. It does not need to be
+initialized. It will contain the error code for the query after the
+function returns. ECONNABORTED means that the query could not be
+started at all; EAGAIN means that the query was still pending when
+an error happened; other codes report various network problems. </li>
+</ul>
+
+<p>
+ The s6dns_resolven_parse() function is a simple, convenient way to
+perform several resolutions in parallel to avoid the waiting time
+incurred by serial resolutions. However, it is still a synchronous
+function, and cannot replace a real asynchronous DNS library: for
+more complex parallel resolution needs, use the
+<a href="../skadns/">skadns</a> library.
+</p>
+
+</body>
+</html>
diff --git a/doc/s6-dnsip4-filter.html b/doc/s6-dnsip4-filter.html
new file mode 100644
index 0000000..5433438
--- /dev/null
+++ b/doc/s6-dnsip4-filter.html
@@ -0,0 +1,111 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsip4-filter program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsip4-filter program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsip4-filter dnsip domain name ip address ipv4 filter" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsip4-filter program </h1>
+
+<p>
+ s6-dnsip4-filter reads domain names on its standard input and
+prints the corresponding IPv4 addresses on its standard output.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsip4-filter [ -l <em>maxlines</em> ] [ -c <em>maxconn</em> ] [ -t <em>timeout</em> ] [ -f <em>normalfmt</em> ] [ -e <em>errorfmt</em> ]
+</pre>
+
+<ul>
+ <li> s6-dnsip4-filter reads lines from its stdin, processes them, and prints
+the processed lines to its stdout, in the same order. It exits 0 when it
+reads EOF on stdin. </li>
+ <li> Every line starting with a domain name is processed. Lines not
+starting with a domain name are not, and the formatting rules treat them
+as a pure "remainder". </li>
+ <li> For every processed line, an A query is made, and the result is
+printed according to the formatting rules. The domain is not qualified
+before being resolved. </li>
+ <li> By default, s6-dnsip4-filter looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-l</tt>&nbsp;<em>maxlines</em>&nbsp;: maximum lines. s6-dnsip4-filter
+will keep at most <em>maxlines</em> lines in memory at the same time. If line
+<em>n</em> is still waiting for resolution and cannot be printed, then
+s6-dnsip4-filter will stop reading from stdin before line <em>n+maxlines</em>,
+until the result arrives for line <em>n</em>. By default, <em>maxlines</em>
+is 256. </li>
+ <li> <tt>-c</tt>&nbsp;<em>maxconn</em>&nbsp;: maximum concurrent resolutions.
+s6-dnsip4-filter will perform at most <em>maxconn</em> resolutions in
+parallel at the same time. <em>maxlines</em> cannot be lesser than <em>maxconn</em>.
+By default, <em>maxconn</em> is 128. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if any resolution takes more
+than <em>timeout</em> milliseconds, then it is aborted and printed as a
+timeout error. By default, <em>timeout</em> is 0, which means no timeout. </li>
+ <li> <tt>-f</tt>&nbsp;<em>normalfmt</em>&nbsp;: print the positive results according to
+the <em>normalfmt</em> format string. By default, <em>normalfmt</em> is
+<tt>%s=%d%w%r</tt>, which means: print the original domain name, then an equal
+sign, then the corresponding IPv4 address, then the remainder of the line. </li>
+ <li> <tt>-e</tt>&nbsp;<em>errorfmt</em>&nbsp;: print the negative results according to
+the <em>errorfmt</em> format string. By default, <em>errorfmt</em> is
+<tt>%s=&lt;%e%&gt;%w%r</tt>, which means: print the original domain name, then an equal
+sign, then the error message between angle brackets, then the remainder of the line. </li>
+</ul>
+
+<h2> Formatting rules </h2>
+
+<p>
+ <em>normalfmt</em> and <em>errorfmt</em> are format strings, i.e. they tell the
+program how a line must be printed. The following sequences are recognized:
+</p>
+
+<ul>
+ <li> <tt>%%</tt> prints a single <tt>%</tt> character </li>
+ <li> <tt>%s</tt> prints the original domain name </li>
+ <li> <tt>%d</tt> prints the IP address, if any </li>
+ <li> <tt>%e</tt> prints the error message, if any </li>
+ <li> <tt>%w%r</tt> prints the remainder of the input line </li>
+</ul>
+
+<h2> Example </h2>
+
+<pre>
+ s6-dnsns google.com | s6-dnsip4-filter -f "%d"
+</pre>
+
+<p>
+ prints all the nameserver addresses for the <tt>google.com</tt>
+domain. This is useful, for instance, to give the result as an
+argument to <a href="s6-dnsq.html">s6-dnsq</a>.
+</p>
+
+<h2> Notes </h2>
+
+<p>
+ s6-dnsip4-filter does not perform DNS resolutions itself. Instead, if forks
+a <a href="skadns/skadnsd.html">skadnsd</a> child and sends it queries, getting
+the results asynchronously. The s6-dns filter programs have actually been
+written as example uses of the <a href="skadns/">skadns library</a>.
+</p>
+
+</body>
+</html>
diff --git a/doc/s6-dnsip4.html b/doc/s6-dnsip4.html
new file mode 100644
index 0000000..72f585b
--- /dev/null
+++ b/doc/s6-dnsip4.html
@@ -0,0 +1,63 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsip4 program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsip4 program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsip4 dnsip domain name ip address ipv4" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsip4 program </h1>
+
+<p>
+ s6-dnsip4 finds the IPv4 addresses associated to a domain name.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsip4 [ -q ] [ -r ] [ -t <em>timeout</em> ] <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnsip4 makes an A query for the name <em>domain</em>. It
+waits for the result and prints the obtained IPv4 addresses, one per line,
+then exits 0. </li>
+ <li> If the domain exists but no relevant field has been found, it exits 1. </li>
+ <li> If the DNS answered but no answer is available, it prints a relevant
+error message and exits 2. </li>
+ <li> By default, s6-dnsip4 looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-q</tt>&nbsp;: qualify. Qualifies <em>domain</em> before resolution,
+according to suffixes found in <tt>/etc/resolv.conf</tt>. If the DNSQUALIFY
+environment variable is set and contains a list of suffixes separated by spaces,
+tabs, newlines or carriage returns, then this list is used instead. By
+default, no qualification is used: if <em>domain</em> is not a FQDN, a dot
+is just appended to it. </li>
+ <li> <tt>-r</tt>&nbsp;: random. By default, the program does not sort the
+result, but prints them in the order received from the DNS. With this
+option, it performs a random permutation on the results before printing
+them. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnsip6-filter.html b/doc/s6-dnsip6-filter.html
new file mode 100644
index 0000000..7cbad6b
--- /dev/null
+++ b/doc/s6-dnsip6-filter.html
@@ -0,0 +1,99 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsip6-filter program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsip6-filter program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsip6-filter dnsip domain name ip address ipv6 filter" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsip6-filter program </h1>
+
+<p>
+ s6-dnsip6-filter reads domain names on its standard input and
+prints the corresponding IPv6 addresses on its standard output.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsip6-filter [ -l <em>maxlines</em> ] [ -c <em>maxconn</em> ] [ -t <em>timeout</em> ] [ -f <em>normalfmt</em> ] [ -e <em>errorfmt</em> ]
+</pre>
+
+<ul>
+ <li> s6-dnsip6-filter reads lines from its stdin, processes them, and prints
+the processed lines to its stdout, in the same order. It exits 0 when it
+reads EOF on stdin. </li>
+ <li> Every line starting with a domain name is processed. Lines not
+starting with a domain name are not, and the formatting rules treat them
+as a pure "remainder". </li>
+ <li> For every processed line, an AAAA query is made, and the result is
+printed according to the formatting rules. The domain is not qualified before
+being resolved. </li>
+ <li> By default, s6-dnsip6-filter looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-l</tt>&nbsp;<em>maxlines</em>&nbsp;: maximum lines. s6-dnsip6-filter
+will keep at most <em>maxlines</em> lines in memory at the same time. If line
+<em>n</em> is still waiting for resolution and cannot be printed, then
+s6-dnsip6-filter will stop reading from stdin before line <em>n+maxlines</em>,
+until the result arrives for line <em>n</em>. By default, <em>maxlines</em>
+is 256. </li>
+ <li> <tt>-c</tt>&nbsp;<em>maxconn</em>&nbsp;: maximum concurrent resolutions.
+s6-dnsip6-filter will perform at most <em>maxconn</em> resolutions in
+parallel at the same time. <em>maxlines</em> cannot be lesser than <em>maxconn</em>.
+By default, <em>maxconn</em> is 128. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if any resolution takes more
+than <em>timeout</em> milliseconds, then it is aborted and printed as a
+timeout error. By default, <em>timeout</em> is 0, which means no timeout. </li>
+ <li> <tt>-f</tt>&nbsp;<em>normalfmt</em>&nbsp;: print the positive results according to
+the <em>normalfmt</em> format string. By default, <em>normalfmt</em> is
+<tt>%s=%d%w%r</tt>, which means: print the original domain name, then an equal
+sign, then the corresponding IPv6 address, then the remainder of the line. </li>
+ <li> <tt>-e</tt>&nbsp;<em>errorfmt</em>&nbsp;: print the negative results according to
+the <em>errorfmt</em> format string. By default, <em>errorfmt</em> is
+<tt>%s=&lt;%e%&gt;%w%r</tt>, which means: print the original domain name, then an equal
+sign, then the error message between angle brackets, then the remainder of the line. </li>
+</ul>
+
+<h2> Formatting rules </h2>
+
+<p>
+ <em>normalfmt</em> and <em>errorfmt</em> are format strings, i.e. they tell the
+program how a line must be printed. The following sequences are recognized:
+</p>
+
+<ul>
+ <li> <tt>%%</tt> prints a single <tt>%</tt> character </li>
+ <li> <tt>%s</tt> prints the original domain name </li>
+ <li> <tt>%d</tt> prints the IP address, if any </li>
+ <li> <tt>%e</tt> prints the error message, if any </li>
+ <li> <tt>%w%r</tt> prints the remainder of the input line </li>
+</ul>
+
+<h2> Notes </h2>
+
+<p>
+ s6-dnsip6-filter does not perform DNS resolutions itself. Instead, if forks
+a <a href="skadns/skadnsd.html">skadnsd</a> child and sends it queries, getting
+the results asynchronously. The s6-dns filter programs have actually been
+written as example uses of the <a href="skadns/">skadns library</a>.
+</p>
+
+</body>
+</html>
diff --git a/doc/s6-dnsip6.html b/doc/s6-dnsip6.html
new file mode 100644
index 0000000..d417011
--- /dev/null
+++ b/doc/s6-dnsip6.html
@@ -0,0 +1,73 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsip6 program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsip6 program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsip6 dnsip domain name ip address ipv6" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsip6 program </h1>
+
+<p>
+ s6-dnsip6 finds the IPv6 addresses associated to a domain name.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsip6 [ -q ] [ -r ] [ -t <em>timeout</em> ] <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnsip6 makes an AAAA query for the name <em>domain</em>. It
+waits for the result and prints the obtained IPv6 addresses, one per line,
+then exits 0. </li>
+ <li> If the domain exists but no relevant field has been found, it exits 1. </li>
+ <li> If the DNS answered but no answer is available, it prints a relevant
+error message and exits 2. </li>
+ <li> By default, s6-dnsip6 looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-q</tt>&nbsp;: qualify. Qualifies <em>domain</em> before resolution,
+according to suffixes found in <tt>/etc/resolv.conf</tt>. If the DNSQUALIFY
+environment variable is set and contains a list of suffixes separated by spaces,
+tabs, newlines or carriage returns, then this list is used instead. By
+default, no qualification is used: if <em>domain</em> is not a FQDN, a dot
+is just appended to it. </li>
+ <li> <tt>-r</tt>&nbsp;: random. By default, the program does not sort the
+result, but prints them in the order received from the DNS. With this
+option, it performs a random permutation on the results before printing
+them. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<p>
+ Bear in mind that <em>making AAAA queries</em> is very different, and
+totally independent, from <em>using IPv6 transport for the DNS queries</em>.
+Even if the underlying skalibs has been compiled without IPv6 support,
+or IPv6 DNS transport is unavailable for any reason, you can still perform
+AAAA queries and s6-dnsip6 will answer correctly.
+</p>
+
+</body>
+</html>
diff --git a/doc/s6-dnsmx.html b/doc/s6-dnsmx.html
new file mode 100644
index 0000000..1396aed
--- /dev/null
+++ b/doc/s6-dnsmx.html
@@ -0,0 +1,64 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsmx program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsmx program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsmx dnsmx domain name mx mail exchanger" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsmx program </h1>
+
+<p>
+ s6-dnsmx finds the MX information associated to a domain name.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsmx [ -q ] [ -r ] [ -t <em>timeout</em> ] <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnsmx makes an MX query for the name <em>domain</em>. It
+waits for the result and prints the obtained information, one exchanger
+per line, then exits 0. It prints the preference, then a space, then
+the exchanger name. </li>
+ <li> If the domain exists but no relevant field has been found, it exits 1. </li>
+ <li> If the DNS answered but no answer is available, it prints a relevant
+error message and exits 2. </li>
+ <li> By default, s6-dnsmx looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-q</tt>&nbsp;: qualify. Qualifies <em>domain</em> before resolution,
+according to suffixes found in <tt>/etc/resolv.conf</tt>. If the DNSQUALIFY
+environment variable is set and contains a list of suffixes separated by spaces,
+tabs, newlines or carriage returns, then this list is used instead. By
+default, no qualification is used: if <em>domain</em> is not a FQDN, a dot
+is just appended to it. </li>
+ <li> <tt>-r</tt>&nbsp;: random. By default, the program does not sort the
+result, but prints them in the order received from the DNS. With this
+option, it performs a random permutation on the results before printing
+them. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnsname-filter.html b/doc/s6-dnsname-filter.html
new file mode 100644
index 0000000..1554270
--- /dev/null
+++ b/doc/s6-dnsname-filter.html
@@ -0,0 +1,103 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsname-filter program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsname-filter program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsname-filter dnsname domain name ptr ip address filter" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsname-filter program </h1>
+
+<p>
+ s6-dnsname-filter reads IP addresses on its standard input and
+prints the corresponding domain names on its standard output.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsname-filter [ -4 ] [ -6 ] [ -l <em>maxlines</em> ] [ -c <em>maxconn</em> ] [ -t <em>timeout</em> ] [ -f <em>normalfmt</em> ] [ -e <em>errorfmt</em> ]
+</pre>
+
+<ul>
+ <li> s6-dnsname-filter reads lines from its stdin, processes them, and prints
+the processed lines to its stdout, in the same order. It exits 0 when it
+reads EOF on stdin. </li>
+ <li> Every line starting with an IP address is processed. Lines not
+starting with an IP address are not, and the formatting rules treat them
+as a pure "remainder". </li>
+ <li> For every processed line, the read IP address is converted to an
+<tt>in-addr.arpa.</tt> or <tt>ip6.arpa.</tt> domain on which a PTR query is made,
+and the result is printed according to the formatting rules. </li>
+ <li> By default, s6-dnsname-filter looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-4</tt>&nbsp;: scan for IPv4 addresses. </li>
+ <li> <tt>-6</tt>&nbsp;: scan for IPv6 addresses. Both the <tt>-4</tt> and
+<tt>-6</tt> options may be given. If none is given, by default
+s6-dnsname-filter will only scan for IPv4 addresses. </li>
+ <li> <tt>-l</tt>&nbsp;<em>maxlines</em>&nbsp;: maximum lines. s6-dnsname-filter
+will keep at most <em>maxlines</em> lines in memory at the same time. If line
+<em>n</em> is still waiting for resolution and cannot be printed, then
+s6-dnsname-filter will stop reading from stdin before line <em>n+maxlines</em>,
+until the result arrives for line <em>n</em>. By default, <em>maxlines</em>
+is 256. </li>
+ <li> <tt>-c</tt>&nbsp;<em>maxconn</em>&nbsp;: maximum concurrent resolutions.
+s6-dnsname-filter will perform at most <em>maxconn</em> resolutions in
+parallel at the same time. <em>maxlines</em> cannot be lesser than <em>maxconn</em>.
+By default, <em>maxconn</em> is 128. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if any resolution takes more
+than <em>timeout</em> milliseconds, then it is aborted and printed as a
+timeout error. By default, <em>timeout</em> is 0, which means no timeout. </li>
+ <li> <tt>-f</tt>&nbsp;<em>normalfmt</em>&nbsp;: print the positive results according to
+the <em>normalfmt</em> format string. By default, <em>normalfmt</em> is
+<tt>%s=%d%w%r</tt>, which means: print the original IP address, then an equal
+sign, then the corresponding domain name, then the remainder of the line. </li>
+ <li> <tt>-e</tt>&nbsp;<em>errorfmt</em>&nbsp;: print the negative results according to
+the <em>errorfmt</em> format string. By default, <em>errorfmt</em> is
+<tt>%s=&lt;%e%&gt;%w%r</tt>, which means: print the original IP address, then an equal
+sign, then the error message between angle brackets, then the remainder of the line. </li>
+</ul>
+
+<h2> Formatting rules </h2>
+
+<p>
+ <em>normalfmt</em> and <em>errorfmt</em> are format strings, i.e. they tell the
+program how a line must be printed. The following sequences are recognized:
+</p>
+
+<ul>
+ <li> <tt>%%</tt> prints a single <tt>%</tt> character </li>
+ <li> <tt>%s</tt> prints the original IP address </li>
+ <li> <tt>%d</tt> prints the domain name, if any </li>
+ <li> <tt>%e</tt> prints the error message, if any </li>
+ <li> <tt>%w%r</tt> prints the remainder of the input line </li>
+</ul>
+
+<h2> Notes </h2>
+
+<p>
+ s6-dnsname-filter does not perform DNS resolutions itself. Instead, if forks
+a <a href="skadns/skadnsd.html">skadnsd</a> child and sends it queries, getting
+the results asynchronously. The s6-dns filter programs have actually been
+written as example uses of the <a href="skadns/">skadns library</a>.
+</p>
+
+</body>
+</html>
diff --git a/doc/s6-dnsname.html b/doc/s6-dnsname.html
new file mode 100644
index 0000000..0c0c5b0
--- /dev/null
+++ b/doc/s6-dnsname.html
@@ -0,0 +1,72 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsname program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsname program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsname dnsname domain name ip address ipv4 ipv6" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsname program </h1>
+
+<p>
+ s6-dnsname finds the name associated to an IPv4 or IPv6 address.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsname [ -4 | -6 ] [ -r ] [ -t <em>timeout</em> ] <em>ip</em>
+</pre>
+
+<ul>
+ <li> s6-dnsname converts the IP address <em>ip</em> to a name
+ending in <tt>in-addr.arpa.</tt> or <tt>ip6.arpa.</tt> then makes a
+PTR query for this name.
+It waits for the result and prints the obtained names, one per line,
+then exits 0. </li>
+ <li> If the domain exists but no relevant field has been found, it exits 1. </li>
+ <li> If the DNS answered but no answer is available, it prints a relevant
+error message and exits 2. </li>
+ <li> By default, s6-dnsname looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-4</tt>&nbsp;: interpret <em>ip</em> as an IPv4 address. </li>
+ <li> <tt>-6</tt>&nbsp;: interpret <em>ip</em> as an IPv6 address.
+If neither of the <tt>-4</tt> and <tt>-6</tt> is given, or if both are
+given, then <em>ip</em> will be interpreted as v4 or v6 depending on
+its syntax. </li>
+ <li> <tt>-r</tt>&nbsp;: random. By default, the program does not sort the
+result, but prints them in the order received from the DNS. With this
+option, it performs a random permutation on the results before printing
+them. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> If the underlying skalibs has been compiled with IPv6 support disabled,
+s6-dnsname will not be able to use IPv6 transport for its resolution, but it
+will still accept and resolve IPv6 addresses. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnsns.html b/doc/s6-dnsns.html
new file mode 100644
index 0000000..c15a7df
--- /dev/null
+++ b/doc/s6-dnsns.html
@@ -0,0 +1,63 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsns program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsns program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsns domain name nameserver ns rr field" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsns program </h1>
+
+<p>
+ s6-dnsns finds the relevant nameservers providing data for a domain.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsns [ -q ] [ -r ] [ -t <em>timeout</em> ] <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnsns makes an NS query for the name <em>domain</em>. It
+waits for the result and prints the obtained information, one name
+per line, then exits 0. </li>
+ <li> If the domain exists but no relevant field has been found, it exits 1. </li>
+ <li> If the DNS answered but no answer is available, it prints a relevant
+error message and exits 2. </li>
+ <li> By default, s6-dnsns looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-q</tt>&nbsp;: qualify. Qualifies <em>domain</em> before resolution,
+according to suffixes found in <tt>/etc/resolv.conf</tt>. If the DNSQUALIFY
+environment variable is set and contains a list of suffixes separated by spaces,
+tabs, newlines or carriage returns, then this list is used instead. By
+default, no qualification is used: if <em>domain</em> is not a FQDN, a dot
+is just appended to it. </li>
+ <li> <tt>-r</tt>&nbsp;: random. By default, the program does not sort the
+result, but prints them in the order received from the DNS. With this
+option, it performs a random permutation on the results before printing
+them. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnsq.html b/doc/s6-dnsq.html
new file mode 100644
index 0000000..9a22602
--- /dev/null
+++ b/doc/s6-dnsq.html
@@ -0,0 +1,82 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsq program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsq program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsq dnsq analysis debug" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsq program </h1>
+
+<p>
+ s6-dnsq is an analysis and debug tool. It performs a non-recursive DNS query
+to a given list of servers,
+then prints the contents of the answer packet, and optionally debug
+information during the resolution.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsq [ -1 | -2 ] [ -t <em>timeout</em> ] [ -D <em>level</em> ] <em>qtype</em> <em>domain</em> <em>serverlist</em>
+</pre>
+
+<ul>
+ <li> s6-dnsq makes an iterative DNS query of type <em>qtype</em> for the name <em>domain</em>
+to <em>serverlist</em>.
+It prints the answer packet, in human-readable form, to its standard output,
+then exits 0. </li>
+ <li> It does not qualify <em>domain</em>. </li>
+ <li> If the resolution fails for some reason, s6-dnsq exits 2. </li>
+ <li> <em>serverlist</em> is a list of IP addresses (v4 or v6), one or more addresses
+per argument on the command line (so you can use, for instance,
+<tt>`s6-dnsip4 ns.example.com`</tt> as argument, and get all the addresses for
+<tt>ns.example.com</tt> in your server list).
+Servers are tried in the order given, until a definitive
+answer is obtained or s6-dnsq runs out of server addresses. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-1</tt>&nbsp;: send debug information to stdout. </li>
+ <li> <tt>-2</tt>&nbsp;: send debug information to stderr. This is the default. Note that
+those options only apply to debug output, not to the regular packet dump, which
+is always printed to stdout. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+ <li> <tt>-D</tt>&nbsp;<em>level</em>&nbsp;: produce debug output during
+resolution. If <em>level</em> is:
+ <ul>
+ <li> 0: no debug output is produced </li>
+ <li> 1: information is printed when s6-dnsq receives a DNS packet from a server </li>
+ <li> 2: information is printed before and after s6-dnsq sends a DNS packet to a server </li>
+ <li> 3: both 1. and 2. apply </li>
+ </ul>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> TXT records are printed in a quoted form similar to
+<a href="http://skarnet.org/software/s6-portable-utils/s6-quote.html">s6-quote</a>'s
+output. </li>
+ <li> If s6-dnsq finds a record it cannot print, such as an unknown RR type,
+it dumps its contents in the same quoted form. </li>
+ <li> The normal output format should be stable, so you can write programs that
+automatically parse it. However, the debug output format is undocumented and subject
+to change. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnsqr.html b/doc/s6-dnsqr.html
new file mode 100644
index 0000000..ce308bd
--- /dev/null
+++ b/doc/s6-dnsqr.html
@@ -0,0 +1,80 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsqr program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsqr program" />
+ <meta name="Keywords" content="s6-dns client s6-dnsqr dnsqr analysis debug" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsqr program </h1>
+
+<p>
+ s6-dnsqr is an analysis and debug tool. It performs a DNS resolution,
+then prints the contents of the answer packet, and optionally debug
+information during the resolution.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsqr [ -1 | -2 ] [ -t <em>timeout</em> ] [ -D <em>level</em> ] <em>qtype</em> <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnsqr makes a recursive DNS query of type <em>qtype</em> for the name <em>domain</em>.
+It prints the answer packet, in human-readable form, to its standard output,
+then exits 0. </li>
+ <li> It does not qualify <em>domain</em>. </li>
+ <li> If the resolution fails for some reason, s6-dnsqr exits 2. </li>
+ <li> By default, s6-dnsqr looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-1</tt>&nbsp;: send debug information to stdout. </li>
+ <li> <tt>-2</tt>&nbsp;: send debug information to stderr. This is the default. Not that
+those options only apply to debug output, not to the regular packet dump, which
+is always printed to stdout. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+ <li> <tt>-D</tt>&nbsp;<em>level</em>&nbsp;: produce debug output during
+resolution. If <em>level</em> is:
+ <ul>
+ <li> 0: no debug output is produced </li>
+ <li> 1: information is printed when s6-dnsqr receives a DNS packet from a cache </li>
+ <li> 2: information is printed before and after s6-dnsqr sends a DNS packet to a cache </li>
+ <li> 3: both 1. and 2. apply </li>
+ </ul>
+
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> TXT records are printed in a quoted form similar to
+<a href="http://skarnet.org/software/s6-portable-utils/s6-quote.html">s6-quote</a>'s
+output. </li>
+ <li> If s6-dnsqr finds a record it cannot print, such as an unknown RR type,
+it dumps its content in the same quoted form. </li>
+ <li> The normal output format should be stable, so you can write programs that
+automatically parse it. However, the debug output format is undocumented and subject
+to change. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnsqualify.html b/doc/s6-dnsqualify.html
new file mode 100644
index 0000000..f515be7
--- /dev/null
+++ b/doc/s6-dnsqualify.html
@@ -0,0 +1,41 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnsqualify program</title>
+ <meta name="Description" content="s6-dns: the s6-dnsqualify program" />
+ <meta name="Keywords" content="s6-dns s6-dnsqualify domain name qualification fqdn resolv.conf" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnsqualify program </h1>
+
+<p>
+ s6-dnsqualify qualifies a domain name.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnsqualify <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnsqualify qualifies <em>domain</em> according to the "domain" and
+"search" rules in <tt>/etc/resolv.conf</tt>. It prints the result on
+stdout, one FQDN per line. </li>
+ <li> If the DNSQUALIFY
+environment variable is set and contains a list of suffixes separated by spaces,
+tabs, newlines or carriage returns, then this list is used instead of the
+<tt>/etc/resolv.conf</tt> rules. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnssoa.html b/doc/s6-dnssoa.html
new file mode 100644
index 0000000..6f4a8f2
--- /dev/null
+++ b/doc/s6-dnssoa.html
@@ -0,0 +1,65 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnssoa program</title>
+ <meta name="Description" content="s6-dns: the s6-dnssoa program" />
+ <meta name="Keywords" content="s6-dns client s6-dnssoa domain soa start of authority rr field" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnssoa program </h1>
+
+<p>
+ s6-dnssoa finds the SOA information associated to a domain.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnssoa [ -q ] [ -r ] [ -t <em>timeout</em> ] <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnsmx makes an SOA query for the name <em>domain</em>. It
+waits for the result and prints the obtained information line by line,
+then exits 0. It prints the mname, the rname, the serial number, and
+the refresh, retry, expiration and minimum times, in that order,
+separated by spaces. </li>
+ <li> If the domain exists but no relevant field has been found, it exits 1. </li>
+ <li> If the DNS answered but no answer is available, it prints a relevant
+error message and exits 2. </li>
+ <li> By default, s6-dnssoa looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-q</tt>&nbsp;: qualify. Qualifies <em>domain</em> before resolution,
+according to suffixes found in <tt>/etc/resolv.conf</tt>. If the DNSQUALIFY
+environment variable is set and contains a list of suffixes separated by spaces,
+tabs, newlines or carriage returns, then this list is used instead. By
+default, no qualification is used: if <em>domain</em> is not a FQDN, a dot
+is just appended to it. </li>
+ <li> <tt>-r</tt>&nbsp;: random. By default, the program does not sort the
+result, but prints them in the order received from the DNS. With this
+option, it performs a random permutation on the results before printing
+them. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnssrv.html b/doc/s6-dnssrv.html
new file mode 100644
index 0000000..2a88408
--- /dev/null
+++ b/doc/s6-dnssrv.html
@@ -0,0 +1,65 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnssrv program</title>
+ <meta name="Description" content="s6-dns: the s6-dnssrv program" />
+ <meta name="Keywords" content="s6-dns client s6-dnssrv domain name srv rr field service" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnssrv program </h1>
+
+<p>
+ s6-dnssrv finds the SRV information associated to a
+service name, protocol name and domain name.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnssrv [ -q ] [ -r ] [ -t <em>timeout</em> ] <em>service</em> <em>proto</em> <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnssrv makes an SRV query for _<em>service</em>._<em>proto</em>.<em>domain</em>.
+It waits for the result and prints the obtained information line by line,
+then exits 0. It prints the priority, the weight, the port number and the
+target, in that order, separated by spaces. </li>
+ <li> If the domain exists but no relevant field has been found, it exits 1. </li>
+ <li> If the DNS answered but no answer is available, it prints a relevant
+error message and exits 2. </li>
+ <li> By default, s6-dnssrv looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-q</tt>&nbsp;: qualify. Qualifies <em>domain</em> before resolution,
+according to suffixes found in <tt>/etc/resolv.conf</tt>. If the DNSQUALIFY
+environment variable is set and contains a list of suffixes separated by spaces,
+tabs, newlines or carriage returns, then this list is used instead. By
+default, no qualification is used: if <em>domain</em> is not a FQDN, a dot
+is just appended to it. </li>
+ <li> <tt>-r</tt>&nbsp;: random. By default, the program does not sort the
+result, but prints them in the order received from the DNS. With this
+option, it performs a random permutation on the results before printing
+them. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-dnstxt.html b/doc/s6-dnstxt.html
new file mode 100644
index 0000000..fa84174
--- /dev/null
+++ b/doc/s6-dnstxt.html
@@ -0,0 +1,77 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-dnstxt program</title>
+ <meta name="Description" content="s6-dns: the s6-dnstxt program" />
+ <meta name="Keywords" content="s6-dns client s6-dnstxt dnstxt domain name txt text rr field" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-dnstxt program </h1>
+
+<p>
+ s6-dnstxt finds the TXT information associated to a domain name.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-dnstxt [ -q ] [ -r ] [ -t <em>timeout</em> ] <em>domain</em>
+</pre>
+
+<ul>
+ <li> s6-dnstxt makes a TXT query for the name <em>domain</em>. It
+waits for the result and prints the obtained strings, one by line,
+in a quoted form similar to
+<a href="http://skarnet.org/software/s6-portable-utils/s6-quote.html">s6-quote</a>'s
+output. You can pipe s6-dnstxt's output through
+<a href="http://skarnet.org/software/s6-portable-utils/s6-unquote-filter.html">s6-unquote-filter</a>
+to get unquoted TXT fields. </li>
+ <li> If the domain exists but no relevant field has been found, it exits 1. </li>
+ <li> If the DNS answered but no answer is available, it prints a relevant
+error message and exits 2. </li>
+ <li> By default, s6-dnstxt looks for DNS cache addresses in the
+<tt>/etc/resolv.conf</tt> file. If the DNSCACHEIP environment variable is set
+and contains a list of IP (v4 or v6) addresses, separated by commas,
+semicolons, spaces, tabs, newlines or carriage returns, then this list
+is used instead. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-q</tt>&nbsp;: qualify. Qualifies <em>domain</em> before resolution,
+according to suffixes found in <tt>/etc/resolv.conf</tt>. If the DNSQUALIFY
+environment variable is set and contains a list of suffixes separated by spaces,
+tabs, newlines or carriage returns, then this list is used instead. By
+default, no qualification is used: if <em>domain</em> is not a FQDN, a dot
+is just appended to it. </li>
+ <li> <tt>-r</tt>&nbsp;: random. By default, the program does not sort the
+result, but prints them in the order received from the DNS. With this
+option, it performs a random permutation on the results before printing
+them. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: if the resolution takes more
+than <em>timeout</em> milliseconds, then it exits 99 right away with an error
+message. By default, <em>timeout</em> is 0, which means no timeout. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<p>
+ There can be more than one character-string in a TXT RR, and there can be
+more than one TXT RR per domain. DNS clients usually concatenate all this
+and only give one string. s6-dnstxt concatenates all the character-strings
+in one TXT record, but separates different TXT records, printing each one
+on a separate line.
+</p>
+
+</body>
+</html>
diff --git a/doc/s6-randomip.html b/doc/s6-randomip.html
new file mode 100644
index 0000000..0ad3b46
--- /dev/null
+++ b/doc/s6-randomip.html
@@ -0,0 +1,57 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the s6-randomip program</title>
+ <meta name="Description" content="s6-dns: the s6-randomip program" />
+ <meta name="Keywords" content="s6-dns s6-randomip randomip random ip address filter" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The s6-randomip program </h1>
+
+<p>
+ s6-randomip generates random IP addresses and prints them to
+its standard output, one per line.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-randomip [ -4 ] [ -6 ] [ -n <em>max</em> ]
+</pre>
+
+<ul>
+ <li> s6-randomip makes up IP addresses and prints
+them to its stdout. It keeps running until it receives a signal. </li>
+ <li> IP addresses are not guaranteed to represent anything. They are totally random. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-4</tt>&nbsp;: generate IPv4 addresses. </li>
+ <li> <tt>-6</tt>&nbsp;: generate IPv6 addresses. Both the <tt>-4</tt> and
+<tt>-6</tt> options may be given. If none is given, by default
+s6-randomip will only generate IPv4 addresses. </li>
+ <li> <tt>-n</tt>&nbsp;<em>max</em>&nbsp;: s6-randomip will only generate
+<em>max</em> addresses, then exit 0, instead of running forever. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<p>
+ s6-randomip can be used in conjunction with
+<a href="s6-dnsname-filter">s6-dnsname-filter</a> to stress-test your
+DNS architecture. Please use this responsibly and ethically.
+</p>
+
+</body>
+</html>
diff --git a/doc/skadns/index.html b/doc/skadns/index.html
new file mode 100644
index 0000000..f02f2dd
--- /dev/null
+++ b/doc/skadns/index.html
@@ -0,0 +1,262 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the skadns library interface</title>
+ <meta name="Description" content="s6-dns: the skadns library interface" />
+ <meta name="Keywords" content="s6-dns skadns library asynchronous resolution resolver client C interface" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="../index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>skadns</tt> library interface </h1>
+
+<p>
+ The <tt>skadns</tt> library provides an API for asynchronous DNS
+resolution.
+</p>
+
+<h2> Compiling </h2>
+
+<ul>
+<ul>
+ <li> Make sure the s6-dns headers, as well as the skalibs headers,
+are visible in your header search path. </li>
+ <li> Use <tt>#include &lt;s6-dns/skadns.h&gt;</tt> </li>
+ <li> You might also want to include the <tt>s6-dns/s6dns.h</tt> header for
+the definition of the <tt>s6dns_domain_t</tt> type and the various
+qtype constants </li>
+</ul>
+
+<h2> Linking </h2>
+
+<ul>
+ <li> Make sure the s6 libraries, as well as the skalibs libraries,
+are visible in your library search path. </li>
+ <li> Link against <tt>-lskadns</tt> and <tt>-lskarnet</tt>. </li>
+ <li> If you're using a skadnsd service, also add `cat $SYSDEPS/socket.lib`
+to the end of your linking command line, $SYSDEPS standing for your
+skalibs sysdeps directory. </li>
+</ul>
+
+
+<h2> Programming </h2>
+
+<p>
+ Check the <tt>s6-dns/skadns.h</tt> header for the
+exact function prototypes.
+</p>
+
+<p>
+ Make sure your application is not disturbed by children it doesn't
+know it has. This means paying some attention to the SIGCHLD handler,
+if any, and to the way you perform <tt>waitpid()</tt>s. The best
+practice is to use a
+<a href="http://skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a>
+to handle SIGCHLD (as well as other signals the application needs to trap),
+and to <em>always</em> use <tt>wait_nohang()</tt> to reap children,
+simply ignoring pids you don't know.
+</p>
+
+<p>
+ If your (badly programmed) application has trouble handling unknown
+children, consider using a skadnsd service.
+</p>
+
+<h3> A programming example </h3>
+
+<p>
+ The <tt>src/clients/s6dns_generic_filter_main.c</tt> file in the s6-dns
+package, used in the <tt>s6-dns*-filter</tt> programs, illustrates how to
+use the skadns library.
+</p>
+
+<h3> Starting and ending a session </h3>
+
+<pre>
+skadns_t a = SKADNS_ZERO ;
+tain_t deadline, stamp ;
+
+tain_now(&amp;stamp) ;
+tain_addsec(&amp;deadline, &amp;stamp, 2)
+
+// char const *path = SKADNS_IPCPATH ;
+// skadns_start(&amp;a, path, &amp;deadline, &amp;stamp) ;
+skadns_startf(&amp;a, &amp;deadline, &amp;stamp) ;
+</pre>
+
+<p>
+<tt>skadns_start</tt> starts a session with a skadnsd service, listening
+on <em>path</em>. <br />
+<tt>skadns_startf</tt> starts a session with a skadnsd process as a child
+(which is the simplest usage). <br />
+<tt>a</tt> is a skadns_t structure that must be declared and
+initialized to SKADNS_ZERO.
+<tt>stamp</tt> must be an accurate enough timestamp. <br />
+If the session initialization fails, the function returns 0 and errno is set;
+else the function returns 1.
+</p>
+<p>
+If the absolute time <tt>deadline</tt> is reached and the function
+has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT.
+
+Only local interprocess communications are involved; unless your system is
+heavily overloaded, the function should return near-instantly. One or two
+seconds of delay between <tt>stamp</tt> and <tt>deadline</tt> should be
+enough: if the function takes more than that to return, then there is a
+problem with the underlying processes.
+</p>
+
+<p>
+ You can have more than one session open in parallel, by declaring
+several distinct <tt>skadns_t</tt> structures and calling
+<tt>skadns_startf</tt> (or <tt>skadns_start</tt>) more than once.
+However, this is only useful if you need to perform more than
+SKADNS_MAXCONCURRENCY, i.e. a thousand, concurrent requests. In
+most situations, a single skadns session will be enough.
+</p>
+
+<pre>
+skadns_end(&amp;a) ;
+</pre>
+
+<p>
+<tt>skadns_end</tt> frees all the resources used by the session. The
+<tt>a</tt> structure is then reusable for another session.
+</p>
+
+<h3> Sending a DNS query </h3>
+
+<pre>
+s6dns_domain_t d ;
+uint16 qtype ;
+uint16 id ;
+tain_t limit, deadline, stamp ;
+
+skadns_send(&amp;a, &amp;id, &amp;d, qtype, &amp;limit, &amp;deadline, &amp;stamp) ;
+</pre>
+
+<p>
+<tt>skadns_send</tt> starts the asynchronous resolution of domain <em>d</em>
+with query type <em>qtype</em>. <em>d</em> must be encoded in packet form.
+<em>stamp</em> must be an accurate enough timetamp.
+If the resolution hasn't completed by deadline <em>limit</em>, it will
+automatically fail with a status set to ETIMEDOUT.
+</p>
+
+<p>
+ Like <tt>skadns_startf()</tt>, the <tt>skadns_send()</tt> call
+is synchronous but should not be blocking; however, if it hasn't returned by
+deadline <em>deadline</em>, it then returns 0 with errno set to ETIMEDOUT.
+On failure, the call returns 0 and sets errno. On success, it returns 1 and
+<em>id</em> is set to a 16-bit number identifying the query.
+</p>
+
+<h3> Cancelling a query </h3>
+
+<pre>
+skadns_cancel(&amp;a, id, &amp;deadline, &amp;stamp) ;
+</pre>
+
+<p>
+ <tt>skadns_cancel</tt> cancels the resolution identified by <em>id</em>.
+<em>stamp</em> must be an accurate enough timestamp.
+The call returns 1 on success, or 0 (and sets errno) on failure. It is
+synchronous but should return almost-instantly; if it hasn't returned
+by <em>deadline</em>, it then returns 0 ETIMEDOUT.
+</p>
+
+<p>
+ After a query has been successfully canceled, its id can be discarded.
+</p>
+
+<h3> Asynchronously waiting for answers </h3>
+
+<p>
+<em> (from now on, the functions are listed with their prototypes instead
+of usage examples.) </em>
+</p>
+
+<pre>
+int skadns_fd (skadns_t const *a)
+</pre>
+
+<p>
+ Returns a file descriptor to select on for reading. Do not
+<tt>read()</tt> it though.
+</p>
+
+<pre>
+int skadns_update (skadns_t *a)
+</pre>
+
+<p>
+ Call this function whenever the fd checks readability: it will
+update <em>a</em>'s internal structures with information from the
+<a href="skadnsd.html">skadnsd</a> daemon. It returns -1 if an error
+occurs; in case of success, it returns the number of identifiers for
+which something happened.
+</p>
+
+<p>
+ When <tt>skadns_update</tt> returns,
+<tt>genalloc_s(uint16, &amp;a-&gt;list)</tt> points to an array of
+<tt>genalloc_len(uint16, &amp;a-&gt;list)</tt> 16-bit unsigned
+integers. Those integers are valid ids that can be used with the
+following functions.
+</p>
+
+<pre>
+char const *skadns_packet (skadns_t *a, uint16 id)
+int skadns_packetlen (skadns_t *a, uint16 id)
+</pre>
+
+<p>
+ <tt>skadns_packet()</tt> returns a pointer to the DNS packet
+containing the answer to the query identified by <em>id</em>,
+and <tt>skadns_packetlen()</tt> returns its length. If an
+error has occurred, <tt>skadns_packet()</tt> returns NULL and
+<tt>skadns_packetlen()</tt> returns -1; either call sets errno
+to a value identifying the error. Some errno values have a
+special meaning:
+</p>
+
+<ul>
+ <li> EAGAIN: the result has not arrived yet. </li>
+ <li> ECANCELED: the query has been canceled. </li>
+ <li> EINVAL: <em>id</em> does not identify a valid query. Note
+that <strong>if</strong> you get EINVAL, <strong>then</strong>
+<em>id</em> is invalid, but the reverse is not true:
+using invalid ids is a programming error and may result in a crash. </li>
+</ul>
+
+<pre>
+int skadns_release (skadns_t *a, uint16 id)
+</pre>
+
+<p>
+ <tt>skadns_release()</tt> frees the cell holding the result of
+query <em>id</em>. It returns 1 on success, then <em>id</em> can
+be discarded - further calls to <tt>skadns_send()</tt> may reuse
+the same number.
+</p>
+
+<p>
+ <tt>skadns_release()</tt> may fail, and return 0. This signals a
+programming error and shouldn't be relied on - however, if it happens,
+errno can help you identify the problem:
+</p>
+
+<ul>
+ <li> EBUSY: the query is still pending, cancelled or not. </li>
+ <li> EINVAL: <em>id</em> is invalid.
+</ul>
+
+</body>
+</html>
diff --git a/doc/skadns/skadnsd.html b/doc/skadns/skadnsd.html
new file mode 100644
index 0000000..e8316b8
--- /dev/null
+++ b/doc/skadns/skadnsd.html
@@ -0,0 +1,120 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: the skadnsd internal program</title>
+ <meta name="Description" content="s6-dns: the skadnsd internal program" />
+ <meta name="Keywords" content="s6-dns skadnsd asynchronous dns daemon" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<a href="index.html">libskadns</a><br />
+<a href="../index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a><p />
+
+<h1> The <tt>skadnsd</tt> program </h1>
+
+<p>
+<tt>skadnsd</tt> is the skadns daemon. It reads a series of
+queries from the client on stdin, resolves them asynchronously,
+and writes
+the answers to the client as soon as it gets them. It exits 0
+when its stdin closes. It exits 111 on any serious error,
+writing the error message to stderr.
+</p>
+
+<p>
+<tt>skadnsd</tt> is a stub resolver. It reads <tt>/etc/resolv.conf</tt>
+at start looking for a "nameserver" line containing
+the address of a DNS cache (aka full resolver). It will exit 111 if it cannot
+find any valid cache address in <tt>/etc/resolv.conf</tt>. If the
+<tt>DNSCACHEIP</tt> environment variable is set, its value overrides
+what <tt>/etc/resolv.conf</tt> says.
+</p>
+
+<h2> Interface </h2>
+
+<p>
+skadnsd does not fork, does not background itself automatically,
+and does not use syslog. It is not meant to be run directly by the
+user: it will be invoked and spawned by the skadns library calls.
+</p>
+
+<p>
+ There are 2 ways to use skadnsd:
+</p>
+<ol>
+<li> (preferred) Use the <tt>skadns_startf()</tt> library call.
+A <tt>skadnsd</tt> child will then be spawned from your
+calling process, and automatically reaped when you call
+<tt>skadns_end()</tt>. It requires care with applications that
+trap SIGCHLD. </li>
+<li> Use the <tt>skadns_start()</tt> library call, together with
+a <em>skadnsd service</em>. </li>
+</ol>
+
+<h3> Running skadnsd as a child process </h3>
+
+<p>
+ This is the simplest and safest way of using <em>skadns</em>. Forget
+about <tt>skadnsd</tt>: just start your library calls with
+<tt>skadns_startf()</tt> and end them with <tt>skadns_end()</tt>.
+Be careful though: if you're using SIGCHLD handlers, make sure they do
+not interfere with the child processes your application has without
+knowing. This is a general Unix programming rule.
+</p>
+
+<h3> Running a skadnsd as a daemon: the skadnsd service </h3>
+
+<p>
+ In this mode, you set up a daemon listening on a Unix domain socket,
+and clients connect to this socket to access the service. The
+advantage of this setup is that it works even with badly written
+clients that have trouble handling a child process; the drawback is
+that it requires support from the system administrator.
+</p>
+
+<p>
+skadnsd has no "standalone" mode: it is designed to work with a Unix
+domain superserver, like
+<a href="http://skarnet.org/software/s6-networking/s6-ipcserver.html">s6-ipcserver</a>.
+skadnsd follows the <a href="http://cr.yp.to/proto/ucspi.txt">UCSPI"</a>
+interface, it can be directly executed from the superserver.
+</p>
+
+<p>
+You should run skadnsd (and its Unix superserver) under a specific user
+and group, for elementary security reasons; and you should run its
+dedicated logger as another specific user. Do NOT run skadnsd as root;
+check your super-server documentation to find how
+to run it under a specific account.
+</p>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> Users should never invoke <tt>skadnsd</tt> directly. It's an
+internal program designed to be spawned by the skadns library. </li>
+ <li> If a poorly designed client sends a lot of queries and never reads the
+answers, those will indefinitely queue up in the daemon, eating up
+memory. You should run your process (or your Unix superserver, if you're
+using a skadnsd service) under a program like
+<a href="http://skarnet.org/software/s6/s6-softlimit.html">s6-softlimit</a> to
+set a memory limit to every skadnsd instance. If skadnsd runs out of
+allowed memory, it will simply die. </li>
+ <li> If you're using a skadnsd service: you should configure your
+Unix superserver to set a maximum
+number of concurrent skadnsd instances; most
+Unix superserver implementations provide the
+<tt>-c</tt> option for this. The s6-ipcserver program also
+allows you to specify a maximum number of concurrent daemon
+instances per client. </li>
+<li> The only way to ensure absolute reliability of the skadnsd process is
+to run it as a child, not as a daemon: use <tt>skadnsd_startf()</tt> when
+possible. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/upgrade.html b/doc/upgrade.html
new file mode 100644
index 0000000..c75d08e
--- /dev/null
+++ b/doc/upgrade.html
@@ -0,0 +1,32 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-dns: how to upgrade</title>
+ <meta name="Description" content="s6-dns: how to upgrade" />
+ <meta name="Keywords" content="s6-dns installation upgrade" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-dns</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> What has changed in s6-dns </h1>
+
+<h2> in 2.0.0.0 </h2>
+
+<ul>
+ <li> The build system has completely changed. It is now a standard
+<tt>./configure &amp;&amp; make &amp;&amp; sudo make install</tt>
+build system. See the enclosed INSTALL file for details. </li>
+ <li> slashpackage is not activated by default. </li>
+ <li> shared libraries are not used by default. </li>
+ <li> skalibs dependency bumped to 2.0.0.0. </li>
+</ul>
+
+</body>
+</html>
diff --git a/package/deps-build b/package/deps-build
new file mode 100644
index 0000000..05d5af4
--- /dev/null
+++ b/package/deps-build
@@ -0,0 +1 @@
+/package/prog/skalibs
diff --git a/package/deps.mak b/package/deps.mak
new file mode 100644
index 0000000..e298f1b
--- /dev/null
+++ b/package/deps.mak
@@ -0,0 +1,150 @@
+#
+# This file has been generated by tools/gen-deps.sh
+#
+
+src/include/s6-dns/s6dns-analyze.h: src/include/s6-dns/s6dns-message.h
+src/include/s6-dns/s6dns-debug.h: src/include/s6-dns/s6dns-engine.h
+src/include/s6-dns/s6dns-engine.h: src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-ip46.h
+src/include/s6-dns/s6dns-fmt.h: src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-message.h
+src/include/s6-dns/s6dns-ip46.h: src/include/s6-dns/s6dns-constants.h
+src/include/s6-dns/s6dns-message.h: src/include/s6-dns/s6dns-domain.h
+src/include/s6-dns/s6dns.h: src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-ip46.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-rci.h src/include/s6-dns/s6dns-resolve.h
+src/include/s6-dns/skadns.h: src/include/s6-dns/config.h src/include/s6-dns/s6dns-domain.h
+src/clients/s6dns-generic-filter.h: src/include/s6-dns/s6dns-domain.h
+src/clients/s6-dnsip4-filter.o src/clients/s6-dnsip4-filter.lo: src/clients/s6-dnsip4-filter.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h src/clients/s6dns-generic-filter.h
+src/clients/s6-dnsip4.o src/clients/s6-dnsip4.lo: src/clients/s6-dnsip4.c src/include/s6-dns/s6dns.h
+src/clients/s6-dnsip6-filter.o src/clients/s6-dnsip6-filter.lo: src/clients/s6-dnsip6-filter.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h src/clients/s6dns-generic-filter.h
+src/clients/s6-dnsip6.o src/clients/s6-dnsip6.lo: src/clients/s6-dnsip6.c src/include/s6-dns/s6dns.h
+src/clients/s6-dnsmx.o src/clients/s6-dnsmx.lo: src/clients/s6-dnsmx.c src/include/s6-dns/s6dns.h
+src/clients/s6-dnsname-filter.o src/clients/s6-dnsname-filter.lo: src/clients/s6-dnsname-filter.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-message.h src/clients/s6dns-generic-filter.h
+src/clients/s6-dnsname.o src/clients/s6-dnsname.lo: src/clients/s6-dnsname.c
+src/clients/s6-dnsns.o src/clients/s6-dnsns.lo: src/clients/s6-dnsns.c src/include/s6-dns/s6dns.h
+src/clients/s6-dnsq.o src/clients/s6-dnsq.lo: src/clients/s6-dnsq.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-debug.h src/include/s6-dns/s6dns.h
+src/clients/s6-dnsqr.o src/clients/s6-dnsqr.lo: src/clients/s6-dnsqr.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-debug.h src/include/s6-dns/s6dns.h
+src/clients/s6-dnsqualify.o src/clients/s6-dnsqualify.lo: src/clients/s6-dnsqualify.c src/include/s6-dns/s6dns.h
+src/clients/s6-dnssoa.o src/clients/s6-dnssoa.lo: src/clients/s6-dnssoa.c src/include/s6-dns/s6dns.h
+src/clients/s6-dnssrv.o src/clients/s6-dnssrv.lo: src/clients/s6-dnssrv.c src/include/s6-dns/s6dns.h
+src/clients/s6-dnstxt.o src/clients/s6-dnstxt.lo: src/clients/s6-dnstxt.c src/include/s6-dns/s6dns.h
+src/clients/s6-randomip.o src/clients/s6-randomip.lo: src/clients/s6-randomip.c
+src/clients/s6dns_generic_filter_main.o src/clients/s6dns_generic_filter_main.lo: src/clients/s6dns_generic_filter_main.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/skadns.h src/clients/s6dns-generic-filter.h
+src/clients/s6dns_namescanner.o src/clients/s6dns_namescanner.lo: src/clients/s6dns_namescanner.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-generic-filter.h
+src/libs6dns/s6dns_analyze_packet.o src/libs6dns/s6dns_analyze_packet.lo: src/libs6dns/s6dns_analyze_packet.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_qtype_parse.o src/libs6dns/s6dns_analyze_qtype_parse.lo: src/libs6dns/s6dns_analyze_qtype_parse.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-constants.h
+src/libs6dns/s6dns_analyze_record.o src/libs6dns/s6dns_analyze_record.lo: src/libs6dns/s6dns_analyze_record.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_a.o src/libs6dns/s6dns_analyze_record_a.lo: src/libs6dns/s6dns_analyze_record_a.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_aaaa.o src/libs6dns/s6dns_analyze_record_aaaa.lo: src/libs6dns/s6dns_analyze_record_aaaa.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_domain.o src/libs6dns/s6dns_analyze_record_domain.lo: src/libs6dns/s6dns_analyze_record_domain.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_hinfo.o src/libs6dns/s6dns_analyze_record_hinfo.lo: src/libs6dns/s6dns_analyze_record_hinfo.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_mx.o src/libs6dns/s6dns_analyze_record_mx.lo: src/libs6dns/s6dns_analyze_record_mx.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_soa.o src/libs6dns/s6dns_analyze_record_soa.lo: src/libs6dns/s6dns_analyze_record_soa.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_srv.o src/libs6dns/s6dns_analyze_record_srv.lo: src/libs6dns/s6dns_analyze_record_srv.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_strings.o src/libs6dns/s6dns_analyze_record_strings.lo: src/libs6dns/s6dns_analyze_record_strings.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_record_unknown.o src/libs6dns/s6dns_analyze_record_unknown.lo: src/libs6dns/s6dns_analyze_record_unknown.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_analyze_rtypetable.o src/libs6dns/s6dns_analyze_rtypetable.lo: src/libs6dns/s6dns_analyze_rtypetable.c src/include/s6-dns/s6dns-analyze.h
+src/libs6dns/s6dns_constants_error.o src/libs6dns/s6dns_constants_error.lo: src/libs6dns/s6dns_constants_error.c src/include/s6-dns/s6dns-constants.h
+src/libs6dns/s6dns_constants_error_str.o src/libs6dns/s6dns_constants_error_str.lo: src/libs6dns/s6dns_constants_error_str.c src/include/s6-dns/s6dns-constants.h
+src/libs6dns/s6dns_debug_dumpdt_post_recv.o src/libs6dns/s6dns_debug_dumpdt_post_recv.lo: src/libs6dns/s6dns_debug_dumpdt_post_recv.c src/include/s6-dns/s6dns-debug.h src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_debug_dumpdt_post_send.o src/libs6dns/s6dns_debug_dumpdt_post_send.lo: src/libs6dns/s6dns_debug_dumpdt_post_send.c src/include/s6-dns/s6dns-debug.h src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_debug_dumpdt_pre_send.o src/libs6dns/s6dns_debug_dumpdt_pre_send.lo: src/libs6dns/s6dns_debug_dumpdt_pre_send.c src/include/s6-dns/s6dns-analyze.h src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-debug.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-ip46.h
+src/libs6dns/s6dns_debug_dumpdt_stderr.o src/libs6dns/s6dns_debug_dumpdt_stderr.lo: src/libs6dns/s6dns_debug_dumpdt_stderr.c src/include/s6-dns/s6dns-debug.h src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_debug_dumpdt_stdout.o src/libs6dns/s6dns_debug_dumpdt_stdout.lo: src/libs6dns/s6dns_debug_dumpdt_stdout.c src/include/s6-dns/s6dns-debug.h src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_debughook_zero.o src/libs6dns/s6dns_debughook_zero.lo: src/libs6dns/s6dns_debughook_zero.c src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_domain_arpafromip4.o src/libs6dns/s6dns_domain_arpafromip4.lo: src/libs6dns/s6dns_domain_arpafromip4.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_arpafromip6.o src/libs6dns/s6dns_domain_arpafromip6.lo: src/libs6dns/s6dns_domain_arpafromip6.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_decode.o src/libs6dns/s6dns_domain_decode.lo: src/libs6dns/s6dns_domain_decode.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_encode.o src/libs6dns/s6dns_domain_encode.lo: src/libs6dns/s6dns_domain_encode.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_encodelist.o src/libs6dns/s6dns_domain_encodelist.lo: src/libs6dns/s6dns_domain_encodelist.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_fromstring.o src/libs6dns/s6dns_domain_fromstring.lo: src/libs6dns/s6dns_domain_fromstring.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_fromstring_noqualify_encode.o src/libs6dns/s6dns_domain_fromstring_noqualify_encode.lo: src/libs6dns/s6dns_domain_fromstring_noqualify_encode.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_fromstring_qualify_encode.o src/libs6dns/s6dns_domain_fromstring_qualify_encode.lo: src/libs6dns/s6dns_domain_fromstring_qualify_encode.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_noqualify.o src/libs6dns/s6dns_domain_noqualify.lo: src/libs6dns/s6dns_domain_noqualify.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_qualify.o src/libs6dns/s6dns_domain_qualify.lo: src/libs6dns/s6dns_domain_qualify.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_domain_tostring.o src/libs6dns/s6dns_domain_tostring.lo: src/libs6dns/s6dns_domain_tostring.c src/include/s6-dns/s6dns-domain.h
+src/libs6dns/s6dns_engine.o src/libs6dns/s6dns_engine.lo: src/libs6dns/s6dns_engine.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-message-internal.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_engine_free.o src/libs6dns/s6dns_engine_free.lo: src/libs6dns/s6dns_engine_free.c src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_engine_freen.o src/libs6dns/s6dns_engine_freen.lo: src/libs6dns/s6dns_engine_freen.c src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_engine_here.o src/libs6dns/s6dns_engine_here.lo: src/libs6dns/s6dns_engine_here.c src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_engine_nextdeadline.o src/libs6dns/s6dns_engine_nextdeadline.lo: src/libs6dns/s6dns_engine_nextdeadline.c src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_engine_zero.o src/libs6dns/s6dns_engine_zero.lo: src/libs6dns/s6dns_engine_zero.c src/include/s6-dns/s6dns-engine.h
+src/libs6dns/s6dns_fmt_domainlist.o src/libs6dns/s6dns_fmt_domainlist.lo: src/libs6dns/s6dns_fmt_domainlist.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-fmt.h
+src/libs6dns/s6dns_fmt_hinfo.o src/libs6dns/s6dns_fmt_hinfo.lo: src/libs6dns/s6dns_fmt_hinfo.c src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_fmt_mx.o src/libs6dns/s6dns_fmt_mx.lo: src/libs6dns/s6dns_fmt_mx.c src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_fmt_soa.o src/libs6dns/s6dns_fmt_soa.lo: src/libs6dns/s6dns_fmt_soa.c src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_fmt_srv.o src/libs6dns/s6dns_fmt_srv.lo: src/libs6dns/s6dns_fmt_srv.c src/include/s6-dns/s6dns-fmt.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_counts_next.o src/libs6dns/s6dns_message_counts_next.lo: src/libs6dns/s6dns_message_counts_next.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_counts_pack.o src/libs6dns/s6dns_message_counts_pack.lo: src/libs6dns/s6dns_message_counts_pack.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_counts_unpack.o src/libs6dns/s6dns_message_counts_unpack.lo: src/libs6dns/s6dns_message_counts_unpack.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_counts_zero.o src/libs6dns/s6dns_message_counts_zero.lo: src/libs6dns/s6dns_message_counts_zero.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_get_domain.o src/libs6dns/s6dns_message_get_domain.lo: src/libs6dns/s6dns_message_get_domain.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-message-internal.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_get_domain_internal.o src/libs6dns/s6dns_message_get_domain_internal.lo: src/libs6dns/s6dns_message_get_domain_internal.c src/libs6dns/s6dns-message-internal.h
+src/libs6dns/s6dns_message_get_hinfo.o src/libs6dns/s6dns_message_get_hinfo.lo: src/libs6dns/s6dns_message_get_hinfo.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_get_mx.o src/libs6dns/s6dns_message_get_mx.lo: src/libs6dns/s6dns_message_get_mx.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_get_soa.o src/libs6dns/s6dns_message_get_soa.lo: src/libs6dns/s6dns_message_get_soa.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_get_srv.o src/libs6dns/s6dns_message_get_srv.lo: src/libs6dns/s6dns_message_get_srv.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_get_string.o src/libs6dns/s6dns_message_get_string.lo: src/libs6dns/s6dns_message_get_string.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-message.h src/libs6dns/s6dns-message-internal.h
+src/libs6dns/s6dns_message_get_string_internal.o src/libs6dns/s6dns_message_get_string_internal.lo: src/libs6dns/s6dns_message_get_string_internal.c src/libs6dns/s6dns-message-internal.h
+src/libs6dns/s6dns_message_get_strings.o src/libs6dns/s6dns_message_get_strings.lo: src/libs6dns/s6dns_message_get_strings.c src/include/s6-dns/s6dns-message.h src/libs6dns/s6dns-message-internal.h
+src/libs6dns/s6dns_message_header_pack.o src/libs6dns/s6dns_message_header_pack.lo: src/libs6dns/s6dns_message_header_pack.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_header_unpack.o src/libs6dns/s6dns_message_header_unpack.lo: src/libs6dns/s6dns_message_header_unpack.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_header_zero.o src/libs6dns/s6dns_message_header_zero.lo: src/libs6dns/s6dns_message_header_zero.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse.o src/libs6dns/s6dns_message_parse.lo: src/libs6dns/s6dns_message_parse.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_answer_a.o src/libs6dns/s6dns_message_parse_answer_a.lo: src/libs6dns/s6dns_message_parse_answer_a.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_answer_aaaa.o src/libs6dns/s6dns_message_parse_answer_aaaa.lo: src/libs6dns/s6dns_message_parse_answer_aaaa.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_answer_domain.o src/libs6dns/s6dns_message_parse_answer_domain.lo: src/libs6dns/s6dns_message_parse_answer_domain.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_answer_hinfo.o src/libs6dns/s6dns_message_parse_answer_hinfo.lo: src/libs6dns/s6dns_message_parse_answer_hinfo.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_answer_mx.o src/libs6dns/s6dns_message_parse_answer_mx.lo: src/libs6dns/s6dns_message_parse_answer_mx.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_answer_soa.o src/libs6dns/s6dns_message_parse_answer_soa.lo: src/libs6dns/s6dns_message_parse_answer_soa.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_answer_srv.o src/libs6dns/s6dns_message_parse_answer_srv.lo: src/libs6dns/s6dns_message_parse_answer_srv.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_answer_strings.o src/libs6dns/s6dns_message_parse_answer_strings.lo: src/libs6dns/s6dns_message_parse_answer_strings.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_getrr.o src/libs6dns/s6dns_message_parse_getrr.lo: src/libs6dns/s6dns_message_parse_getrr.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_init.o src/libs6dns/s6dns_message_parse_init.lo: src/libs6dns/s6dns_message_parse_init.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_next.o src/libs6dns/s6dns_message_parse_next.lo: src/libs6dns/s6dns_message_parse_next.c src/include/s6-dns/s6dns-message.h
+src/libs6dns/s6dns_message_parse_skipqd.o src/libs6dns/s6dns_message_parse_skipqd.lo: src/libs6dns/s6dns_message_parse_skipqd.c src/include/s6-dns/s6dns-message.h src/libs6dns/s6dns-message-internal.h
+src/libs6dns/s6dns_rci_free.o src/libs6dns/s6dns_rci_free.lo: src/libs6dns/s6dns_rci_free.c src/include/s6-dns/s6dns-rci.h
+src/libs6dns/s6dns_rci_here.o src/libs6dns/s6dns_rci_here.lo: src/libs6dns/s6dns_rci_here.c src/include/s6-dns/s6dns-rci.h
+src/libs6dns/s6dns_rci_init.o src/libs6dns/s6dns_rci_init.lo: src/libs6dns/s6dns_rci_init.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-rci.h
+src/libs6dns/s6dns_rci_zero.o src/libs6dns/s6dns_rci_zero.lo: src/libs6dns/s6dns_rci_zero.c src/include/s6-dns/s6dns-rci.h
+src/libs6dns/s6dns_resolve_core.o src/libs6dns/s6dns_resolve_core.lo: src/libs6dns/s6dns_resolve_core.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-ip46.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolve_dpag.o src/libs6dns/s6dns_resolve_dpag.lo: src/libs6dns/s6dns_resolve_dpag.c src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-rci.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolve_mpag.o src/libs6dns/s6dns_resolve_mpag.lo: src/libs6dns/s6dns_resolve_mpag.c src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-rci.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolve_name4.o src/libs6dns/s6dns_resolve_name4.lo: src/libs6dns/s6dns_resolve_name4.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-ip46.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolve_name6.o src/libs6dns/s6dns_resolve_name6.lo: src/libs6dns/s6dns_resolve_name6.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-ip46.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolve_parse.o src/libs6dns/s6dns_resolve_parse.lo: src/libs6dns/s6dns_resolve_parse.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-ip46.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolven_loop.o src/libs6dns/s6dns_resolven_loop.lo: src/libs6dns/s6dns_resolven_loop.c src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolven_parse.o src/libs6dns/s6dns_resolven_parse.lo: src/libs6dns/s6dns_resolven_parse.c src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-ip46.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolvenoq.o src/libs6dns/s6dns_resolvenoq.lo: src/libs6dns/s6dns_resolvenoq.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-ip46.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolvenoq_aaaaa.o src/libs6dns/s6dns_resolvenoq_aaaaa.lo: src/libs6dns/s6dns_resolvenoq_aaaaa.c src/include/s6-dns/s6dns-constants.h src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-ip46.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolveq.o src/libs6dns/s6dns_resolveq.lo: src/libs6dns/s6dns_resolveq.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-rci.h src/include/s6-dns/s6dns-resolve.h
+src/libs6dns/s6dns_resolveq_aaaaa.o src/libs6dns/s6dns_resolveq_aaaaa.lo: src/libs6dns/s6dns_resolveq_aaaaa.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/s6dns-engine.h src/include/s6-dns/s6dns-message.h src/include/s6-dns/s6dns-rci.h src/include/s6-dns/s6dns-resolve.h
+src/skadns/skadns_cancel.o src/skadns/skadns_cancel.lo: src/skadns/skadns_cancel.c src/include/s6-dns/skadns.h
+src/skadns/skadns_end.o src/skadns/skadns_end.lo: src/skadns/skadns_end.c src/include/s6-dns/skadns.h
+src/skadns/skadns_packet.o src/skadns/skadns_packet.lo: src/skadns/skadns_packet.c src/include/s6-dns/skadns.h
+src/skadns/skadns_packetlen.o src/skadns/skadns_packetlen.lo: src/skadns/skadns_packetlen.c src/include/s6-dns/skadns.h
+src/skadns/skadns_release.o src/skadns/skadns_release.lo: src/skadns/skadns_release.c src/include/s6-dns/skadns.h
+src/skadns/skadns_send.o src/skadns/skadns_send.lo: src/skadns/skadns_send.c src/include/s6-dns/s6dns-domain.h src/include/s6-dns/skadns.h
+src/skadns/skadns_start.o src/skadns/skadns_start.lo: src/skadns/skadns_start.c src/include/s6-dns/skadns.h
+src/skadns/skadns_startf.o src/skadns/skadns_startf.lo: src/skadns/skadns_startf.c src/include/s6-dns/skadns.h
+src/skadns/skadns_update.o src/skadns/skadns_update.lo: src/skadns/skadns_update.c
+src/skadns/skadns_zero.o src/skadns/skadns_zero.lo: src/skadns/skadns_zero.c src/include/s6-dns/skadns.h
+src/skadns/skadnsd.o src/skadns/skadnsd.lo: src/skadns/skadnsd.c src/include/s6-dns/s6dns.h src/include/s6-dns/skadns.h
+
+libs6dnsgenericfilter.a: src/clients/s6dns_generic_filter_main.o src/clients/s6dns_namescanner.o
+s6-dnsip4: src/clients/s6-dnsip4.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsip4-filter: src/clients/s6-dnsip4-filter.o libs6dnsgenericfilter.a -lskadns -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsip6: src/clients/s6-dnsip6.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsip6-filter: src/clients/s6-dnsip6-filter.o libs6dnsgenericfilter.a -lskadns -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsmx: src/clients/s6-dnsmx.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsname: src/clients/s6-dnsname.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsname-filter: src/clients/s6-dnsname-filter.o libs6dnsgenericfilter.a -lskadns -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsns: src/clients/s6-dnsns.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsq: src/clients/s6-dnsq.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsqr: src/clients/s6-dnsqr.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnsqualify: src/clients/s6-dnsqualify.o -ls6dns -lskarnet
+s6-dnssoa: src/clients/s6-dnssoa.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnssrv: src/clients/s6-dnssrv.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-dnstxt: src/clients/s6-dnstxt.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+s6-randomip: src/clients/s6-randomip.o -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+libs6dns.a: src/libs6dns/s6dns_constants_error.o src/libs6dns/s6dns_constants_error_str.o src/libs6dns/s6dns_debughook_zero.o src/libs6dns/s6dns_domain_arpafromip4.o src/libs6dns/s6dns_domain_arpafromip6.o src/libs6dns/s6dns_domain_decode.o src/libs6dns/s6dns_domain_encode.o src/libs6dns/s6dns_domain_encodelist.o src/libs6dns/s6dns_domain_fromstring.o src/libs6dns/s6dns_domain_fromstring_noqualify_encode.o src/libs6dns/s6dns_domain_fromstring_qualify_encode.o src/libs6dns/s6dns_domain_noqualify.o src/libs6dns/s6dns_domain_qualify.o src/libs6dns/s6dns_domain_tostring.o src/libs6dns/s6dns_engine.o src/libs6dns/s6dns_engine_free.o src/libs6dns/s6dns_engine_freen.o src/libs6dns/s6dns_engine_here.o src/libs6dns/s6dns_engine_nextdeadline.o src/libs6dns/s6dns_engine_zero.o src/libs6dns/s6dns_fmt_domainlist.o src/libs6dns/s6dns_fmt_hinfo.o src/libs6dns/s6dns_fmt_mx.o src/libs6dns/s6dns_fmt_soa.o src/libs6dns/s6dns_fmt_srv.o src/libs6dns/s6dns_message_counts_next.o src/libs6dns/s6dns_message_counts_pack.o src/libs6dns/s6dns_message_counts_unpack.o src/libs6dns/s6dns_message_counts_zero.o src/libs6dns/s6dns_message_get_domain.o src/libs6dns/s6dns_message_get_domain_internal.o src/libs6dns/s6dns_message_get_hinfo.o src/libs6dns/s6dns_message_get_string.o src/libs6dns/s6dns_message_get_string_internal.o src/libs6dns/s6dns_message_get_strings.o src/libs6dns/s6dns_message_get_mx.o src/libs6dns/s6dns_message_get_soa.o src/libs6dns/s6dns_message_get_srv.o src/libs6dns/s6dns_message_header_pack.o src/libs6dns/s6dns_message_header_unpack.o src/libs6dns/s6dns_message_header_zero.o src/libs6dns/s6dns_message_parse_answer_aaaa.o src/libs6dns/s6dns_message_parse_answer_a.o src/libs6dns/s6dns_message_parse_answer_domain.o src/libs6dns/s6dns_message_parse_answer_hinfo.o src/libs6dns/s6dns_message_parse_answer_mx.o src/libs6dns/s6dns_message_parse_answer_soa.o src/libs6dns/s6dns_message_parse_answer_srv.o src/libs6dns/s6dns_message_parse_answer_strings.o src/libs6dns/s6dns_message_parse.o src/libs6dns/s6dns_message_parse_getrr.o src/libs6dns/s6dns_message_parse_init.o src/libs6dns/s6dns_message_parse_next.o src/libs6dns/s6dns_message_parse_skipqd.o src/libs6dns/s6dns_rci_free.o src/libs6dns/s6dns_rci_here.o src/libs6dns/s6dns_rci_init.o src/libs6dns/s6dns_rci_zero.o src/libs6dns/s6dns_resolve_core.o src/libs6dns/s6dns_resolve_parse.o src/libs6dns/s6dns_resolven_loop.o src/libs6dns/s6dns_resolven_parse.o src/libs6dns/s6dns_resolve_dpag.o src/libs6dns/s6dns_resolve_mpag.o src/libs6dns/s6dns_resolve_name4.o src/libs6dns/s6dns_resolve_name6.o src/libs6dns/s6dns_resolvenoq.o src/libs6dns/s6dns_resolveq.o src/libs6dns/s6dns_resolvenoq_aaaaa.o src/libs6dns/s6dns_resolveq_aaaaa.o src/libs6dns/s6dns_analyze_packet.o src/libs6dns/s6dns_analyze_qtype_parse.o src/libs6dns/s6dns_analyze_record.o src/libs6dns/s6dns_analyze_record_a.o src/libs6dns/s6dns_analyze_record_aaaa.o src/libs6dns/s6dns_analyze_record_hinfo.o src/libs6dns/s6dns_analyze_record_mx.o src/libs6dns/s6dns_analyze_record_soa.o src/libs6dns/s6dns_analyze_record_srv.o src/libs6dns/s6dns_analyze_record_domain.o src/libs6dns/s6dns_analyze_record_strings.o src/libs6dns/s6dns_analyze_record_unknown.o src/libs6dns/s6dns_analyze_rtypetable.o src/libs6dns/s6dns_debug_dumpdt_stdout.o src/libs6dns/s6dns_debug_dumpdt_stderr.o src/libs6dns/s6dns_debug_dumpdt_post_recv.o src/libs6dns/s6dns_debug_dumpdt_pre_send.o src/libs6dns/s6dns_debug_dumpdt_post_send.o
+libs6dns.so: src/libs6dns/s6dns_constants_error.lo src/libs6dns/s6dns_constants_error_str.lo src/libs6dns/s6dns_debughook_zero.lo src/libs6dns/s6dns_domain_arpafromip4.lo src/libs6dns/s6dns_domain_arpafromip6.lo src/libs6dns/s6dns_domain_decode.lo src/libs6dns/s6dns_domain_encode.lo src/libs6dns/s6dns_domain_encodelist.lo src/libs6dns/s6dns_domain_fromstring.lo src/libs6dns/s6dns_domain_fromstring_noqualify_encode.lo src/libs6dns/s6dns_domain_fromstring_qualify_encode.lo src/libs6dns/s6dns_domain_noqualify.lo src/libs6dns/s6dns_domain_qualify.lo src/libs6dns/s6dns_domain_tostring.lo src/libs6dns/s6dns_engine.lo src/libs6dns/s6dns_engine_free.lo src/libs6dns/s6dns_engine_freen.lo src/libs6dns/s6dns_engine_here.lo src/libs6dns/s6dns_engine_nextdeadline.lo src/libs6dns/s6dns_engine_zero.lo src/libs6dns/s6dns_fmt_domainlist.lo src/libs6dns/s6dns_fmt_hinfo.lo src/libs6dns/s6dns_fmt_mx.lo src/libs6dns/s6dns_fmt_soa.lo src/libs6dns/s6dns_fmt_srv.lo src/libs6dns/s6dns_message_counts_next.lo src/libs6dns/s6dns_message_counts_pack.lo src/libs6dns/s6dns_message_counts_unpack.lo src/libs6dns/s6dns_message_counts_zero.lo src/libs6dns/s6dns_message_get_domain.lo src/libs6dns/s6dns_message_get_domain_internal.lo src/libs6dns/s6dns_message_get_hinfo.lo src/libs6dns/s6dns_message_get_string.lo src/libs6dns/s6dns_message_get_string_internal.lo src/libs6dns/s6dns_message_get_strings.lo src/libs6dns/s6dns_message_get_mx.lo src/libs6dns/s6dns_message_get_soa.lo src/libs6dns/s6dns_message_get_srv.lo src/libs6dns/s6dns_message_header_pack.lo src/libs6dns/s6dns_message_header_unpack.lo src/libs6dns/s6dns_message_header_zero.lo src/libs6dns/s6dns_message_parse_answer_aaaa.lo src/libs6dns/s6dns_message_parse_answer_a.lo src/libs6dns/s6dns_message_parse_answer_domain.lo src/libs6dns/s6dns_message_parse_answer_hinfo.lo src/libs6dns/s6dns_message_parse_answer_mx.lo src/libs6dns/s6dns_message_parse_answer_soa.lo src/libs6dns/s6dns_message_parse_answer_srv.lo src/libs6dns/s6dns_message_parse_answer_strings.lo src/libs6dns/s6dns_message_parse.lo src/libs6dns/s6dns_message_parse_getrr.lo src/libs6dns/s6dns_message_parse_init.lo src/libs6dns/s6dns_message_parse_next.lo src/libs6dns/s6dns_message_parse_skipqd.lo src/libs6dns/s6dns_rci_free.lo src/libs6dns/s6dns_rci_here.lo src/libs6dns/s6dns_rci_init.lo src/libs6dns/s6dns_rci_zero.lo src/libs6dns/s6dns_resolve_core.lo src/libs6dns/s6dns_resolve_parse.lo src/libs6dns/s6dns_resolven_loop.lo src/libs6dns/s6dns_resolven_parse.lo src/libs6dns/s6dns_resolve_dpag.lo src/libs6dns/s6dns_resolve_mpag.lo src/libs6dns/s6dns_resolve_name4.lo src/libs6dns/s6dns_resolve_name6.lo src/libs6dns/s6dns_resolvenoq.lo src/libs6dns/s6dns_resolveq.lo src/libs6dns/s6dns_resolvenoq_aaaaa.lo src/libs6dns/s6dns_resolveq_aaaaa.lo src/libs6dns/s6dns_analyze_packet.lo src/libs6dns/s6dns_analyze_qtype_parse.lo src/libs6dns/s6dns_analyze_record.lo src/libs6dns/s6dns_analyze_record_a.lo src/libs6dns/s6dns_analyze_record_aaaa.lo src/libs6dns/s6dns_analyze_record_hinfo.lo src/libs6dns/s6dns_analyze_record_mx.lo src/libs6dns/s6dns_analyze_record_soa.lo src/libs6dns/s6dns_analyze_record_srv.lo src/libs6dns/s6dns_analyze_record_domain.lo src/libs6dns/s6dns_analyze_record_strings.lo src/libs6dns/s6dns_analyze_record_unknown.lo src/libs6dns/s6dns_analyze_rtypetable.lo src/libs6dns/s6dns_debug_dumpdt_stdout.lo src/libs6dns/s6dns_debug_dumpdt_stderr.lo src/libs6dns/s6dns_debug_dumpdt_post_recv.lo src/libs6dns/s6dns_debug_dumpdt_pre_send.lo src/libs6dns/s6dns_debug_dumpdt_post_send.lo
+libskadns.a: src/skadns/skadns_cancel.o src/skadns/skadns_end.o src/skadns/skadns_packet.o src/skadns/skadns_packetlen.o src/skadns/skadns_release.o src/skadns/skadns_send.o src/skadns/skadns_start.o src/skadns/skadns_startf.o src/skadns/skadns_update.o src/skadns/skadns_zero.o
+skadnsd: src/skadns/skadnsd.o -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
diff --git a/package/info b/package/info
new file mode 100644
index 0000000..c5b7191
--- /dev/null
+++ b/package/info
@@ -0,0 +1,4 @@
+package=s6-dns
+version=2.0.0.0
+category=web
+package_macro_name=S6_DNS
diff --git a/package/modes b/package/modes
new file mode 100644
index 0000000..c9e951a
--- /dev/null
+++ b/package/modes
@@ -0,0 +1,16 @@
+skadnsd 0755
+s6-randomip 0755
+s6-dnsqualify 0755
+s6-dnsip4 0755
+s6-dnsip6 0755
+s6-dnsmx 0755
+s6-dnsname 0755
+s6-dnsns 0755
+s6-dnssoa 0755
+s6-dnssrv 0755
+s6-dnstxt 0755
+s6-dnsip4-filter 0755
+s6-dnsip6-filter 0755
+s6-dnsname-filter 0755
+s6-dnsq 0755
+s6-dnsqr 0755
diff --git a/package/targets.mak b/package/targets.mak
new file mode 100644
index 0000000..dbc4608
--- /dev/null
+++ b/package/targets.mak
@@ -0,0 +1,27 @@
+BIN_TARGETS = \
+skadnsd \
+s6-randomip \
+s6-dnsqualify \
+s6-dnsip4 \
+s6-dnsip6 \
+s6-dnsmx \
+s6-dnsname \
+s6-dnsns \
+s6-dnssoa \
+s6-dnssrv \
+s6-dnstxt \
+s6-dnsip4-filter \
+s6-dnsip6-filter \
+s6-dnsname-filter \
+s6-dnsq \
+s6-dnsqr
+
+LIBEXEC_TARGETS =
+
+SHARED_LIBS = \
+libs6dns.so \
+libskadns.so
+
+STATIC_LIBS = \
+libs6dns.a \
+libskadns.a
diff --git a/patch-for-solaris b/patch-for-solaris
new file mode 100755
index 0000000..02f2e3c
--- /dev/null
+++ b/patch-for-solaris
@@ -0,0 +1,17 @@
+#!/usr/xpg4/bin/sh
+
+patchit () {
+ echo '#!/usr/xpg4/bin/sh' > $1.tmp
+ tail -n +2 $1 >> $1.tmp
+ mv -f $1.tmp $1
+ chmod 755 $1
+}
+
+patchit ./configure
+patchit ./tools/install.sh
+patchit ./tools/gen-deps.sh
+
+echo 'SHELL := /usr/xpg4/bin/sh' > Makefile.tmp
+echo >> Makefile.tmp
+cat Makefile >> Makefile.tmp
+mv -f Makefile.tmp Makefile
diff --git a/src/clients/deps-exe/s6-dnsip4 b/src/clients/deps-exe/s6-dnsip4
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsip4
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsip4-filter b/src/clients/deps-exe/s6-dnsip4-filter
new file mode 100644
index 0000000..2c61392
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsip4-filter
@@ -0,0 +1,6 @@
+libs6dnsgenericfilter.a
+-lskadns
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsip6 b/src/clients/deps-exe/s6-dnsip6
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsip6
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsip6-filter b/src/clients/deps-exe/s6-dnsip6-filter
new file mode 100644
index 0000000..2c61392
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsip6-filter
@@ -0,0 +1,6 @@
+libs6dnsgenericfilter.a
+-lskadns
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsmx b/src/clients/deps-exe/s6-dnsmx
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsmx
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsname b/src/clients/deps-exe/s6-dnsname
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsname
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsname-filter b/src/clients/deps-exe/s6-dnsname-filter
new file mode 100644
index 0000000..2c61392
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsname-filter
@@ -0,0 +1,6 @@
+libs6dnsgenericfilter.a
+-lskadns
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsns b/src/clients/deps-exe/s6-dnsns
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsns
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsq b/src/clients/deps-exe/s6-dnsq
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsq
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsqr b/src/clients/deps-exe/s6-dnsqr
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsqr
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnsqualify b/src/clients/deps-exe/s6-dnsqualify
new file mode 100644
index 0000000..295d71e
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnsqualify
@@ -0,0 +1,2 @@
+-ls6dns
+-lskarnet
diff --git a/src/clients/deps-exe/s6-dnssoa b/src/clients/deps-exe/s6-dnssoa
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnssoa
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnssrv b/src/clients/deps-exe/s6-dnssrv
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnssrv
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-dnstxt b/src/clients/deps-exe/s6-dnstxt
new file mode 100644
index 0000000..4181fd6
--- /dev/null
+++ b/src/clients/deps-exe/s6-dnstxt
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-exe/s6-randomip b/src/clients/deps-exe/s6-randomip
new file mode 100644
index 0000000..e027835
--- /dev/null
+++ b/src/clients/deps-exe/s6-randomip
@@ -0,0 +1,3 @@
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/clients/deps-lib/s6dnsgenericfilter b/src/clients/deps-lib/s6dnsgenericfilter
new file mode 100644
index 0000000..2d5eee8
--- /dev/null
+++ b/src/clients/deps-lib/s6dnsgenericfilter
@@ -0,0 +1,2 @@
+s6dns_generic_filter_main.o
+s6dns_namescanner.o
diff --git a/src/clients/s6-dnsip4-filter.c b/src/clients/s6-dnsip4-filter.c
new file mode 100644
index 0000000..6d6862e
--- /dev/null
+++ b/src/clients/s6-dnsip4-filter.c
@@ -0,0 +1,52 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+#include "s6dns-generic-filter.h"
+
+#define USAGE "s6-dnsip4-filter [ -l lines ] [ -c concurrency ] [ -t timeout ] [ -f format ] [ -e errorformat ]"
+
+typedef struct s6dns_a1_s s6dns_a1_t, *s6dns_a1_t_ref ;
+struct s6dns_a1_s
+{
+ char ip[4] ;
+ unsigned int got : 1 ;
+} ;
+
+static int s6dns_message_parse_answer_a1 (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_A) && (rr->rdlength == 4))
+ {
+ s6dns_a1_t *data = stuff ;
+ if (data->got) return 1 ;
+ byte_copy(data->ip, 4, packet+pos) ;
+ data->got = 1 ;
+ }
+ (void)packetlen ;
+ return 1 ;
+}
+
+static int ipformatter (stralloc *sa, char const *packet, unsigned int packetlen)
+{
+ s6dns_a1_t data ;
+ s6dns_message_header_t h ;
+ register int r ;
+ data.got = 0 ;
+ r = s6dns_message_parse(&h, packet, packetlen, &s6dns_message_parse_answer_a1, &data) ;
+ if (r <= 0) return r ;
+ if (!data.got) return 1 ;
+ if (!stralloc_readyplus(sa, IP4_FMT)) return -1 ;
+ sa->len += ip4_fmt(sa->s + sa->len, data.ip) ;
+ stralloc_0(sa) ;
+ return 1 ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ PROG = "s6-dnsip4-filter" ;
+ return s6dns_generic_filter_main(argc, argv, envp, S6DNS_T_A, &s6dns_namescanner, &ipformatter, USAGE) ;
+}
diff --git a/src/clients/s6-dnsip4.c b/src/clients/s6-dnsip4.c
new file mode 100644
index 0000000..03c52dd
--- /dev/null
+++ b/src/clients/s6-dnsip4.c
@@ -0,0 +1,62 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/random.h>
+#include <s6-dns/s6dns.h>
+
+#define USAGE "s6-dnsip4 [ -q ] [ -r ] [ -t timeout ] domain"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ stralloc ips = STRALLOC_ZERO ;
+ tain_t deadline ;
+ unsigned int i = 0 ;
+ int flagqualify = 0 ;
+ int flagunsort = 0 ;
+ PROG = "s6-dnsip4" ;
+
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "qrt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'q' : flagqualify = 1 ; break ;
+ case 'r' : flagunsort = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &i)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 1) dieusage() ;
+
+ tain_now_g() ;
+ if (i) tain_from_millisecs(&deadline, i) ; else deadline = tain_infinite_relative ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ register int r = s6dns_resolve_a_g(&ips, argv[0], str_len(argv[0]), flagqualify, &deadline) ;
+ if (r < 0) strerr_diefu2sys((errno == ETIMEDOUT) ? 99 : 111, "resolve ", argv[0]) ;
+ if (!r) strerr_diefu4x(2, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ;
+ }
+ if (!ips.len) return 1 ;
+ if (flagunsort) random_unsort(ips.s, ips.len / 4, 4) ;
+ for (i = 0 ; i < ips.len / 4 ; i++)
+ {
+ char fmt[IP4_FMT] ;
+ register unsigned int n = ip4_fmt(fmt, ips.s + 4 * i) ;
+ fmt[n++] = '\n' ;
+ if (buffer_put(buffer_1small, fmt, n) < (int)n)
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+ if (!buffer_flush(buffer_1small))
+ strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/clients/s6-dnsip6-filter.c b/src/clients/s6-dnsip6-filter.c
new file mode 100644
index 0000000..a5c7418
--- /dev/null
+++ b/src/clients/s6-dnsip6-filter.c
@@ -0,0 +1,52 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+#include "s6dns-generic-filter.h"
+
+#define USAGE "s6-dnsip6-filter [ -l lines ] [ -c concurrency ] [ -t timeout ] [ -f format ] [ -e errorformat ]"
+
+typedef struct s6dns_aaaa1_s s6dns_aaaa1_t, *s6dns_aaaa1_t_ref ;
+struct s6dns_aaaa1_s
+{
+ char ip[16] ;
+ unsigned int got : 1 ;
+} ;
+
+static int s6dns_message_parse_answer_aaaa1 (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_AAAA) && (rr->rdlength == 16))
+ {
+ s6dns_aaaa1_t *data = stuff ;
+ if (data->got) return 1 ;
+ byte_copy(data->ip, 16, packet+pos) ;
+ data->got = 1 ;
+ }
+ (void)packetlen ;
+ return 1 ;
+}
+
+static int ipformatter (stralloc *sa, char const *packet, unsigned int packetlen)
+{
+ s6dns_aaaa1_t data ;
+ s6dns_message_header_t h ;
+ register int r ;
+ data.got = 0 ;
+ r = s6dns_message_parse(&h, packet, packetlen, &s6dns_message_parse_answer_aaaa1, &data) ;
+ if (r <= 0) return r ;
+ if (!data.got) return 1 ;
+ if (!stralloc_readyplus(sa, IP6_FMT)) return -1 ;
+ sa->len += ip6_fmt(sa->s + sa->len, data.ip) ;
+ stralloc_0(sa) ;
+ return 1 ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ PROG = "s6-dnsip6-filter" ;
+ return s6dns_generic_filter_main(argc, argv, envp, S6DNS_T_AAAA, &s6dns_namescanner, &ipformatter, USAGE) ;
+}
diff --git a/src/clients/s6-dnsip6.c b/src/clients/s6-dnsip6.c
new file mode 100644
index 0000000..e07ba53
--- /dev/null
+++ b/src/clients/s6-dnsip6.c
@@ -0,0 +1,62 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/random.h>
+#include <s6-dns/s6dns.h>
+
+#define USAGE "s6-dnsip6 [ -q ] [ -r ] [ -t timeout ] domain"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ stralloc ips = STRALLOC_ZERO ;
+ tain_t deadline ;
+ unsigned int i = 0 ;
+ int flagqualify = 0 ;
+ int flagunsort = 0 ;
+ PROG = "s6-dnsip6" ;
+
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "qrt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'q' : flagqualify = 1 ; break ;
+ case 'r' : flagunsort = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &i)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 1) dieusage() ;
+
+ tain_now_g() ;
+ if (i) tain_from_millisecs(&deadline, i) ; else deadline = tain_infinite_relative ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ register int r = s6dns_resolve_aaaa_g(&ips, argv[0], str_len(argv[0]), flagqualify, &deadline) ;
+ if (r < 0) strerr_diefu2sys((errno == ETIMEDOUT) ? 99 : 111, "resolve ", argv[0]) ;
+ if (!r) strerr_diefu4x(2, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ;
+ }
+ if (!ips.len) return 1 ;
+ if (flagunsort) random_unsort(ips.s, ips.len / 16, 16) ;
+ for (i = 0 ; i < ips.len / 16 ; i++)
+ {
+ char fmt[IP6_FMT] ;
+ register unsigned int n = ip6_fmt(fmt, ips.s + 16 * i) ;
+ fmt[n++] = '\n' ;
+ if (buffer_put(buffer_1small, fmt, n) < (int)n)
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+ if (!buffer_flush(buffer_1small))
+ strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/clients/s6-dnsmx.c b/src/clients/s6-dnsmx.c
new file mode 100644
index 0000000..a495cb2
--- /dev/null
+++ b/src/clients/s6-dnsmx.c
@@ -0,0 +1,64 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/random.h>
+#include <s6-dns/s6dns.h>
+
+#define USAGE "s6-dnsmx [ -q ] [ -r ] [ -t timeout ] name"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ genalloc mxs = GENALLOC_ZERO ; /* array of s6dns_message_rr_mx_t */
+ tain_t deadline ;
+ unsigned int i = 0 ;
+ int flagqualify = 0 ;
+ int flagunsort = 0 ;
+ PROG = "s6-dnsmx" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "qrt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'q' : flagqualify = 1 ; break ;
+ case 'r' : flagunsort = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &i)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 1) dieusage() ;
+
+ tain_now_g() ;
+ if (i) tain_from_millisecs(&deadline, i) ; else deadline = tain_infinite_relative ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ register int r = s6dns_resolve_mx_g(&mxs, argv[0], str_len(argv[0]), flagqualify, &deadline) ;
+ if (r < 0) strerr_diefu2sys((errno == ETIMEDOUT) ? 99 : 111, "resolve ", argv[0]) ;
+ if (!r) strerr_diefu4x(2, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ;
+ }
+ if (!genalloc_len(s6dns_message_rr_mx_t, &mxs)) return 1 ;
+ if (flagunsort) random_unsort(mxs.s, genalloc_len(s6dns_message_rr_mx_t, &mxs), sizeof(s6dns_message_rr_mx_t)) ;
+ for (i = 0 ; i < genalloc_len(s6dns_message_rr_mx_t, &mxs) ; i++)
+ {
+ char buf[S6DNS_FMT_MX] ;
+ register unsigned int len = s6dns_fmt_mx(buf, S6DNS_FMT_MX, genalloc_s(s6dns_message_rr_mx_t, &mxs) + i) ;
+ if (!len) strerr_diefu1sys(111, "format result") ;
+ if (buffer_put(buffer_1, buf, len) < 0) goto err ;
+ if (buffer_put(buffer_1, "\n", 1) < 0) goto err ;
+ }
+ if (!buffer_flush(buffer_1)) goto err ;
+ return 0 ;
+ err:
+ strerr_diefu1sys(111, "write to stdout") ;
+}
diff --git a/src/clients/s6-dnsname-filter.c b/src/clients/s6-dnsname-filter.c
new file mode 100644
index 0000000..6522003
--- /dev/null
+++ b/src/clients/s6-dnsname-filter.c
@@ -0,0 +1,83 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include "s6dns-generic-filter.h"
+
+#define USAGE "s6-dnsname-filter [ -4 ] [ -6 ] [ -l lines ] [ -c concurrency ] [ -t timeout ] [ -f format ] [ -e errorformat ]"
+
+static unsigned int ipscanner (s6dns_domain_t_ref d, char const *s)
+{
+ char ip[16] ;
+ register unsigned int pos ;
+ if (flag6)
+ {
+ pos = ip6_scan(s, ip) ;
+ if (pos)
+ {
+ s6dns_domain_arpafromip6(d, ip, 128) ;
+ goto yes ;
+ }
+ }
+ if (flag4)
+ {
+ pos = ip4_scan(s, ip) ;
+ if (pos)
+ {
+ s6dns_domain_arpafromip4(d, ip) ;
+ goto yes ;
+ }
+ }
+ return 0 ;
+ yes:
+ if (!s6dns_domain_encode(d)) return 0 ;
+ return pos ;
+}
+
+typedef struct s6dns_domain1_s s6dns_domain1_t, *s6dns_domain1_t_ref ;
+struct s6dns_domain1_s
+{
+ s6dns_domain_t d ;
+ unsigned int got : 1 ;
+} ;
+
+static int s6dns_message_parse_answer_domain1 (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_PTR))
+ {
+ s6dns_domain1_t *data = stuff ;
+ register unsigned int start = pos ;
+ if (data->got) return 1 ;
+ if (!s6dns_message_get_domain(&data->d, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ data->got = 1 ;
+ }
+ return 1 ;
+}
+
+static int domainformatter (stralloc *sa, char const *packet, unsigned int packetlen)
+{
+ s6dns_domain1_t data ;
+ s6dns_message_header_t h ;
+ register int r ;
+ data.got = 0 ;
+ r = s6dns_message_parse(&h, packet, packetlen, &s6dns_message_parse_answer_domain1, &data) ;
+ if (r <= 0) return r ;
+ if (!data.got) return 1 ;
+ if (!stralloc_readyplus(sa, data.d.len + 1)) return -1 ;
+ sa->len += s6dns_domain_tostring(sa->s + sa->len, data.d.len + 1, &data.d) ;
+ stralloc_0(sa) ;
+ return 1 ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ PROG = "s6-dnsname-filter" ;
+ return s6dns_generic_filter_main(argc, argv, envp, S6DNS_T_PTR, &ipscanner, &domainformatter, USAGE) ;
+}
diff --git a/src/clients/s6-dnsname.c b/src/clients/s6-dnsname.c
new file mode 100644
index 0000000..d65da3a
--- /dev/null
+++ b/src/clients/s6-dnsname.c
@@ -0,0 +1,75 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/ip46.h>
+#include <skalibs/random.h>
+#include <skalibs/s6dns.h>
+
+#define USAGE "s6-dnsname [ -4 | -6 ] [ -r ] [ -t timeout ] ip"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ genalloc ds = GENALLOC_ZERO ; /* array of s6dns_domain_t */
+ tain_t deadline ;
+ ip46full_t ip = IP46FULL_ZERO ;
+ unsigned int i = 0 ;
+ int flagunsort = 0 ;
+ int do4 = 0 ;
+ int do6 = 0 ;
+ PROG = "s6-dnsname" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "46rt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case '4' : do4 = 1 ; break ;
+ case '6' : do6 = 1 ; break ;
+ case 'r' : flagunsort = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &i)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 1) dieusage() ;
+ if (!do4 && !do6) do4 = do6 = 1 ;
+ if (do4 && do6)
+ {
+ if (!ip46full_scan(argv[0], &ip)) dieusage() ;
+ }
+ else if (do6)
+ {
+ if (!ip6_scan(argv[0], ip.ip)) dieusage() ;
+ ip.is6 = 1 ;
+ }
+ else if (!ip4_scan(argv[0], ip.ip)) dieusage() ;
+
+ tain_now_g() ;
+ if (i) tain_from_millisecs(&deadline, i) ; else deadline = tain_infinite_relative ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ register int r = ip.is6 ? s6dns_resolve_name6_g(&ds, ip.ip, &deadline) : s6dns_resolve_name4_g(&ds, ip.ip, &deadline) ;
+ if (r < 0) strerr_diefu2sys((errno == ETIMEDOUT) ? 99 : 111, "resolve ", argv[0]) ;
+ if (!r) strerr_diefu4x(2, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ;
+ }
+ if (!genalloc_len(s6dns_domain_t, &ds)) return 1 ;
+ if (flagunsort) random_unsort(ds.s, genalloc_len(s6dns_domain_t, &ds), sizeof(s6dns_domain_t)) ;
+ {
+ char buf[S6DNS_FMT_DOMAINLIST(genalloc_len(s6dns_domain_t, &ds))] ;
+ unsigned int len = s6dns_fmt_domainlist(buf, S6DNS_FMT_DOMAINLIST(genalloc_len(s6dns_domain_t, &ds)), genalloc_s(s6dns_domain_t, &ds), genalloc_len(s6dns_domain_t, &ds), "\n", 1) ;
+ if (!len) strerr_diefu1sys(111, "format result") ;
+ if (buffer_putalign(buffer_1, buf, len) < 0) goto err ;
+ }
+ if (buffer_putflush(buffer_1, "\n", 1) < 0) goto err ;
+ return 0 ;
+ err:
+ strerr_diefu1sys(111, "write to stdout") ;
+}
diff --git a/src/clients/s6-dnsns.c b/src/clients/s6-dnsns.c
new file mode 100644
index 0000000..d1caba5
--- /dev/null
+++ b/src/clients/s6-dnsns.c
@@ -0,0 +1,61 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/random.h>
+#include <s6-dns/s6dns.h>
+
+#define USAGE "s6-dnsns [ -q ] [ -r ] [ -t timeout ] name"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ genalloc ds = GENALLOC_ZERO ; /* array of s6dns_domain_t */
+ tain_t deadline ;
+ unsigned int i = 0 ;
+ int flagqualify = 0 ;
+ int flagunsort = 0 ;
+ PROG = "s6-dnsns" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "qrt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'q' : flagqualify = 1 ; break ;
+ case 'r' : flagunsort = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &i)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 1) dieusage() ;
+
+ tain_now_g() ;
+ if (i) tain_from_millisecs(&deadline, i) ; else deadline = tain_infinite_relative ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ register int r = s6dns_resolve_ns_g(&ds, argv[0], str_len(argv[0]), flagqualify, &deadline) ;
+ if (r < 0) strerr_diefu2sys((errno == ETIMEDOUT) ? 99 : 111, "resolve ", argv[0]) ;
+ if (!r) strerr_diefu4x(2, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ;
+ }
+ if (!genalloc_len(s6dns_domain_t, &ds)) return 1 ;
+ if (flagunsort) random_unsort(ds.s, genalloc_len(s6dns_domain_t, &ds), sizeof(s6dns_domain_t)) ;
+ {
+ char buf[S6DNS_FMT_DOMAINLIST(genalloc_len(s6dns_domain_t, &ds))] ;
+ unsigned int len = s6dns_fmt_domainlist(buf, S6DNS_FMT_DOMAINLIST(genalloc_len(s6dns_domain_t, &ds)), genalloc_s(s6dns_domain_t, &ds), genalloc_len(s6dns_domain_t, &ds), "\n", 1) ;
+ if (!len) strerr_diefu1sys(111, "format result") ;
+ if (buffer_put(buffer_1, buf, len) < 0) goto err ;
+ }
+ if (buffer_putflush(buffer_1, "\n", 1) < 0) goto err ;
+ return 0 ;
+ err:
+ strerr_diefu1sys(111, "write to stdout") ;
+}
diff --git a/src/clients/s6-dnsq.c b/src/clients/s6-dnsq.c
new file mode 100644
index 0000000..76ca4ea
--- /dev/null
+++ b/src/clients/s6-dnsq.c
@@ -0,0 +1,99 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/bitarray.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/buffer.h>
+#include <skalibs/genwrite.h>
+#include <skalibs/tai.h>
+#include <skalibs/ip46.h>
+#include <s6-dns/s6dns.h>
+#include <s6-dns/s6dns-analyze.h>
+#include <s6-dns/s6dns-debug.h>
+
+#define USAGE "s6-dnsq [ -1 | -2 ] [ -t timeout ] [ -D debuglevel ] qtype query serverip..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ tain_t deadline ;
+ unsigned int debuglevel = 0 ;
+ uint16 qtype ;
+ genwrite_t *where = &genwrite_stderr ;
+ PROG = "s6-dnsq" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ unsigned int t = 0 ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "12t:D:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case '1' : where = &genwrite_stdout ; break ;
+ case '2' : where = &genwrite_stderr ; break ;
+ case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
+ case 'D' : if (!uint0_scan(l.arg, &debuglevel)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ;
+ }
+ if (argc < 3) dieusage() ;
+ {
+ s6dns_debughook_t dbh = { .post_recv = 0, .pre_send = 0, .post_send = 0 } ;
+ s6dns_ip46list_t servers ;
+ s6dns_domain_t d ;
+ unsigned int i = 0, j = 0 ;
+ qtype = s6dns_analyze_qtype_parse(argv[0]) ;
+ if (!qtype) dieusage() ;
+ if (!s6dns_domain_fromstring_noqualify_encode(&d, argv[1], str_len(argv[1])))
+ strerr_diefu2sys(100, "encode ", argv[1]) ;
+ dbh.external = where ;
+ byte_zero(&servers, sizeof(s6dns_ip46list_t)) ;
+ for (; (i < (unsigned int)(argc - 2)) && (j < S6DNS_MAX_SERVERS) ; i++)
+ {
+ ip46_t z[S6DNS_MAX_SERVERS] ;
+ unsigned int n ;
+ register unsigned int k = 0 ;
+ if (!*argv[2+i]) continue ;
+ if (!ip46_scanlist(z, S6DNS_MAX_SERVERS - j, argv[2 + i], &n))
+ strerr_diefu2sys(100, "make an IP address list out of ", argv[2+i]) ;
+ for (; k < n ; k++)
+ {
+ byte_copy(s6dns_ip46list_ip(&servers, j + k), SKALIBS_IP_SIZE, z[k].ip) ;
+#ifdef SKALIBS_IPV6_ENABLED
+ if (ip46_is6(z + k)) bitarray_set(servers.is6, j + k) ;
+#endif
+ }
+ j += n ;
+ }
+ if (debuglevel & 1) dbh.post_recv = &s6dns_debug_dumpdt_post_recv ;
+ if (debuglevel & 2) { dbh.pre_send = &s6dns_debug_dumpdt_pre_send ; dbh.post_send = &s6dns_debug_dumpdt_post_send ; }
+ tain_now_g() ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_engine_init_r_g(&s6dns_engine_here, &servers, 0, d.s, d.len, qtype, &dbh, &deadline))
+ strerr_diefu1sys(111, "initialize query") ;
+ }
+ if (!s6dns_resolve_loop_g(&deadline))
+ {
+ char fmt[UINT16_FMT] ;
+ fmt[uint16_fmt(fmt, qtype)] = 0 ;
+ strerr_diefu6x((errno == ETIMEDOUT) ? 99 : 2, "resolve query ", argv[0], " of qtype ", fmt, ": ", s6dns_constants_error_str(errno)) ;
+ }
+ if (!s6dns_analyze_packet(&genwrite_stdout, s6dns_engine_packet(&s6dns_engine_here), s6dns_engine_packetlen(&s6dns_engine_here), 0))
+ {
+ int e = errno ;
+ buffer_flush(buffer_1) ;
+ errno = e ;
+ strerr_diefu1sys(111, "analyze response") ;
+ }
+ if (!buffer_flush(buffer_1)) strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/clients/s6-dnsqr.c b/src/clients/s6-dnsqr.c
new file mode 100644
index 0000000..1effe68
--- /dev/null
+++ b/src/clients/s6-dnsqr.c
@@ -0,0 +1,74 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/buffer.h>
+#include <skalibs/genwrite.h>
+#include <skalibs/tai.h>
+#include <s6-dns/s6dns.h>
+#include <s6-dns/s6dns-analyze.h>
+#include <s6-dns/s6dns-debug.h>
+
+#define USAGE "s6-dnsqr [ -1 | -2 ] [ -t timeout ] [ -D debuglevel ] qtype query"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ tain_t deadline ;
+ unsigned int debuglevel = 0 ;
+ genwrite_t *where = &genwrite_stderr ;
+ PROG = "s6-dnsqr" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ unsigned int t = 0 ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "12t:D:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case '1' : where = &genwrite_stdout ; break ;
+ case '2' : where = &genwrite_stderr ; break ;
+ case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
+ case 'D' : if (!uint0_scan(l.arg, &debuglevel)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ;
+ }
+ if (argc < 2) dieusage() ;
+ {
+ s6dns_debughook_t dbh = { .post_recv = 0, .pre_send = 0, .post_send = 0 } ;
+ s6dns_domain_t d ;
+ uint16 qtype = s6dns_analyze_qtype_parse(argv[0]) ;
+ if (!qtype) dieusage() ;
+ if (!s6dns_domain_fromstring_noqualify_encode(&d, argv[1], str_len(argv[1])))
+ strerr_diefu2sys(100, "encode ", argv[1]) ;
+ dbh.external = where ;
+ if (debuglevel & 1) dbh.post_recv = &s6dns_debug_dumpdt_post_recv ;
+ if (debuglevel & 2) { dbh.pre_send = &s6dns_debug_dumpdt_pre_send ; dbh.post_send = &s6dns_debug_dumpdt_post_send ; }
+ tain_now_g() ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ if (!s6dns_resolve_core_r_g(&d, qtype, &s6dns_engine_here, &s6dns_rci_here.servers, &dbh, &deadline))
+ {
+ char fmt[UINT16_FMT] ;
+ fmt[uint16_fmt(fmt, qtype)] = 0 ;
+ strerr_diefu6x((errno == ETIMEDOUT) ? 99 : 2, "resolve query ", argv[1], " of qtype ", fmt, ": ", s6dns_constants_error_str(errno)) ;
+ }
+ }
+ if (!s6dns_analyze_packet(&genwrite_stdout, s6dns_engine_packet(&s6dns_engine_here), s6dns_engine_packetlen(&s6dns_engine_here), 1))
+ {
+ int e = errno ;
+ buffer_flush(buffer_1) ;
+ errno = e ;
+ strerr_diefu1sys(111, "analyze response") ;
+ }
+ if (!buffer_flush(buffer_1)) strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/clients/s6-dnsqualify.c b/src/clients/s6-dnsqualify.c
new file mode 100644
index 0000000..64767ae
--- /dev/null
+++ b/src/clients/s6-dnsqualify.c
@@ -0,0 +1,34 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <s6-dns/s6dns.h>
+
+#define USAGE "s6-dnsqualify name"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ s6dns_domain_t d ;
+ PROG = "s6-dnsqualify" ;
+ if (argc < 2) dieusage() ;
+ if (!s6dns_domain_fromstring(&d, argv[1], str_len(argv[1])))
+ strerr_diefu2sys(100, "make a domain name from ", argv[1]) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ s6dns_domain_t list[s6dns_rci_here.rulesnum] ;
+ unsigned int n = s6dns_qualify(list, &d) ;
+ if (!n) strerr_diefu2sys(111, "qualify ", argv[1]) ;
+ {
+ char buf[S6DNS_FMT_DOMAINLIST(n)] ;
+ unsigned int len = s6dns_fmt_domainlist(buf, S6DNS_FMT_DOMAINLIST(n), list, n, "\n", 1) ;
+ if (!len) strerr_diefu1sys(111, "format result") ;
+ if (buffer_put(buffer_1, buf, len) < 0) goto err ;
+ }
+ }
+ if (buffer_putflush(buffer_1, "\n", 1) < 0) goto err ;
+ return 0 ;
+ err:
+ strerr_diefu1sys(111, "write to stdout") ;
+}
diff --git a/src/clients/s6-dnssoa.c b/src/clients/s6-dnssoa.c
new file mode 100644
index 0000000..1b02ff6
--- /dev/null
+++ b/src/clients/s6-dnssoa.c
@@ -0,0 +1,63 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/random.h>
+#include <s6-dns/s6dns.h>
+
+#define USAGE "s6-dnssoa [ -q ] [ -r ] [ -t timeout ] name"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ genalloc soas = GENALLOC_ZERO ; /* array of s6dns_message_rr_soa_t */
+ tain_t deadline ;
+ unsigned int i = 0 ;
+ int flagqualify = 0 ;
+ int flagunsort = 0 ;
+ PROG = "s6-dnssoa" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "qrt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'q' : flagqualify = 1 ; break ;
+ case 'r' : flagunsort = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &i)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 1) dieusage() ;
+
+ tain_now_g() ;
+ if (i) tain_from_millisecs(&deadline, i) ; else deadline = tain_infinite_relative ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ register int r = s6dns_resolve_soa_g(&soas, argv[0], str_len(argv[0]), flagqualify, &deadline) ;
+ if (r < 0) strerr_diefu2sys((errno == ETIMEDOUT) ? 99 : 111, "resolve ", argv[0]) ;
+ if (!r) strerr_diefu4x(2, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ;
+ }
+ if (!genalloc_len(s6dns_message_rr_soa_t, &soas)) return 1 ;
+ if (flagunsort) random_unsort(soas.s, genalloc_len(s6dns_message_rr_soa_t, &soas), sizeof(s6dns_message_rr_soa_t)) ;
+ for (i = 0 ; i < genalloc_len(s6dns_message_rr_soa_t, &soas) ; i++)
+ {
+ char buf[S6DNS_FMT_SOA] ;
+ register unsigned int len = s6dns_fmt_soa(buf, S6DNS_FMT_SOA, genalloc_s(s6dns_message_rr_soa_t, &soas) + i) ;
+ if (!len) strerr_diefu1sys(111, "format result") ;
+ if (buffer_put(buffer_1, buf, len) < 0) goto err ;
+ if (buffer_put(buffer_1, "\n", 1) < 0) goto err ;
+ }
+ if (!buffer_flush(buffer_1)) goto err ;
+ return 0 ;
+ err:
+ strerr_diefu1sys(111, "write to stdout") ;
+}
diff --git a/src/clients/s6-dnssrv.c b/src/clients/s6-dnssrv.c
new file mode 100644
index 0000000..283f1cd
--- /dev/null
+++ b/src/clients/s6-dnssrv.c
@@ -0,0 +1,75 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/random.h>
+#include <s6-dns/s6dns.h>
+
+#define USAGE "s6-dnssrv [ -q ] [ -r ] [ -t timeout ] service protocol name"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ genalloc srvs = GENALLOC_ZERO ; /* array of s6dns_message_rr_srv_t */
+ tain_t deadline ;
+ unsigned int i = 0 ;
+ int flagqualify = 0 ;
+ int flagunsort = 0 ;
+ PROG = "s6-dnssrv" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "qt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'q' : flagqualify = 1 ; break ;
+ case 'r' : flagunsort = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &i)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 3) dieusage() ;
+
+ tain_now_g() ;
+ if (i) tain_from_millisecs(&deadline, i) ; else deadline = tain_infinite_relative ;
+ tain_add_g(&deadline, &deadline) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ unsigned int n0 = str_len(argv[0]) ;
+ unsigned int n1 = str_len(argv[1]) ;
+ unsigned int n2 = str_len(argv[2]) ;
+ char name[n0 + n1 + n2 + 5] ;
+ name[0] = '_' ;
+ byte_copy(name + 1, n0, argv[0]) ;
+ name[n0 + 1] = '.' ;
+ name[n0 + 2] = '_' ;
+ byte_copy(name + n0 + 3, n1, argv[1]) ;
+ name[n0 + n1 + 3] = '.' ;
+ byte_copy(name + n0 + n1 + 4, n2, argv[2]) ;
+ name[n0 + n1 + n2 + 4] = 0 ;
+ register int r = s6dns_resolve_srv_g(&srvs, name, n0 + n1 + n2 + 4, flagqualify, &deadline) ;
+ if (r < 0) strerr_diefu2sys((errno == ETIMEDOUT) ? 99 : 111, "resolve ", argv[0]) ;
+ if (!r) strerr_diefu4x(2, "resolve ", name, ": ", s6dns_constants_error_str(errno)) ;
+ }
+ if (!genalloc_len(s6dns_message_rr_srv_t, &srvs)) return 1 ;
+ if (flagunsort) random_unsort(srvs.s, genalloc_len(s6dns_message_rr_srv_t, &srvs), sizeof(s6dns_message_rr_srv_t)) ;
+ for (i = 0 ; i < genalloc_len(s6dns_message_rr_srv_t, &srvs) ; i++)
+ {
+ char buf[S6DNS_FMT_SRV] ;
+ register unsigned int len = s6dns_fmt_srv(buf, S6DNS_FMT_SRV, genalloc_s(s6dns_message_rr_srv_t, &srvs) + i) ;
+ if (!len) strerr_diefu1sys(111, "format result") ;
+ if (buffer_put(buffer_1, buf, len) < 0) goto err ;
+ if (buffer_put(buffer_1, "\n", 1) < 0) goto err ;
+ }
+ if (!buffer_flush(buffer_1)) goto err ;
+ return 0 ;
+ err:
+ strerr_diefu1sys(111, "write to stdout") ;
+}
diff --git a/src/clients/s6-dnstxt.c b/src/clients/s6-dnstxt.c
new file mode 100644
index 0000000..fde69d2
--- /dev/null
+++ b/src/clients/s6-dnstxt.c
@@ -0,0 +1,78 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/random.h>
+#include <s6-dns/s6dns.h>
+
+#define USAGE "s6-dnstxt [ -q ] [ -r ] [ -t timeout ] name"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ stralloc quoted = STRALLOC_ZERO ;
+ stralloc sa = STRALLOC_ZERO ;
+ genalloc offsets = GENALLOC_ZERO ; /* array of unsigned int */
+ tain_t deadline, stamp ;
+ unsigned int n ;
+ unsigned int i = 0 ;
+ int flagqualify = 0 ;
+ int flagunsort = 0 ;
+ PROG = "s6-dnstxt" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "qrt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'q' : flagqualify = 1 ; break ;
+ case 'r' : flagunsort = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &i)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 1) dieusage() ;
+
+ tain_now(&stamp) ;
+ if (i) tain_from_millisecs(&deadline, i) ; else deadline = tain_infinite_relative ;
+ tain_add(&deadline, &deadline, &stamp) ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+ {
+ register int r = s6dns_resolve_txt(&sa, &offsets, argv[0], str_len(argv[0]), flagqualify, &deadline, &stamp) ;
+ if (r < 0) strerr_diefu2sys((errno == ETIMEDOUT) ? 99 : 111, "resolve ", argv[0]) ;
+ if (!r) strerr_diefu4x(2, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ;
+ }
+ n = genalloc_len(unsigned int, &offsets) ;
+ if (!n) return 1 ;
+ {
+ unsigned int printable_offsets[n] ;
+ for (i = 0 ; i < n ; i++)
+ {
+ unsigned int beg = genalloc_s(unsigned int, &offsets)[i] ;
+ unsigned int end = (i < n-1 ? genalloc_s(unsigned int, &offsets)[i+1] : sa.len) - 1 ;
+ printable_offsets[i] = quoted.len ;
+ if (!string_quote(&quoted, sa.s + beg, end - beg) || !stralloc_0(&quoted))
+ strerr_diefu2sys(111, "quote ", sa.s + beg) ;
+ }
+ genalloc_free(unsigned int, &offsets) ;
+ stralloc_free(&sa) ;
+ if (flagunsort) random_unsort((char *)printable_offsets, n, sizeof(unsigned int)) ;
+ for (i = 0 ; i < n ; i++)
+ if ((buffer_puts(buffer_1small, quoted.s + printable_offsets[i]) < 0)
+ || (buffer_put(buffer_1small, "\n", 1) < 1))
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+ stralloc_free(&quoted) ;
+ if (!buffer_flush(buffer_1small))
+ strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/clients/s6-randomip.c b/src/clients/s6-randomip.c
new file mode 100644
index 0000000..b220374
--- /dev/null
+++ b/src/clients/s6-randomip.c
@@ -0,0 +1,62 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/random.h>
+
+#define USAGE "s6-randomip [ -4 ] [ -6 ] [ -n number ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ char fmt[IP6_FMT] ;
+ char ip[16] ;
+ unsigned int n ;
+ unsigned int i = 0 ;
+ unsigned int what = 0 ;
+ int finite = 0 ;
+ PROG = "s6-randomip" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "46n:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case '4' : what |= 1 ; break ;
+ case '6' : what |= 2 ; break ;
+ case 'n' :
+ if (!uint0_scan(subgetopt_here.arg, &n)) dieusage() ;
+ finite = 1 ;
+ break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (!what) what = 1 ;
+ what = 1 << (1 << what) ;
+ if (!badrandom_init()) strerr_diefu1sys(111, "init RNG") ;
+ for (i = 0 ; !finite || (i < n) ; i++)
+ {
+ unsigned int len = what ;
+ if (len > 16)
+ {
+ char c ;
+ if (badrandom_string(&c, 1) < 1)
+ strerr_diefu1sys(111, "badrandom_string") ;
+ len = (c & 1) ? 16 : 4 ;
+ }
+ if (badrandom_string(ip, len) < 4)
+ strerr_diefu1sys(111, "badrandom_string") ;
+ len = (len == 16) ? ip6_fmt(fmt, ip) : ip4_fmt(fmt, ip) ;
+ fmt[len++] = '\n' ;
+ if (buffer_put(buffer_1, fmt, len) < (int)len)
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+ if (!buffer_flush(buffer_1))
+ strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/clients/s6dns-generic-filter.h b/src/clients/s6dns-generic-filter.h
new file mode 100644
index 0000000..53fa560
--- /dev/null
+++ b/src/clients/s6dns-generic-filter.h
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#ifndef SKADNS_GENERIC_FILTER_H
+#define SKADNS_GENERIC_FILTER_H
+
+#include <skalibs/uint16.h>
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-domain.h>
+
+typedef unsigned int scan_func_t (s6dns_domain_t *, char const *) ;
+typedef scan_func_t *scan_func_t_ref ;
+typedef int fmt_func_t (stralloc *, char const *, unsigned int) ;
+typedef fmt_func_t *fmt_func_t_ref ;
+
+extern unsigned int s6dns_namescanner (s6dns_domain_t *, char const *) ;
+extern int s6dns_domainformatter (stralloc *, char const *, unsigned int) ;
+extern int s6dns_generic_filter_main (int, char const *const *, char const *const *, uint16, scan_func_t_ref, fmt_func_t_ref, char const *) ;
+
+extern int flag4 ;
+extern int flag6 ;
+
+#endif
diff --git a/src/clients/s6dns_generic_filter_main.c b/src/clients/s6dns_generic_filter_main.c
new file mode 100644
index 0000000..430b0c3
--- /dev/null
+++ b/src/clients/s6dns_generic_filter_main.c
@@ -0,0 +1,230 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/buffer.h>
+#include <skalibs/bufalloc.h>
+#include <skalibs/skamisc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/skadns.h>
+#include "s6dns-generic-filter.h"
+
+#define dieusage() strerr_dief1x(100, USAGE) ;
+
+typedef struct line_s line_t, *line_t_ref ;
+struct line_s
+{
+ stralloc swrd ;
+ unsigned int wpos ;
+ int dpos ;
+ char w[2] ;
+ unsigned int pending : 1 ;
+} ;
+
+#define LINE_ZERO { STRALLOC_ZERO, 0, 0, "\0", 0 }
+
+static void line_recycle (line_t_ref l)
+{
+ l->swrd.len = 0 ;
+ l->pending = 0 ;
+}
+
+int flag4 = 0 ;
+int flag6 = 0 ;
+
+int s6dns_generic_filter_main (int argc, char const *const *argv, char const *const *envp, uint16 qtype, scan_func_t_ref scanner, fmt_func_t_ref formatter, char const *USAGE)
+{
+ skadns_t a = SKADNS_ZERO ;
+ tain_t deadline, tto ;
+ char const *normalformat = "%s=%d%w%r" ;
+ char const *errorformat = "%s=<%e>%w%r" ;
+ uint16 maxlines = 256 ;
+ uint16 maxconn = 128 ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ unsigned int t = 0 ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, (qtype == S6DNS_T_PTR) ? "46l:c:t:f:e:" : "l:c:t:f:e:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case '4' : flag4 = 1 ; break ;
+ case '6' : flag6 = 1 ; break ;
+ case 'l' : if (!uint160_scan(l.arg, &maxlines)) dieusage() ; break ;
+ case 'c' : if (!uint160_scan(l.arg, &maxconn)) dieusage() ; break ;
+ case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
+ case 'f' : normalformat = l.arg ; break ;
+ case 'e' : errorformat = l.arg ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ;
+ }
+ if (!flag4 && !flag6) flag4 = 1 ;
+ if (maxconn < 1) maxconn = 1 ;
+ if (maxconn > SKADNS_MAXCONCURRENCY) maxconn = SKADNS_MAXCONCURRENCY ;
+ if (maxlines < maxconn) maxlines = maxconn ;
+
+ tain_now_g() ;
+ tain_addsec_g(&deadline, 2) ;
+ if (!skadns_startf_g(&a, &deadline))
+ strerr_diefu1sys(111, "establish skadns connection") ;
+ if ((ndelay_on(0) < 0) || (ndelay_on(1) < 0))
+ strerr_diefu1sys(111, "ndelay_on") ;
+
+ {
+ iopause_fd x[3] = { { 0, 0, 0 }, { 1, 0, 0 }, { -1, 0, 0 } } ;
+ uint16 lhead = 0, ltail = 0, numlines = 0, pending = 0 ;
+ line_t storage[maxlines+1] ;
+ uint16 lineindex[maxconn] ;
+ x[2].fd = skadns_fd(&a) ;
+ {
+ line_t line_zero = LINE_ZERO ;
+ char const *args[4] = { "", "", "", "" } ;
+ uint16 i = 0 ;
+ for (; i <= maxlines ; i++) storage[i] = line_zero ;
+ if (!string_format(&storage[0].swrd, "sdwr", normalformat, args)
+ || !string_format(&storage[0].swrd, "sewr", errorformat, args))
+ strerr_diefu1sys(111, "format a string") ;
+ storage[0].swrd.len = 0 ;
+ }
+
+ for (;;)
+ {
+ x[0].events = !x[0].fd && (numlines < maxlines) && (pending < maxconn) ? IOPAUSE_READ : 0 ;
+ x[1].events = bufalloc_len(bufalloc_1) ? IOPAUSE_WRITE : 0 ;
+ x[2].events = pending ? IOPAUSE_READ : 0 ;
+ if (!x[0].events && !x[1].events && !x[2].events) break ;
+ tain_add_g(&deadline, &tain_infinite_relative) ;
+
+ if (iopause_g(x + !(x[0].events & IOPAUSE_READ), 3 - !(x[0].events & IOPAUSE_READ), &deadline) < 0)
+ strerr_diefu1sys(111, "iopause") ;
+
+
+ /* Flush stdout */
+
+ if (x[1].revents)
+ {
+ if (!bufalloc_flush(bufalloc_1) && !error_isagain(errno))
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+
+
+ /* Get and format results from skadnsd */
+
+ if (x[2].revents)
+ {
+ register int j = 0 ;
+ register uint16 const *list ;
+ int n = skadns_update(&a) ;
+ if (n < 0) strerr_diefu1sys(111, "skadns_update") ;
+ list = skadns_list(&a) ;
+ for (; j < n ; j++)
+ {
+ register uint16 i = lineindex[list[j]] ;
+ register char const *packet = skadns_packet(&a, list[j]) ;
+ if (packet)
+ {
+ register int r ;
+ r = (*formatter)(&storage[i].swrd, packet, skadns_packetlen(&a, list[j])) ;
+ if (r < 0) strerr_diefu1sys(111, "format skadns answer") ;
+ if (!r) storage[i].dpos = -errno ;
+ }
+ else storage[i].dpos = -errno ;
+ storage[i].pending = 0 ;
+ skadns_release(&a, list[j]) ;
+ pending-- ;
+ }
+ skadns_clearlist(&a) ;
+ }
+
+
+ /* Scan stdin and send queries to skadnsd */
+
+ if (buffer_len(buffer_0) || (!x[0].fd && x[0].revents))
+ {
+ for (; (numlines < maxlines) && (pending < maxconn) ; lhead = (lhead+1) % (maxlines+1), numlines++)
+ {
+ s6dns_domain_t d ;
+ register line_t_ref line = storage + lhead ;
+ register int r = skagetln(buffer_0, &line->swrd, '\n') ;
+ if (r < 0)
+ {
+ if (error_isagain(errno)) break ;
+ if (errno != EPIPE) strerr_diefu1sys(111, "read from stdin") ;
+ if (!stralloc_catb(&line->swrd, "\n", 1)) strerr_diefu1sys(111, "stralloc_catb") ;
+ x[0].fd = -1 ;
+ break ;
+ }
+ else if (!r)
+ {
+ x[0].fd = -1 ;
+ break ;
+ }
+ line->swrd.s[line->swrd.len-1] = 0 ;
+ line->wpos = (*scanner)(&d, line->swrd.s) ;
+ if (!line->wpos)
+ {
+ line->wpos = line->swrd.len - 1 ;
+ line->w[0] = 0 ;
+ line->dpos = -errno ;
+ }
+ else
+ {
+ tain_t sendlimit ;
+ uint16 id ;
+ line->w[0] = line->swrd.s[line->wpos] ;
+ line->swrd.s[line->wpos] = 0 ;
+ tain_addsec_g(&sendlimit, 2) ;
+ tain_add_g(&deadline, &tto) ;
+ if (!skadns_send_g(&a, &id, &d, qtype, &deadline, &sendlimit))
+ line->dpos = -errno ;
+ else
+ {
+ line->dpos = line->swrd.len ;
+ lineindex[id] = lhead ;
+ line->pending = 1 ;
+ pending++ ;
+ }
+ }
+ }
+ }
+
+ if (x[0].revents & IOPAUSE_EXCEPT) x[0].fd = -1 ;
+
+
+ /* Send processed lines to stdout */
+
+ for (; ltail != lhead ; ltail = (ltail+1) % (maxlines+1), numlines--)
+ {
+ char *args[4] ;
+ register line_t_ref line = storage + ltail ;
+ if (line->pending) break ;
+ args[0] = line->swrd.s ;
+ args[1] = line->dpos < 0 ? (char *)s6dns_constants_error_str(-line->dpos) : line->swrd.s + line->dpos ;
+ args[2] = line->w ;
+ args[3] = line->swrd.s + line->wpos + !!line->w[0] ;
+ if (!string_format(&bufalloc_1->x, line->dpos < 0 ? "sewr" : "sdwr", line->dpos < 0 ? errorformat : normalformat, (char const **)args))
+ strerr_diefu1sys(111, "format output line") ;
+ line_recycle(line) ;
+ if (!bufalloc_put(bufalloc_1, "\n", 1))
+ strerr_diefu1sys(111, "bufalloc_put") ;
+ }
+ }
+ }
+
+ (void)envp ;
+ return 0 ;
+}
diff --git a/src/clients/s6dns_namescanner.c b/src/clients/s6dns_namescanner.c
new file mode 100644
index 0000000..b23fb3d
--- /dev/null
+++ b/src/clients/s6dns_namescanner.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-generic-filter.h>
+
+unsigned int s6dns_namescanner (s6dns_domain_t *d, char const *s)
+{
+ register unsigned int pos = 0 ;
+ while (s[pos] && (s[pos] != ' ') && (s[pos] != '\t') && (s[pos] != '\r') && (s[pos] != '\n')) pos++ ;
+ if (!s6dns_domain_fromstring_noqualify_encode(d, s, pos)) return 0 ;
+ return pos ;
+}
diff --git a/src/include/s6-dns/s6dns-analyze.h b/src/include/s6-dns/s6dns-analyze.h
new file mode 100644
index 0000000..2908763
--- /dev/null
+++ b/src/include/s6-dns/s6dns-analyze.h
@@ -0,0 +1,39 @@
+/* ISC license. */
+
+#ifndef S6DNS_ANALYZE_H
+#define S6DNS_ANALYZE_H
+
+#include <skalibs/uint16.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-message.h>
+
+typedef int s6dns_analyze_record_func_t (genwrite_t *, s6dns_message_rr_t const *, char const *, unsigned int, unsigned int) ;
+typedef s6dns_analyze_record_func_t *s6dns_analyze_record_func_t_ref ;
+
+typedef struct s6dns_analyze_rtypetable_s s6dns_analyze_rtypetable_t, *s6dns_analyze_rtypetable_t_ref ;
+struct s6dns_analyze_rtypetable_s
+{
+ uint16 rtype ;
+ char const *string ;
+ s6dns_analyze_record_func_t_ref f ;
+} ;
+
+extern uint16 s6dns_analyze_qtype_parse (char const *) ;
+
+extern s6dns_analyze_rtypetable_t const *s6dns_analyze_rtypetable ;
+
+extern s6dns_analyze_record_func_t s6dns_analyze_record_a ;
+extern s6dns_analyze_record_func_t s6dns_analyze_record_aaaa ;
+extern s6dns_analyze_record_func_t s6dns_analyze_record_hinfo ;
+extern s6dns_analyze_record_func_t s6dns_analyze_record_soa ;
+extern s6dns_analyze_record_func_t s6dns_analyze_record_mx ;
+extern s6dns_analyze_record_func_t s6dns_analyze_record_srv ;
+extern s6dns_analyze_record_func_t s6dns_analyze_record_domain ;
+extern s6dns_analyze_record_func_t s6dns_analyze_record_strings ;
+extern s6dns_analyze_record_func_t s6dns_analyze_record_unknown ;
+
+extern s6dns_analyze_record_func_t s6dns_analyze_record ;
+
+extern int s6dns_analyze_packet (genwrite_t *, char const *, unsigned int, int) ;
+
+#endif
diff --git a/src/include/s6-dns/s6dns-constants.h b/src/include/s6-dns/s6dns-constants.h
new file mode 100644
index 0000000..ad47dc1
--- /dev/null
+++ b/src/include/s6-dns/s6dns-constants.h
@@ -0,0 +1,52 @@
+/* ISC license. */
+
+#ifndef S6DNS_CONSTANTS_H
+#define S6DNS_CONSTANTS_H
+
+#include <skalibs/ip46.h>
+
+#define S6DNS_MAX_SERVERS 16U
+
+#define S6DNS_C_IN 0x0001U
+#define S6DNS_C_ANY 0x00ffU
+
+#define S6DNS_T_A 1U
+#define S6DNS_T_NS 2U
+#define S6DNS_T_CNAME 5U
+#define S6DNS_T_SOA 6U
+#define S6DNS_T_PTR 12U
+#define S6DNS_T_HINFO 13U
+#define S6DNS_T_MX 15U
+#define S6DNS_T_TXT 16U
+#define S6DNS_T_RP 17U
+#define S6DNS_T_SIG 24U
+#define S6DNS_T_KEY 25U
+#define S6DNS_T_AAAA 28U
+#define S6DNS_T_SRV 33U
+#define S6DNS_T_AXFR 252U
+#define S6DNS_T_ANY 255U
+
+#define S6DNS_O_RECURSIVE 0x0001U
+#define S6DNS_O_STRICT 0x0002U
+
+#define S6DNS_W_AND 0
+#define S6DNS_W_OR 1
+#define S6DNS_W_BEST 2
+
+typedef struct s6dns_constants_error_message_s s6dns_constants_error_message_t, *s6dns_constants_error_message_t_ref ;
+struct s6dns_constants_error_message_s
+{
+ int num ;
+ char const *string ;
+} ;
+
+extern s6dns_constants_error_message_t const *const s6dns_constants_error ;
+extern char const *s6dns_constants_error_str (int) ;
+
+#ifdef SKALIBS_IPV6_ENABLED
+# define S6DNS_LOCALHOST_IP IP6_LOCAL
+#else
+# define S6DNS_LOCALHOST_IP "\177\0\0\1"
+#endif
+
+#endif
diff --git a/src/include/s6-dns/s6dns-debug.h b/src/include/s6-dns/s6dns-debug.h
new file mode 100644
index 0000000..acc5818
--- /dev/null
+++ b/src/include/s6-dns/s6dns-debug.h
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#ifndef S6DNS_DEBUG_H
+#define S6DNS_DEBUG_H
+
+#include <s6-dns/s6dns-engine.h>
+
+extern s6dns_debughook_func_t s6dns_debug_dumpdt_post_recv ;
+extern s6dns_debughook_func_t s6dns_debug_dumpdt_pre_send ;
+extern s6dns_debughook_func_t s6dns_debug_dumpdt_post_send ;
+
+#define S6DNS_DEBUG_DUMPDT_INIT(gp) { &s6dns_debug_dumpdt_post_recv, &s6dns_debug_dumpdt_pre_send, &s6dns_debug_dumpdt_post_send, (gp) }
+extern s6dns_debughook_t const s6dns_debug_dumpdt_stdout ;
+extern s6dns_debughook_t const s6dns_debug_dumpdt_stderr ;
+
+#endif
diff --git a/src/include/s6-dns/s6dns-domain.h b/src/include/s6-dns/s6dns-domain.h
new file mode 100644
index 0000000..3faae07
--- /dev/null
+++ b/src/include/s6-dns/s6dns-domain.h
@@ -0,0 +1,47 @@
+/* ISC license. */
+
+#ifndef S6DNS_DOMAIN_H
+#define S6DNS_DOMAIN_H
+
+#include <skalibs/ip46.h>
+
+typedef struct s6dns_domain_s s6dns_domain_t, *s6dns_domain_t_ref ;
+struct s6dns_domain_s
+{
+ unsigned char len ;
+ char s[255] ;
+} ;
+
+
+ /* Conversions from/to user strings */
+
+extern int s6dns_domain_fromstring (s6dns_domain_t_ref, char const *, unsigned int) ;
+extern unsigned int s6dns_domain_tostring (char *, unsigned int, s6dns_domain_t const *) ;
+
+
+ /* Qualification */
+
+extern int s6dns_domain_noqualify (s6dns_domain_t_ref) ;
+extern unsigned int s6dns_domain_qualify (s6dns_domain_t *, s6dns_domain_t const *, char const *, unsigned int) ;
+
+
+ /* Internal coding/encoding to/from protocol form */
+
+extern int s6dns_domain_encode (s6dns_domain_t_ref) ;
+extern unsigned int s6dns_domain_encodelist (s6dns_domain_t_ref, unsigned int) ;
+extern int s6dns_domain_decode (s6dns_domain_t_ref) ;
+
+
+ /* Useful shortcuts */
+
+extern int s6dns_domain_fromstring_noqualify_encode (s6dns_domain_t_ref, char const *, unsigned int) ;
+extern unsigned int s6dns_domain_fromstring_qualify_encode (s6dns_domain_t *, char const *, unsigned int, char const *, unsigned int) ;
+
+
+ /* Helpers for PTR */
+
+extern void s6dns_domain_arpafromip4 (s6dns_domain_t_ref, char const *) ;
+extern void s6dns_domain_arpafromip6 (s6dns_domain_t_ref, char const *, unsigned int) ;
+#define s6dns_domain_arpafromip46(d, i) (ip46_is6(i) ? s6dns_domain_arpafromip6(d, (i)->ip, 128) : s6dns_domain_arpafromip4(d, (i)->ip))
+
+#endif
diff --git a/src/include/s6-dns/s6dns-engine.h b/src/include/s6-dns/s6dns-engine.h
new file mode 100644
index 0000000..d6b0503
--- /dev/null
+++ b/src/include/s6-dns/s6dns-engine.h
@@ -0,0 +1,108 @@
+/* ISC license. */
+
+#ifndef S6DNS_ENGINE_H
+#define S6DNS_ENGINE_H
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-ip46.h>
+
+
+ /* The dt structure: all the DNS q/r state information, transport-agnostic */
+
+typedef struct s6dns_engine_s s6dns_engine_t, *s6dns_engine_t_ref ;
+
+
+ /* Debug function hooks */
+
+typedef int s6dns_debughook_func_t (s6dns_engine_t const *, void *) ;
+typedef s6dns_debughook_func_t *s6dns_debughook_func_t_ref ;
+
+typedef struct s6dns_debughook_s s6dns_debughook_t, *s6dns_debughook_t_ref ;
+struct s6dns_debughook_s
+{
+ s6dns_debughook_func_t_ref post_recv ;
+ s6dns_debughook_func_t_ref pre_send ;
+ s6dns_debughook_func_t_ref post_send ;
+ void *external ;
+} ;
+#define S6DNS_DEBUGHOOK_ZERO { 0, 0, 0, 0 }
+extern s6dns_debughook_t const s6dns_debughook_zero ;
+
+
+ /*
+ s6dns-engine: the asynchronous DNS resolution primitives.
+ */
+
+struct s6dns_engine_s
+{
+ stralloc sa ; /* 2 bytes (qlen) + qlen bytes (query) + answers */
+ tain_t deadline ;
+ tain_t localdeadline ;
+ unsigned int querylen ;
+ int fd ;
+ uint32 protostate ;
+ s6dns_ip46list_t servers ;
+ s6dns_debughook_t const *debughook ;
+ unsigned int curserver ;
+ int status ;
+ unsigned int flagstrict : 1 ;
+ unsigned int flagtcp : 1 ;
+ unsigned int flagconnecting : 1 ;
+ unsigned int flagreading : 1 ;
+ unsigned int flagwriting : 1 ;
+} ;
+
+#define S6DNS_ENGINE_ZERO { \
+ .sa = STRALLOC_ZERO, \
+ .deadline = TAIN_ZERO, \
+ .localdeadline = TAIN_ZERO, \
+ .querylen = 0, \
+ .fd = -1, \
+ .protostate = 0, \
+ .servers = S6DNS_IP46LIST_ZERO, \
+ .debughook = 0, \
+ .curserver = 0, \
+ .status = ECONNABORTED, \
+ .flagstrict = 0, \
+ .flagtcp = 0, \
+ .flagconnecting = 0, \
+ .flagreading = 0, \
+ .flagwriting = 0 \
+}
+
+extern s6dns_engine_t const s6dns_engine_zero ;
+extern s6dns_engine_t s6dns_engine_here ;
+
+extern void s6dns_engine_recycle (s6dns_engine_t *) ;
+extern void s6dns_engine_free (s6dns_engine_t *) ;
+extern void s6dns_engine_freen (s6dns_engine_t *, unsigned int) ;
+
+#define s6dns_engine_init(dt, servers, options, q, qlen, qtype, deadline, stamp) s6dns_engine_init_r(dt, servers, options, q, qlen, qtype, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_engine_init_g(dt, servers, options, q, qlen, qtype, deadline) s6dns_engine_init(dt, servers, options, q, qlen, qtype, (deadline), &STAMP)
+extern int s6dns_engine_init_r (s6dns_engine_t *, s6dns_ip46list_t const *, uint32, char const *, unsigned int, uint16, s6dns_debughook_t const *, tain_t const *, tain_t const *) ;
+#define s6dns_engine_init_r_g(dt, servers, options, q, qlen, qtype, dbh, deadline) s6dns_engine_init_r(dt, servers, options, q, qlen, qtype, dbh, (deadline), &STAMP)
+
+
+ /* Call before iopause() */
+
+extern void s6dns_engine_nextdeadline (s6dns_engine_t const *, tain_t *) ;
+#define s6dns_engine_isreadable(dt) ((dt)->flagreading)
+#define s6dns_engine_iswritable(dt) ((dt)->flagwriting)
+
+
+ /* Call after iopause(): _timeout if iopause returns 0, _event otherwise */
+
+extern int s6dns_engine_timeout (s6dns_engine_t *, tain_t const *) ;
+#define s6dns_engine_timeout_g(dt) s6dns_engine_timeout((dt), &STAMP)
+extern int s6dns_engine_event (s6dns_engine_t *, tain_t const *) ;
+#define s6dns_engine_event_g(dt) s6dns_engine_event((dt), &STAMP)
+
+#define s6dns_engine_packet(dt) ((dt)->sa.s + (dt)->querylen)
+#define s6dns_engine_packetlen(dt) ((dt)->sa.len - (dt)->querylen)
+
+#endif
diff --git a/src/include/s6-dns/s6dns-fmt.h b/src/include/s6-dns/s6dns-fmt.h
new file mode 100644
index 0000000..fd4dfc5
--- /dev/null
+++ b/src/include/s6-dns/s6dns-fmt.h
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#ifndef S6DNS_FMT_H
+#define S6DNS_FMT_H
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+
+#define S6DNS_FMT_DOMAIN 256
+#define s6dns_fmt_domain(s, max, d) s6dns_domain_tostring(s, max, d)
+
+#define S6DNS_FMT_DOMAINLIST(n) ((n) * S6DNS_FMT_DOMAIN)
+extern unsigned int s6dns_fmt_domainlist (char *, unsigned int, s6dns_domain_t const *, unsigned int, char const *, unsigned int) ;
+
+#define S6DNS_FMT_HINFO 512
+extern unsigned int s6dns_fmt_hinfo (char *, unsigned int, s6dns_message_rr_hinfo_t const *) ;
+
+#define S6DNS_FMT_MX (S6DNS_FMT_DOMAIN + UINT16_FMT)
+extern unsigned int s6dns_fmt_mx (char *, unsigned int, s6dns_message_rr_mx_t const *) ;
+
+#define S6DNS_FMT_SOA (S6DNS_FMT_DOMAIN * 2 + 5 * UINT32_FMT)
+extern unsigned int s6dns_fmt_soa (char *, unsigned int, s6dns_message_rr_soa_t const *) ;
+
+#define S6DNS_FMT_SRV (S6DNS_FMT_DOMAIN + 3 * UINT16_FMT)
+extern unsigned int s6dns_fmt_srv (char *, unsigned int, s6dns_message_rr_srv_t const *) ;
+
+#endif
diff --git a/src/include/s6-dns/s6dns-ip46.h b/src/include/s6-dns/s6dns-ip46.h
new file mode 100644
index 0000000..d09aa7f
--- /dev/null
+++ b/src/include/s6-dns/s6dns-ip46.h
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#ifndef S6DNS_IP46_H
+#define S6DNS_IP46_H
+
+#include <skalibs/bitarray.h>
+#include <skalibs/ip46.h>
+#include <s6-dns/s6dns-constants.h>
+
+typedef struct s6dns_ip46list_s s6dns_ip46list_t, *s6dns_ip46list_t_ref ;
+struct s6dns_ip46list_s
+{
+ char ip[S6DNS_MAX_SERVERS * SKALIBS_IP_SIZE] ;
+#ifdef SKALIBS_IPV6_ENABLED
+ unsigned char is6[bitarray_div8(S6DNS_MAX_SERVERS)] ;
+#endif
+} ;
+
+#define s6dns_ip46list_ip(list, i) ((list)->ip + SKALIBS_IP_SIZE * (i))
+
+#ifdef SKALIBS_IPV6_ENABLED
+# define S6DNS_IP46LIST_ZERO { .ip = S6DNS_LOCALHOST_IP, .is6 = "\0" }
+# define s6dns_ip46list_is6(list, i) bitarray_peek((list)->is6, i)
+#else
+# define S6DNS_IP46LIST_ZERO { .ip = S6DNS_LOCALHOST_IP }
+# define s6dns_ip46list_is6(list, i) 0
+#endif
+
+#endif
diff --git a/src/include/s6-dns/s6dns-message.h b/src/include/s6-dns/s6dns-message.h
new file mode 100644
index 0000000..d26106b
--- /dev/null
+++ b/src/include/s6-dns/s6dns-message.h
@@ -0,0 +1,175 @@
+/* ISC license. */
+
+#ifndef S6DNS_MESSAGE_H
+#define S6DNS_MESSAGE_H
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-domain.h>
+
+
+ /* Header */
+
+typedef struct s6dns_message_counts_s s6dns_message_counts_t, *s6dns_message_counts_t_ref ;
+struct s6dns_message_counts_s
+{
+ uint16 qd ;
+ uint16 an ;
+ uint16 ns ;
+ uint16 nr ;
+} ;
+
+#define S6DNS_MESSAGE_COUNTS_ZERO { .qd = 0, .an = 0, .ns = 0, .nr = 0 }
+extern s6dns_message_counts_t const s6dns_message_counts_zero ;
+
+extern void s6dns_message_counts_pack (char *, s6dns_message_counts_t const *) ;
+extern void s6dns_message_counts_unpack (char const *, s6dns_message_counts_t *) ;
+extern unsigned int s6dns_message_counts_next (s6dns_message_counts_t *) ;
+
+typedef struct s6dns_message_header_s s6dns_message_header_t, *s6dns_message_header_t_ref ;
+struct s6dns_message_header_s
+{
+ uint16 id ;
+ unsigned int qr : 1 ;
+ unsigned int opcode : 4 ;
+ unsigned int aa : 1 ;
+ unsigned int tc : 1 ;
+ unsigned int rd : 1 ;
+ unsigned int ra : 1 ;
+ unsigned int z : 3 ;
+ unsigned int rcode : 4 ;
+ s6dns_message_counts_t counts ;
+} ;
+
+#define S6DNS_MESSAGE_HEADER_ZERO { \
+ .id = 0, \
+ .qr = 0, \
+ .opcode = 0, \
+ .aa = 0, \
+ .tc = 0, \
+ .rd = 0, \
+ .ra = 0, \
+ .z = 0, \
+ .rcode = 0, \
+ .counts = S6DNS_MESSAGE_COUNTS_ZERO \
+}
+extern s6dns_message_header_t const s6dns_message_header_zero ;
+
+extern void s6dns_message_header_pack (char *, s6dns_message_header_t const *) ;
+extern void s6dns_message_header_unpack (char const *, s6dns_message_header_t *) ;
+
+
+ /* Specific RR helpers */
+
+extern int s6dns_message_get_string (s6dns_domain_t *, char const *, unsigned int, unsigned int *) ;
+extern int s6dns_message_get_strings (char *, unsigned int, char const *, unsigned int, unsigned int *) ;
+extern int s6dns_message_get_domain (s6dns_domain_t *, char const *, unsigned int, unsigned int *) ;
+
+typedef struct s6dns_message_rr_hinfo_s s6dns_message_rr_hinfo_t, *s6dns_message_rr_hinfo_t_ref ;
+struct s6dns_message_rr_hinfo_s
+{
+ s6dns_domain_t cpu ;
+ s6dns_domain_t os ;
+} ;
+
+extern int s6dns_message_get_hinfo (s6dns_message_rr_hinfo_t *, char const *, unsigned int, unsigned int *) ;
+
+typedef struct s6dns_message_rr_mx_s s6dns_message_rr_mx_t, *s6dns_message_rr_mx_t_ref ;
+struct s6dns_message_rr_mx_s
+{
+ uint16 preference ;
+ s6dns_domain_t exchange ;
+} ;
+
+extern int s6dns_message_get_mx (s6dns_message_rr_mx_t *, char const *, unsigned int, unsigned int *) ;
+
+typedef struct s6dns_message_rr_soa_s s6dns_message_rr_soa_t, *s6dns_message_rr_soa_t_ref ;
+struct s6dns_message_rr_soa_s
+{
+ s6dns_domain_t mname ;
+ s6dns_domain_t rname ;
+ uint32 serial ;
+ uint32 refresh ;
+ uint32 retry ;
+ uint32 expire ;
+ uint32 minimum ;
+} ;
+
+extern int s6dns_message_get_soa (s6dns_message_rr_soa_t *, char const *, unsigned int, unsigned int *) ;
+
+typedef struct s6dns_message_rr_srv_s s6dns_message_rr_srv_t, *s6dns_message_rr_srv_t_ref ;
+struct s6dns_message_rr_srv_s
+{
+ uint16 priority ;
+ uint16 weight ;
+ uint16 port ;
+ s6dns_domain_t target ;
+} ;
+
+extern int s6dns_message_get_srv (s6dns_message_rr_srv_t *, char const *, unsigned int, unsigned int *) ;
+
+
+ /* The callback function type: how to parse RRs */
+
+typedef struct s6dns_message_rr_s s6dns_message_rr_t, *s6dns_message_rr_t_ref ;
+struct s6dns_message_rr_s
+{
+ s6dns_domain_t name ;
+ uint16 rtype ;
+ uint16 rclass ;
+ uint32 ttl ;
+ uint16 rdlength ;
+} ;
+
+typedef int s6dns_message_rr_func_t (s6dns_message_rr_t const *, char const *, unsigned int, unsigned int, unsigned int, void *) ;
+typedef s6dns_message_rr_func_t *s6dns_message_rr_func_t_ref ;
+
+
+ /* mpag: structure to encode several variable-length results */
+
+typedef struct s6dns_mpag_s s6dns_mpag_t, *s6dns_mpag_t_ref ;
+struct s6dns_mpag_s
+{
+ stralloc sa ;
+ genalloc offsets ; /* array of unsigned int */
+ uint16 rtype ;
+} ;
+#define S6DNS_MPAG_ZERO { .sa = STRALLOC_ZERO, .offsets = GENALLOC_ZERO, .rtype = 0 }
+
+
+ /* dpag: structure for domain lists */
+
+typedef struct s6dns_dpag_s s6dns_dpag_t, *s6dns_dpag_t_ref ;
+struct s6dns_dpag_s
+{
+ genalloc ds ; /* array of s6dns_domain_t */
+ uint16 rtype ;
+} ;
+#define S6DNS_DPAG_ZERO { .ds = GENALLOC_ZERO, .rtype = 0 }
+
+
+extern s6dns_message_rr_func_t s6dns_message_parse_answer_strings ;
+extern s6dns_message_rr_func_t s6dns_message_parse_answer_domain ;
+extern s6dns_message_rr_func_t s6dns_message_parse_answer_a ;
+extern s6dns_message_rr_func_t s6dns_message_parse_answer_aaaa ;
+extern s6dns_message_rr_func_t s6dns_message_parse_answer_mx ;
+extern s6dns_message_rr_func_t s6dns_message_parse_answer_hinfo ;
+extern s6dns_message_rr_func_t s6dns_message_parse_answer_soa ;
+extern s6dns_message_rr_func_t s6dns_message_parse_answer_srv ;
+
+
+ /* The actual parsing function */
+
+extern int s6dns_message_parse (s6dns_message_header_t_ref, char const *, unsigned int, s6dns_message_rr_func_t_ref, void *) ;
+
+
+ /* Internals of this function, for lower level access */
+
+extern int s6dns_message_parse_init (s6dns_message_header_t *, s6dns_message_counts_t_ref, char const *, unsigned int, unsigned int *) ;
+extern unsigned int s6dns_message_parse_skipqd (s6dns_message_counts_t *, char const *, unsigned int, unsigned int *) ;
+extern int s6dns_message_parse_getrr (s6dns_message_rr_t_ref, char const *, unsigned int, unsigned int *) ;
+extern unsigned int s6dns_message_parse_next (s6dns_message_counts_t *, s6dns_message_rr_t const *, char const *, unsigned int, unsigned int *) ;
+
+#endif
diff --git a/src/include/s6-dns/s6dns-rci.h b/src/include/s6-dns/s6dns-rci.h
new file mode 100644
index 0000000..5822a71
--- /dev/null
+++ b/src/include/s6-dns/s6dns-rci.h
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#ifndef S6DNS_RCI_H
+#define S6DNS_RCI_H
+
+#include <skalibs/stralloc.h>
+#include <skalibs/s6dns-constants.h>
+#include <skalibs/s6dns-ip46.h>
+#include <skalibs/s6dns-domain.h>
+
+ /* rci: resolv.conf information */
+
+typedef struct s6dns_rci_s s6dns_rci_t, *s6dns_rci_t_ref ;
+struct s6dns_rci_s
+{
+ s6dns_ip46list_t servers ;
+ stralloc rules ;
+ unsigned int rulesnum ;
+} ;
+#define S6DNS_RCI_ZERO { .servers = S6DNS_IP46LIST_ZERO, .rules = STRALLOC_ZERO, .rulesnum = 0 }
+
+extern s6dns_rci_t const s6dns_rci_zero ;
+extern s6dns_rci_t s6dns_rci_here ;
+extern int s6dns_rci_init (s6dns_rci_t_ref, char const *) ;
+extern void s6dns_rci_free (s6dns_rci_t_ref) ;
+
+#define s6dns_qualify(list, d) s6dns_domain_qualify(list, (d), s6dns_rci_here.rules.s, s6dns_rci_here.rulesnum)
+
+#endif
diff --git a/src/include/s6-dns/s6dns-resolve.h b/src/include/s6-dns/s6dns-resolve.h
new file mode 100644
index 0000000..d188a67
--- /dev/null
+++ b/src/include/s6-dns/s6dns-resolve.h
@@ -0,0 +1,242 @@
+ /* ISC license. */
+
+#ifndef S6DNS_RESOLVE_H
+#define S6DNS_RESOLVE_H
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/ip46.h>
+#include <skalibs/s6dns-constants.h>
+#include <skalibs/s6dns-ip46.h>
+#include <skalibs/s6dns-domain.h>
+#include <skalibs/s6dns-message.h>
+#include <skalibs/s6dns-engine.h>
+#include <skalibs/s6dns-rci.h>
+
+ /*
+ Synchronous DNS resolution primitives.
+ The non-reentrant functions are just wrappers around the reentrant ones,
+ using globals for the parameters the user doesn't care about:
+ s6dns_engine_here for the query storage (dt),
+ s6dns_debughook_zero meaning no debugging needed,
+ s6dns_rci_here for the resolv.conf information (qualification rules and
+ initial cache IPs)
+ */
+
+ /*
+ The basic s6dns_engine wrapper loop: takes an initted dt and resolves it.
+ */
+
+#define s6dns_resolve_loop(deadline, stamp) s6dns_resolve_loop_r(&s6dns_engine_here, (deadline), stamp)
+#define s6dns_resolve_loop_g(deadline) s6dns_resolve_loop((deadline), &STAMP)
+#define s6dns_resolve_loop_r(dt, deadline, stamp) (s6dns_resolven_loop(dt, 1, 2, deadline, stamp) < 0 ? 0 : (dt)->status ? (errno = (dt)->status, 0) : 1)
+#define s6dns_resolve_loop_r_g(dt, deadline) s6dns_resolve_loop(dt, (deadline), &STAMP)
+
+
+ /* QoL functions for single-domain synchronous resolution. */
+
+ /*
+ The innermost one:
+ Initializes the dt with the given data (d, qtype), then calls the loop.
+ */
+
+#define s6dns_resolve_core(d, qtype, deadline, stamp) s6dns_resolve_core_r(d, qtype, &s6dns_engine_here, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_core_g(d, qtype, deadline) s6dns_resolve_core(d, qtype, (deadline), &STAMP)
+extern int s6dns_resolve_core_r (s6dns_domain_t const *, uint16, s6dns_engine_t_ref, s6dns_ip46list_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolve_core_r_g(d, qtype, dt, servers, dbh, deadline) s6dns_resolve_core_r(d, qtype, dt, servers, dbh, (deadline), &STAMP)
+
+
+ /*
+ Just above. Calls s6dns_resolve_core() then feeds the result to s6dns_message_parse().
+ Returns -1 if a resolving or parsing error occurs.
+ Returns 0 if everything works but the result is empty for some reason (i.e. nxdomain).
+ Returns 1 if everything works and there's an actual answer.
+ */
+
+#define s6dns_resolve_parse(d, qtype, parsefunc, parsedata, deadline, stamp) s6dns_resolve_parse_r(d, qtype, parsefunc, parsedata, &s6dns_engine_here, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_parse_g(d, qtype, parsefunc, parsedata, deadline) s6dns_resolve_parse(d, qtype, parsefunc, parsedata, (deadline), &STAMP)
+extern int s6dns_resolve_parse_r (s6dns_domain_t const *, uint16, s6dns_message_rr_func_t_ref, void *, s6dns_engine_t_ref, s6dns_ip46list_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolve_parse_r_g(d, qtype, parsefunc, parsedata, dt, servers, dbh, deadline) s6dns_resolve_parse_r(d, qtype, parsefunc, parsedata, dt, servers, dbh, (deadline), &STAMP)
+
+
+ /*
+ Resolution without qualification. Encoding/decoding included.
+ */
+
+#define s6dns_resolvenoq(name, len, qtype, parsefunc, parsedata, deadline, stamp) s6dns_resolvenoq_r(name, len, qtype, parsefunc, parsedata, &s6dns_engine_here, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolvenoq_g(name, len, qtype, parsefunc, parsedata, deadline) s6dns_resolvenoq(name, len, qtype, parsefunc, parsedata, (deadline), &STAMP)
+extern int s6dns_resolvenoq_r (char const *, unsigned int, uint16, s6dns_message_rr_func_t_ref, void *, s6dns_engine_t_ref, s6dns_ip46list_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolvenoq_r_g(name, len, qtype, parsefunc, parsedata, dt, servers, dbh, deadline) s6dns_resolvenoq_r(name, len, qtype, parsefunc, parsedata, dt, servers, dbh, (deadline), &STAMP)
+
+
+ /*
+ Resolution with qualification:
+ Get a qualification list from a name, then resolve the list in parallel.
+ */
+
+#define s6dns_resolveq(name, len, qtype, parsefunc, parsedata, deadline, stamp) s6dns_resolveq_r(name, len, qtype, parsefunc, parsedata, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolveq_g(name, len, qtype, parsefunc, parsedata, deadline) s6dns_resolveq(name, len, qtype, parsefunc, parsedata, (deadline), &STAMP)
+extern int s6dns_resolveq_r (char const *, unsigned int, uint16, s6dns_message_rr_func_t_ref, void *, s6dns_rci_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolveq_r_g(name, len, qtype, parsefunc, parsedata, rci, dbh, deadline) s6dns_resolveq_r(name, len, qtype, parsefunc, parsedata, rci, dbh, (deadline), &STAMP)
+
+
+ /*
+ The resolution primitive that calls s6dns_resolvenoq() if the
+ qualif flag is cleared and s6dns_resolveq() if it is set.
+ */
+
+#define s6dns_resolve(name, len, qtype, parsefunc, parsedata, qualif, deadline, stamp) s6dns_resolve_r(name, len, qtype, parsefunc, parsedata, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_t const *, deadline, stamp)
+#define s6dns_resolve_g(name, len, qtype, parsefunc, parsedata, qualif, deadline) s6dns_resolve(name, len, qtype, parsefunc, parsedata, qualif, (deadline), &STAMP)
+#define s6dns_resolve_r(name, len, qtype, parsefunc, parsedata, qualif, dt, rci, dbh, deadline, stamp) ((qualif) ? s6dns_resolveq_r(name, len, qtype, parsefunc, parsedata, rci, dbh, deadline, stamp) : s6dns_resolvenoq_r(name, len, qtype, parsefunc, parsedata, dt, &(rci)->servers, dbh, deadline, stamp))
+#define s6dns_resolve_r_g(name, len, qtype, parsefunc, parsedata, qualif, dt, rci, dbh, deadline) s6dns_resolve_r(name, len, qtype, parsefunc, parsedata, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+
+ /* How to perform both AAAA and A queries at the same time */
+
+#define s6dns_resolvenoq_aaaaa(ips, name, len, deadline, stamp) s6dns_resolvenoq_aaaaa_r(ips, name, len, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolvenoq_aaaaa_g(ips, name, len, deadline) s6dns_resolvenoq_aaaaa(ips, name, len, (deadline), &STAMP)
+extern int s6dns_resolvenoq_aaaaa_r(genalloc *, char const *, unsigned int, s6dns_ip46list_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolvenoq_aaaaa_r_g(ips, name, len, servers, dbh, deadline) s6dns_resolvenoq_aaaaa_r(ips, name, len, servers, dbh, (deadline), &STAMP)
+
+#define s6dns_resolveq_aaaaa(ips, name, len, deadline, stamp) s6dns_resolveq_aaaaa_r(ips, name, len, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolveq_aaaaa_g(ips, name, len, deadline) s6dns_resolveq_aaaaa(ips, name, len, (deadline), &STAMP)
+extern int s6dns_resolveq_aaaaa_r(genalloc *, char const *, unsigned int, s6dns_rci_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolveq_aaaaa_r_g(ips, name, len, rci, dbh, deadline) s6dns_resolvenoq_aaaaa_r(ips, name, len, rci, dbh, (deadline), &STAMP)
+
+
+ /*
+ Some high-level functions.
+ Queries are automatically translated to domain form and encoded.
+ Domains returned in a stralloc are automatically decoded.
+ Warning: decoded domains all start with '.'
+ The int flag decides if qualification is needed or not.
+ */
+
+ /*
+ For A fields: the stralloc answer has 4 chars per IP4.
+ For AAAA fields: the stralloc answer has 16 chars per IP6.
+ For TXT fields (and other mpag stuff): the stralloc contains the strings
+ and the genalloc contains a list of offsets into that stralloc.
+ For other fields: the result is stored in a genalloc of the appropriate type.
+ */
+
+#define s6dns_resolve_a(ips, name, len, qualif, deadline, stamp) s6dns_resolve_a_r(ips, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_a_g(ips, name, len, qualif, deadline) s6dns_resolve_a(ips, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_a_r(ips, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_r(name, len, S6DNS_T_A, &s6dns_message_parse_answer_a, (ips), qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_a_r_g(ips, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_a_r(ips, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_aaaa(ip6s, name, len, qualif, deadline, stamp) s6dns_resolve_aaaa_r(ip6s, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_aaaa_g(ip6s, name, len, qualif, deadline) s6dns_resolve_aaaa(ip6s, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_aaaa_r(ip6s, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_r(name, len, S6DNS_T_AAAA, &s6dns_message_parse_answer_aaaa, (ip6s), qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_aaaa_r_g(ip6s, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_aaaa_r(ip6s, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_aaaaa(ips, name, len, qualif, deadline, stamp) s6dns_resolve_aaaaa_r(ips, name, len, qualif, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_aaaaa_g(ips, name, len, qualif, deadline) s6dns_resolve_aaaaa(ips, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_aaaaa_r(ips, name, len, qualif, rci, dbh, deadline, stamp) ((qualif) ? s6dns_resolveq_aaaaa_r(ips, name, len, rci, dbh, deadline, stamp) : s6dns_resolvenoq_aaaaa_r(ips, name, len, &(rci)->servers, dbh, deadline, stamp))
+#define s6dns_resolve_aaaaa_r_g(ips, name, len, qualif, rci, dbh, deadline) s6dns_resolve_aaaaa_r(ips, name, len, qualif, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_ptr(ds, name, len, deadline, stamp) s6dns_resolve_ptr_r(ds, name, len, &s6dns_engine_here, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_ptr_g(ds, name, len, deadline) s6dns_resolve_ptr(ds, name, len, (deadline), &STAMP)
+#define s6dns_resolve_ptr_r(ds, name, len, dt, servers, dbh, deadline, stamp) s6dns_resolvenoq_r(name, len, S6DNS_T_PTR, &s6dns_message_parse_answer_domain, (ds), dt, servers, dbh, deadline, stamp)
+#define s6dns_resolve_ptr_r_g(ds, name, len, dt, servers, dbh, deadline) s6dns_resolve_ptr_r(ds, name, len, dt, servers, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_name4(ds, ip, deadline, stamp) s6dns_resolve_name4_r(ds, ip, &s6dns_engine_here, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_name4_g(ds, ip, deadline) s6dns_resolve_name4(ds, ip, (deadline), &STAMP)
+extern int s6dns_resolve_name4_r (genalloc *, char const *, s6dns_engine_t_ref, s6dns_ip46list_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolve_name4_r_g(ds, ip, dt, servers, dbh, deadline) s6dns_resolve_name4_r(ds, ip, dt, servers, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_name6(ds, ip6, deadline, stamp) s6dns_resolve_name6_r(ds, ip6, &s6dns_engine_here, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_name6_g(ds, ip6, deadline) s6dns_resolve_name6(ds, ip6, (deadline), &STAMP)
+extern int s6dns_resolve_name6_r (genalloc *, char const *, s6dns_engine_t_ref, s6dns_ip46list_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolve_name6_r_g(ds, ip6, dt, servers, dbh, deadline) s6dns_resolve_name6_r(ds, ip6, dt, servers, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_name46(ds, i, deadline, stamp) s6dns_resolve_name46_r(ds, i, &s6dns_engine_here, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_name46_g(ds, i, deadline) s6dns_resolve_name46(ds, i, (deadline), &STAMP)
+#define s6dns_resolve_name46_r(ds, i, dt, servers, dbh, deadline, stamp) (ip46_is6(i) ? s6dns_resolve_name6_r(ds, (i)->ip, dt, servers, dbh, deadline, stamp) : s6dns_resolve_name4_r(ds, (i)->ip, dt, servers, dbh, deadline, stamp))
+#define s6dns_resolve_name46_r_g(ds, i, dt, servers, dbh, deadline) s6dns_resolve_name46_r(ds, i, dt, servers, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_txt(sa, offsets, name, len, qualif, deadline, stamp) s6dns_resolve_txt_r(sa, offsets, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_txt_g(sa, offsets, name, len, qualif, deadline) s6dns_resolve_txt(sa, offsets, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_txt_r(sa, offsets, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_mpag_r(sa, offsets, name, len, S6DNS_T_TXT, &s6dns_message_parse_answer_strings, qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_txt_r_g(sa, offsets, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_txt_r(sa, offsets, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_mx(mxs, name, len, qualif, deadline, stamp) s6dns_resolve_mx_r(mxs, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_mx_g(mxs, name, len, qualif, deadline) s6dns_resolve_mx(mxs, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_mx_r(mxs, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_r(name, len, S6DNS_T_MX, &s6dns_message_parse_answer_mx, (mxs), qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_mx_r_g(mxs, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_mx_r(mxs, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_ns(ds, name, len, qualif, deadline, stamp) s6dns_resolve_ns_r(ds, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_ns_g(ds, name, len, qualif, deadline) s6dns_resolve_ns(ds, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_ns_r(ds, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_dpag_r(ds, name, len, S6DNS_T_NS, qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_ns_r_g(ds, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_ns_r(ds, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_cname(ds, name, len, qualif, deadline, stamp) s6dns_resolve_cname_r(ds, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_cname_g(ds, name, len, qualif, deadline) s6dns_resolve_cname(ds, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_cname_r(ds, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_dpag_r(ds, name, len, S6DNS_T_CNAME, qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_cname_r_g(ds, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_cname_r(ds, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_hinfo(hinfos, name, len, qualif, deadline, stamp) s6dns_resolve_hinfo_r(hinfos, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_hinfo_g(hinfos, name, len, qualif, deadline) s6dns_resolve_hinfo(hinfos, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_hinfo_r(hinfos, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_r(name, len, S6DNS_T_HINFO, &s6dns_message_parse_answer_hinfo, (hinfos), qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_hinfo_r_g(hinfos, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_hinfo_r(hinfos, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_srv(srvs, name, len, qualif, deadline, stamp) s6dns_resolve_srv_r(srvs, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_srv_g(srvs, name, len, qualif, deadline) s6dns_resolve_srv(srvs, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_srv_r(srvs, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_r(name, len, S6DNS_T_SRV, &s6dns_message_parse_answer_srv, (srvs), qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_srv_r_g(srvs, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_srv_r(srvs, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_soa(soas, name, len, qualif, deadline, stamp) s6dns_resolve_soa_r(soas, name, len, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_soa_g(soas, name, len, qualif, deadline) s6dns_resolve_soa(soas, name, len, qualif, (deadline), &STAMP)
+#define s6dns_resolve_soa_r(soas, name, len, qualif, dt, rci, dbh, deadline, stamp) s6dns_resolve_r(name, len, S6DNS_T_SOA, &s6dns_message_parse_answer_soa, (soas), qualif, dt, rci, dbh, deadline, stamp)
+#define s6dns_resolve_soa_r_g(soas, name, len, qualif, dt, rci, dbh, deadline) s6dns_resolve_soa_r(soas, name, len, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+
+ /* Internals for the high-level functions. */
+
+ /* dpag: structure for generic domain lists + rtype */
+ /* mpag: encoding variable-length information into storage+offsets */
+
+#define s6dns_resolve_dpag(ds, name, len, qtype, qualif, deadline, stamp) s6dns_resolve_dpag_r(ds, name, len, qtype, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_dpag_g(ds, name, len, qtype, qualif, deadline) s6dns_resolve_dpag(ds, name, len, qtype, qualif, (deadline), &STAMP)
+extern int s6dns_resolve_dpag_r (genalloc *, char const *, unsigned int, uint16, int, s6dns_engine_t_ref, s6dns_rci_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolve_dpag_r_g(ds, name, len, qtype, qualif, dt, rci, dbh, deadline) s6dns_resolve_dpag_r(ds, name, len, qtype, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+#define s6dns_resolve_mpag(sa, offsets, name, len, qtype, parsefunc, qualif, deadline, stamp) s6dns_resolve_mpag_r(sa, offsets, name, len, qtype, parsefunc, qualif, &s6dns_engine_here, &s6dns_rci_here, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolve_mpag_g(sa, offsets, name, len, qtype, parsefunc, qualif, deadline) s6dns_resolve_mpag(sa, offsets, name, len, qtype, parsefunc, qualif, (deadline), &STAMP)
+extern int s6dns_resolve_mpag_r (stralloc *, genalloc *, char const *, unsigned int, uint16, s6dns_message_rr_func_t_ref, int, s6dns_engine_t_ref, s6dns_rci_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolve_mpag_r_g(sa, offsets, name, len, qtype, parsefunc, qualif, dt, rci, dbh, deadline) s6dns_resolve_mpag_r(sa, offsets, name, len, qtype, parsefunc, qualif, dt, rci, dbh, (deadline), &STAMP)
+
+
+ /*
+ Functions for n-domain parallel resolution.
+ s6dns_resolven_loop() is the core primitive.
+ s6dns_resolven_parse() is built upon it.
+ This API is still very limited in what it can do; for full
+ asynchronous resolution, use the skadns library.
+ */
+
+extern int s6dns_resolven_loop (s6dns_engine_t_ref, unsigned int, unsigned int, tain_t const *, tain_t *) ;
+#define s6dns_resolven_loop_g(list, n, or, deadline) s6dns_resolven(list, n, or, (deadline), &STAMP)
+
+typedef struct s6dns_resolve_s s6dns_resolve_t, *s6dns_resolve_t_ref ;
+struct s6dns_resolve_s
+{
+ s6dns_domain_t q ;
+ tain_t deadline ;
+ s6dns_message_rr_func_t_ref parsefunc ;
+ void *data ;
+ uint32 options ;
+ int status ;
+ uint16 qtype ;
+} ;
+
+#define s6dns_resolven_parse(list, n, deadline, stamp) s6dns_resolven_parse_r(list, n, &s6dns_rci_here.servers, &s6dns_debughook_zero, deadline, stamp)
+#define s6dns_resolven_parse_g(list, n, deadline) s6dns_resolven_parse(list, n, (deadline), &STAMP)
+extern int s6dns_resolven_parse_r (s6dns_resolve_t_ref, unsigned int, s6dns_ip46list_t const *, s6dns_debughook_t const *, tain_t const *, tain_t *) ;
+#define s6dns_resolven_parse_r_g(list, n, servers, dbh, deadline) s6dns_resolven_parse_r(list, n, servers, dbh, (deadline), &STAMP)
+
+#endif
diff --git a/src/include/s6-dns/s6dns.h b/src/include/s6-dns/s6dns.h
new file mode 100644
index 0000000..dbc1138
--- /dev/null
+++ b/src/include/s6-dns/s6dns.h
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#ifndef S6DNS_H
+#define S6DNS_H
+
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-rci.h>
+#include <s6-dns/s6dns-resolve.h>
+#include <s6-dns/s6dns-fmt.h>
+
+#define s6dns_init() s6dns_rci_init(&s6dns_rci_here, "/etc/resolv.conf")
+#define s6dns_finish() (s6dns_engine_free(&s6dns_engine_here), s6dns_rci_free(&s6dns_rci_here))
+
+#endif
diff --git a/src/include/s6-dns/skadns.h b/src/include/s6-dns/skadns.h
new file mode 100644
index 0000000..d5ed82f
--- /dev/null
+++ b/src/include/s6-dns/skadns.h
@@ -0,0 +1,73 @@
+/* ISC license. */
+
+#ifndef SKADNS_H
+#define SKADNS_H
+
+#include <errno.h>
+#include <skalibs/config.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6-dns/config.h>
+#include <s6-dns/s6dns-domain.h>
+
+#define SKADNSD_PROG S6_DNS_BINPREFIX "skadnsd"
+#define SKADNS_BANNER1 "skadns v1.0 (b)\n"
+#define SKADNS_BANNER1_LEN (sizeof SKADNS_BANNER1 - 1)
+#define SKADNS_BANNER2 "skadns v1.0 (a)\n"
+#define SKADNS_BANNER2_LEN (sizeof SKADNS_BANNER2 - 1)
+#define SKADNS_MAXCONCURRENCY 1000
+
+typedef struct skadnsanswer_s skadnsanswer_t, *skadnsanswer_t_ref ;
+struct skadnsanswer_s
+{
+ int status ;
+ char *data ;
+ unsigned int len ;
+} ;
+#define SKADNSANSWER_ZERO { .status = EINVAL, .data = 0, .len = 0 }
+
+typedef struct skadns_s skadns_t, *skadns_t_ref ;
+struct skadns_s
+{
+ skaclient_t connection ;
+ genalloc list ; /* array of uint16 */
+ gensetdyn q ; /* set of skadnsanswer_t */
+ skaclient_buffer_t buffers ;
+} ;
+#define SKADNS_ZERO { .connection = SKACLIENT_ZERO, .list = GENALLOC_ZERO, .q = GENSETDYN_INIT(skadnsanswer_t, 3, 3, 8) }
+extern skadns_t const skadns_zero ;
+
+
+ /* Starting and ending a session */
+
+extern int skadns_start (skadns_t *, char const *, tain_t const *, tain_t *) ;
+#define skadns_start_g(a, path, deadline) skadns_start(a, path, (deadline), &STAMP)
+extern int skadns_startf (skadns_t *, tain_t const *, tain_t *) ;
+#define skadns_startf_g(a, deadline) skadns_startf(a, (deadline), &STAMP)
+extern void skadns_end (skadns_t *) ;
+
+
+ /* Synchronous functions */
+
+extern int skadns_send (skadns_t *, uint16 *, s6dns_domain_t const *, uint16, tain_t const *, tain_t const *, tain_t *) ;
+#define skadns_send_g(a, id, d, qtype, limit, deadline) skadns_send(a, id, d, qtype, limit, (deadline), &STAMP)
+extern int skadns_cancel (skadns_t *, uint16, tain_t const *, tain_t *) ;
+#define skadns_cancel_g(a, id, deadline) skadns_cancel(a, id, (deadline), &STAMP)
+
+
+ /* Asynchronous functions */
+
+#define skadns_fd(a) skaclient_fd(&(a)->connection)
+extern int skadns_update (skadns_t *) ;
+#define skadns_list(a) genalloc_s(uint16 const, &(a)->list)
+#define skadns_clearlist(a) ((a)->list.len = 0)
+extern int skadns_packetlen (skadns_t const *, uint16) ;
+extern char const *skadns_packet (skadns_t const *, uint16) ;
+extern int skadns_release (skadns_t *, uint16) ;
+
+#endif
diff --git a/src/libs6dns/deps-lib/s6dns b/src/libs6dns/deps-lib/s6dns
new file mode 100755
index 0000000..b218652
--- /dev/null
+++ b/src/libs6dns/deps-lib/s6dns
@@ -0,0 +1,88 @@
+s6dns_constants_error.o
+s6dns_constants_error_str.o
+s6dns_debughook_zero.o
+s6dns_domain_arpafromip4.o
+s6dns_domain_arpafromip6.o
+s6dns_domain_decode.o
+s6dns_domain_encode.o
+s6dns_domain_encodelist.o
+s6dns_domain_fromstring.o
+s6dns_domain_fromstring_noqualify_encode.o
+s6dns_domain_fromstring_qualify_encode.o
+s6dns_domain_noqualify.o
+s6dns_domain_qualify.o
+s6dns_domain_tostring.o
+s6dns_engine.o
+s6dns_engine_free.o
+s6dns_engine_freen.o
+s6dns_engine_here.o
+s6dns_engine_nextdeadline.o
+s6dns_engine_zero.o
+s6dns_fmt_domainlist.o
+s6dns_fmt_hinfo.o
+s6dns_fmt_mx.o
+s6dns_fmt_soa.o
+s6dns_fmt_srv.o
+s6dns_message_counts_next.o
+s6dns_message_counts_pack.o
+s6dns_message_counts_unpack.o
+s6dns_message_counts_zero.o
+s6dns_message_get_domain.o
+s6dns_message_get_domain_internal.o
+s6dns_message_get_hinfo.o
+s6dns_message_get_string.o
+s6dns_message_get_string_internal.o
+s6dns_message_get_strings.o
+s6dns_message_get_mx.o
+s6dns_message_get_soa.o
+s6dns_message_get_srv.o
+s6dns_message_header_pack.o
+s6dns_message_header_unpack.o
+s6dns_message_header_zero.o
+s6dns_message_parse_answer_aaaa.o
+s6dns_message_parse_answer_a.o
+s6dns_message_parse_answer_domain.o
+s6dns_message_parse_answer_hinfo.o
+s6dns_message_parse_answer_mx.o
+s6dns_message_parse_answer_soa.o
+s6dns_message_parse_answer_srv.o
+s6dns_message_parse_answer_strings.o
+s6dns_message_parse.o
+s6dns_message_parse_getrr.o
+s6dns_message_parse_init.o
+s6dns_message_parse_next.o
+s6dns_message_parse_skipqd.o
+s6dns_rci_free.o
+s6dns_rci_here.o
+s6dns_rci_init.o
+s6dns_rci_zero.o
+s6dns_resolve_core.o
+s6dns_resolve_parse.o
+s6dns_resolven_loop.o
+s6dns_resolven_parse.o
+s6dns_resolve_dpag.o
+s6dns_resolve_mpag.o
+s6dns_resolve_name4.o
+s6dns_resolve_name6.o
+s6dns_resolvenoq.o
+s6dns_resolveq.o
+s6dns_resolvenoq_aaaaa.o
+s6dns_resolveq_aaaaa.o
+s6dns_analyze_packet.o
+s6dns_analyze_qtype_parse.o
+s6dns_analyze_record.o
+s6dns_analyze_record_a.o
+s6dns_analyze_record_aaaa.o
+s6dns_analyze_record_hinfo.o
+s6dns_analyze_record_mx.o
+s6dns_analyze_record_soa.o
+s6dns_analyze_record_srv.o
+s6dns_analyze_record_domain.o
+s6dns_analyze_record_strings.o
+s6dns_analyze_record_unknown.o
+s6dns_analyze_rtypetable.o
+s6dns_debug_dumpdt_stdout.o
+s6dns_debug_dumpdt_stderr.o
+s6dns_debug_dumpdt_post_recv.o
+s6dns_debug_dumpdt_pre_send.o
+s6dns_debug_dumpdt_post_send.o
diff --git a/src/libs6dns/s6dns-message-internal.h b/src/libs6dns/s6dns-message-internal.h
new file mode 100644
index 0000000..e0d10d6
--- /dev/null
+++ b/src/libs6dns/s6dns-message-internal.h
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#ifndef S6DNS_MESSAGE_INTERNAL_H
+#define S6DNS_MESSAGE_INTERNAL_H
+
+
+ /* Low-level packet parsing */
+
+extern int s6dns_message_get_string_internal (char *, unsigned int, char const *, unsigned int, unsigned int *) ;
+extern unsigned int s6dns_message_get_domain_internal (char *, unsigned int, char const *, unsigned int, unsigned int *) ;
+
+#endif
diff --git a/src/libs6dns/s6dns_analyze_packet.c b/src/libs6dns/s6dns_analyze_packet.c
new file mode 100644
index 0000000..57a5ce5
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_packet.c
@@ -0,0 +1,127 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/genwrite.h>
+#include <skalibs/fmtscan.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-analyze.h>
+
+#define add(s) if ((*gp->put)(gp->target, (s), str_len(s)) < 0) return 0
+#define addfmt(n) if ((*gp->put)(gp->target, fmt, uint_fmt(fmt, (n))) < 0) return 0
+#define addfmt16(n) if ((*gp->put)(gp->target, fmt, uint16_fmt(fmt, (n))) < 0) return 0
+
+int s6dns_analyze_packet (genwrite_t *gp, char const *packet, unsigned int packetlen, int rec)
+{
+ s6dns_message_header_t h ;
+ s6dns_message_counts_t counts ;
+ unsigned int pos ;
+ unsigned int section ;
+ char fmt[UINT_FMT] ;
+ if (!s6dns_message_parse_init(&h, &counts, packet, packetlen, &pos))
+ return 0 ;
+
+ addfmt(packetlen) ;
+ add(" bytes, ") ;
+ addfmt16(counts.qd) ;
+ add("+") ;
+ addfmt16(counts.an) ;
+ add("+") ;
+ addfmt16(counts.ns) ;
+ add("+") ;
+ addfmt16(counts.nr) ;
+ add(" records") ;
+ if (h.qr) add(", response") ;
+ if (h.opcode)
+ {
+ add(", weird op (") ;
+ addfmt(h.opcode) ;
+ add(")") ;
+ }
+ if (h.aa) add(", authoritative") ;
+ if (h.tc) add(", truncated") ;
+ if (h.rd)
+ {
+ add(", ") ;
+ if (!rec) add("weird ") ;
+ add("rd") ;
+ }
+ if (h.ra)
+ {
+ add(", ") ;
+ if (!rec) add("weird ") ;
+ add("ra") ;
+ }
+ switch (h.rcode)
+ {
+ case 0 : add(", noerror") ; break ;
+ case 1 : add(", fmterror") ; break ;
+ case 2 : add(", servfail") ; break ;
+ case 3 : add(", nxdomain") ; break ;
+ case 4 : add(", notimpl") ; break ;
+ case 5 : add(", refused") ; break ;
+ default:
+ {
+ add(", weird rcode (") ;
+ addfmt(h.rcode) ;
+ add(")") ;
+ }
+ }
+ if (h.z)
+ {
+ add(", weird z (") ;
+ addfmt(h.z) ;
+ add(")") ;
+ }
+ add("\n") ;
+
+ for (;;)
+ {
+ s6dns_domain_t d ;
+ char buf[257] ;
+ unsigned int len ;
+ uint16 qtype ;
+ uint16 qclass ;
+ section = s6dns_message_counts_next(&counts) ;
+ if (section != 1) break ;
+ add("query: ") ;
+ if (!s6dns_message_get_domain(&d, packet, packetlen, &pos)) return 0 ;
+ len = s6dns_domain_tostring(buf, 255, &d) ;
+ if (!len) return 0 ;
+ buf[len++] = '\n' ; buf[len++] = 0 ;
+ if (pos + 4 > packetlen) return (errno = EPROTO, 0) ;
+ uint16_unpack_big(packet + pos, &qtype) ; pos += 2 ;
+ uint16_unpack_big(packet + pos, &qclass) ; pos += 2 ;
+ if (qclass != S6DNS_C_IN)
+ {
+ add("weird class (") ;
+ addfmt16(qclass) ;
+ add(") - ") ;
+ }
+ addfmt16(qtype) ;
+ add(" ") ;
+ add(buf) ;
+ }
+
+ while (section)
+ {
+ static char const *intro[3] = { "answer: ", "authority: ", "additional: " } ;
+ s6dns_message_rr_t rr ;
+ if (!s6dns_message_parse_getrr(&rr, packet, packetlen, &pos)) return 0 ;
+ add(intro[section-2]) ;
+ if (rr.rclass != S6DNS_C_IN)
+ {
+ add("weird class (") ;
+ addfmt16(rr.rclass) ;
+ add("), not attempting to analyze record\n") ;
+ }
+ else if (!s6dns_analyze_record(gp, &rr, packet, packetlen, pos)) return 0 ;
+ section = s6dns_message_parse_next(&counts, &rr, packet, packetlen, &pos) ;
+ }
+
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_qtype_parse.c b/src/libs6dns/s6dns_analyze_qtype_parse.c
new file mode 100644
index 0000000..8c04137
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_qtype_parse.c
@@ -0,0 +1,46 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-analyze.h>
+
+typedef struct lookuptable_s lookuptable_t, *lookuptable_t_ref ;
+struct lookuptable_s
+{
+ char const *text ;
+ uint16 qtype ;
+} ;
+
+static lookuptable_t const table[] =
+{
+ { "ANY", S6DNS_T_ANY },
+ { "A", S6DNS_T_A },
+ { "NS", S6DNS_T_NS },
+ { "CNAME", S6DNS_T_CNAME },
+ { "SOA", S6DNS_T_SOA },
+ { "PTR", S6DNS_T_PTR },
+ { "HINFO", S6DNS_T_HINFO },
+ { "MX", S6DNS_T_MX },
+ { "TXT", S6DNS_T_TXT },
+ { "AAAA", S6DNS_T_AAAA },
+ { "SRV", S6DNS_T_SRV },
+ { "RP", S6DNS_T_RP },
+ { "SIG", S6DNS_T_SIG },
+ { "KEY", S6DNS_T_KEY },
+ { "AXFR", S6DNS_T_AXFR },
+ { 0, 0 }
+} ;
+
+uint16 s6dns_analyze_qtype_parse (char const *s)
+{
+ {
+ uint16 u ;
+ if (uint160_scan(s, &u)) return u ;
+ }
+ {
+ register lookuptable_t const *p = table ;
+ for (; p->text ; p++) if (case_equals(s, p->text)) return p->qtype ;
+ }
+ return 0 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record.c b/src/libs6dns/s6dns_analyze_record.c
new file mode 100644
index 0000000..dd2e7d2
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record.c
@@ -0,0 +1,37 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-analyze.h>
+
+static s6dns_analyze_rtypetable_t const *rtypelookup (uint16 rtype)
+{
+ register s6dns_analyze_rtypetable_t const *wut = s6dns_analyze_rtypetable ;
+ while (wut->rtype && wut->rtype != rtype) wut++ ;
+ return wut ;
+}
+
+int s6dns_analyze_record (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos)
+{
+ s6dns_analyze_rtypetable_t const *wut = rtypelookup(rr->rtype) ;
+ {
+ char buf[256] ;
+ register unsigned int n = s6dns_domain_tostring(buf, 256, &rr->name) ;
+ if (!n) return 0 ;
+ if ((*gp->put)(gp->target, buf, n) < 0) return 0 ;
+ }
+ {
+ char fmt[UINT32_FMT+1] = " " ;
+ if ((*gp->put)(gp->target, fmt, 1 + uint32_fmt(fmt+1, rr->ttl)) < 0) return 0 ;
+ }
+ if ((*gp->put)(gp->target, " ", 1) < 0) return 0 ;
+ if ((*gp->put)(gp->target, wut->string, str_len(wut->string)) < 0) return 0 ;
+ if ((*gp->put)(gp->target, " ", 1) < 0) return 0 ;
+ if (!(*wut->f)(gp, rr, packet, packetlen, pos)) return 0 ;
+ if ((*gp->put)(gp->target, "\n", 1) < 0) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_a.c b/src/libs6dns/s6dns_analyze_record_a.c
new file mode 100644
index 0000000..5e374cb
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_a.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genwrite.h>
+#include <skalibs/fmtscan.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_a (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos)
+{
+ char fmt[IP4_FMT] ;
+ if (rr->rdlength != 4) return (errno = EPROTO, 0) ;
+ if (pos + 4 > packetlen) return (errno = EPROTO, 0) ;
+ if ((*gp->put)(gp->target, fmt, ip4_fmt(fmt, packet + pos)) < 0) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_aaaa.c b/src/libs6dns/s6dns_analyze_record_aaaa.c
new file mode 100644
index 0000000..495ba24
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_aaaa.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genwrite.h>
+#include <skalibs/fmtscan.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_aaaa (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos)
+{
+ char fmt[IP6_FMT] ;
+ if (rr->rdlength != 16) return (errno = EPROTO, 0) ;
+ if (pos + 16 > packetlen) return (errno = EPROTO, 0) ;
+ if ((*gp->put)(gp->target, fmt, ip6_fmt(fmt, packet + pos)) < 0) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_domain.c b/src/libs6dns/s6dns_analyze_record_domain.c
new file mode 100644
index 0000000..f645dca
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_domain.c
@@ -0,0 +1,23 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_domain (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int start)
+{
+ s6dns_domain_t d ;
+ char buf[S6DNS_FMT_DOMAIN] ;
+ unsigned int pos = start ;
+ register unsigned int len ;
+ if (!s6dns_message_get_domain(&d, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ len = s6dns_fmt_domain(buf, 256, &d) ;
+ if (!len) return 0 ;
+ if ((*gp->put)(gp->target, buf, len) < 0) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_hinfo.c b/src/libs6dns/s6dns_analyze_record_hinfo.c
new file mode 100644
index 0000000..38d13d9
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_hinfo.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_hinfo (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int start)
+{
+ s6dns_message_rr_hinfo_t hinfo ;
+ char buf[S6DNS_FMT_HINFO] ;
+ unsigned int pos = start ;
+ unsigned int len ;
+ if (!s6dns_message_get_hinfo(&hinfo, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ len = s6dns_fmt_hinfo(buf, S6DNS_FMT_HINFO, &hinfo) ;
+ if (!len) return 0 ;
+ if ((*gp->put)(gp->target, buf, len) < 0) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_mx.c b/src/libs6dns/s6dns_analyze_record_mx.c
new file mode 100644
index 0000000..adf9995
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_mx.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_mx (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int start)
+{
+ s6dns_message_rr_mx_t mx ;
+ char buf[S6DNS_FMT_MX] ;
+ unsigned int pos = start ;
+ unsigned int len ;
+ if (!s6dns_message_get_mx(&mx, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ len = s6dns_fmt_mx(buf, S6DNS_FMT_MX, &mx) ;
+ if (!len) return 0 ;
+ if ((*gp->put)(gp->target, buf, len) < 0) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_soa.c b/src/libs6dns/s6dns_analyze_record_soa.c
new file mode 100644
index 0000000..68fc55f
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_soa.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_soa (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int start)
+{
+ s6dns_message_rr_soa_t soa ;
+ char buf[S6DNS_FMT_SOA] ;
+ unsigned int pos = start ;
+ unsigned int len ;
+ if (!s6dns_message_get_soa(&soa, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ len = s6dns_fmt_soa(buf, S6DNS_FMT_SOA, &soa) ;
+ if (!len) return 0 ;
+ if ((*gp->put)(gp->target, buf, len) < 0) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_srv.c b/src/libs6dns/s6dns_analyze_record_srv.c
new file mode 100644
index 0000000..9ce5b6e
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_srv.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_srv (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int start)
+{
+ s6dns_message_rr_srv_t srv ;
+ char buf[S6DNS_FMT_SRV] ;
+ unsigned int pos = start ;
+ unsigned int len ;
+ if (!s6dns_message_get_srv(&srv, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ len = s6dns_fmt_srv(buf, S6DNS_FMT_SRV, &srv) ;
+ if (!len) return 0 ;
+ if ((*gp->put)(gp->target, buf, len) < 0) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_strings.c b/src/libs6dns/s6dns_analyze_record_strings.c
new file mode 100644
index 0000000..c2aa15a
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_strings.c
@@ -0,0 +1,23 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genwrite.h>
+#include <skalibs/skamisc.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_strings (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int start)
+{
+ stralloc sa = STRALLOC_ZERO ;
+ char buf[rr->rdlength] ;
+ unsigned int pos = start ;
+ register int r = s6dns_message_get_strings(buf, rr->rdlength, packet, packetlen, &pos) ;
+ if (r < 0) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ if (!string_quote(&sa, buf, r)) return 0 ;
+ r = (*gp->put)(gp->target, sa.s, sa.len) >= 0 ;
+ stralloc_free(&sa) ;
+ return r ;
+}
diff --git a/src/libs6dns/s6dns_analyze_record_unknown.c b/src/libs6dns/s6dns_analyze_record_unknown.c
new file mode 100644
index 0000000..b5e458e
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_record_unknown.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-analyze.h>
+
+int s6dns_analyze_record_unknown (genwrite_t *gp, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos)
+{
+ char fmt[UINT16_FMT] ;
+ if ((*gp->put)(gp->target, "rtype ", 6) < 0) return 0 ;
+ if ((*gp->put)(gp->target, fmt, uint16_fmt(fmt, rr->rtype)) < 0) return 0 ;
+ if ((*gp->put)(gp->target, " length ", 8) < 0) return 0 ;
+ if ((*gp->put)(gp->target, fmt, uint16_fmt(fmt, rr->rdlength)) < 0) return 0 ;
+ if ((*gp->put)(gp->target, ": ", 2) < 0) return 0 ;
+ {
+ register uint16 i = 0 ;
+ for (; i < rr->rdlength ; i++)
+ if ((*gp->put)(gp->target, fmt, ucharn_fmt(fmt, packet + pos + i, 1)) < 0)
+ return 0 ;
+ }
+ (void)packetlen ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_analyze_rtypetable.c b/src/libs6dns/s6dns_analyze_rtypetable.c
new file mode 100644
index 0000000..523fc72
--- /dev/null
+++ b/src/libs6dns/s6dns_analyze_rtypetable.c
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-analyze.h>
+
+static s6dns_analyze_rtypetable_t const s6dns_analyze_rtypetable_array[] =
+{
+ { 1, "A", &s6dns_analyze_record_a },
+ { 2, "NS", &s6dns_analyze_record_domain },
+ { 5, "CNAME", &s6dns_analyze_record_domain },
+ { 6, "SOA", &s6dns_analyze_record_soa },
+ { 12, "PTR", &s6dns_analyze_record_domain },
+ { 13, "HINFO", &s6dns_analyze_record_hinfo },
+ { 15, "MX", &s6dns_analyze_record_mx },
+ { 16, "TXT", &s6dns_analyze_record_strings },
+ { 28, "AAAA", &s6dns_analyze_record_aaaa },
+ { 33, "SRV", &s6dns_analyze_record_srv },
+ { 0, "unknown", &s6dns_analyze_record_unknown }
+} ;
+
+s6dns_analyze_rtypetable_t const *s6dns_analyze_rtypetable = s6dns_analyze_rtypetable_array ;
diff --git a/src/libs6dns/s6dns_constants_error.c b/src/libs6dns/s6dns_constants_error.c
new file mode 100644
index 0000000..d5e4bcb
--- /dev/null
+++ b/src/libs6dns/s6dns_constants_error.c
@@ -0,0 +1,23 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <s6-dns/s6dns-constants.h>
+
+static s6dns_constants_error_message_t const array[] =
+{
+ { ENETUNREACH, "no available DNS server" },
+ { EBADMSG, "server did not understand query" },
+ { EBUSY, "server failure" },
+ { ENOENT, "no such domain" },
+ { ENOTSUP, "not implemented in server" },
+ { ECONNREFUSED, "server refused" },
+ { EIO, "unknown network error" },
+ { EAGAIN, "query still processing" },
+ { ETIMEDOUT, "query timed out" },
+ { EPROTO, "malformed packet" },
+ { EDOM, "internal error (please submit a bug-report)" },
+ { -1, "unknown error" }
+} ;
+
+s6dns_constants_error_message_t const *const s6dns_constants_error = array ;
diff --git a/src/libs6dns/s6dns_constants_error_str.c b/src/libs6dns/s6dns_constants_error_str.c
new file mode 100644
index 0000000..9dda1a7
--- /dev/null
+++ b/src/libs6dns/s6dns_constants_error_str.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <skalibs/error.h>
+#include <s6-dns/s6dns-constants.h>
+
+char const *s6dns_constants_error_str (int e)
+{
+ s6dns_constants_error_message_t *p = s6dns_constants_error ;
+ while ((p->num != e) && (p->num != -1)) p++ ;
+ return p->num == -1 ? error_str(e) : p->string ;
+}
diff --git a/src/libs6dns/s6dns_debug_dumpdt_post_recv.c b/src/libs6dns/s6dns_debug_dumpdt_post_recv.c
new file mode 100644
index 0000000..5680489
--- /dev/null
+++ b/src/libs6dns/s6dns_debug_dumpdt_post_recv.c
@@ -0,0 +1,14 @@
+/* ISC license */
+
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-debug.h>
+
+int s6dns_debug_dumpdt_post_recv (s6dns_engine_t const *dt, void *data)
+{
+ genwrite_t *gp = data ;
+ (void)dt ;
+ if ((*gp->put)(gp->target, "Received a packet\n", 19) < 19) return 0 ;
+ if ((*gp->put)(gp->target, "\n", 1) < 1) return 0 ;
+ return (*gp->flush)(gp->target) ;
+}
diff --git a/src/libs6dns/s6dns_debug_dumpdt_post_send.c b/src/libs6dns/s6dns_debug_dumpdt_post_send.c
new file mode 100644
index 0000000..e41d595
--- /dev/null
+++ b/src/libs6dns/s6dns_debug_dumpdt_post_send.c
@@ -0,0 +1,30 @@
+/* ISC license */
+
+#include <skalibs/uint16.h>
+#include <skalibs/djbtime.h>
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-debug.h>
+
+int s6dns_debug_dumpdt_post_send (s6dns_engine_t const *dt, void *data)
+{
+ genwrite_t *gp = data ;
+ char buf[LOCALTMN_FMT] ;
+ unsigned int len ;
+ if ((*gp->put)(gp->target, "Sent query ", 11) < 11) return 0 ;
+ {
+ uint16 id ;
+ uint16_unpack_big(dt->sa.s + 2, &id) ;
+ len = uint16_fmt(buf, id) ;
+ }
+ if ((*gp->put)(gp->target, buf, len) < (int)len) return 0 ;
+ if ((*gp->put)(gp->target, " - next recv deadline is ", 25) < 25) return 0 ;
+ {
+ localtmn_t l ;
+ if (!localtmn_from_tain(&l, &dt->localdeadline, 0)) return 0 ;
+ len = localtmn_fmt(buf, &l) ;
+ }
+ if ((*gp->put)(gp->target, buf, len) < (int)len) return 0 ;
+ if ((*gp->put)(gp->target, "\n\n", 2) < 2) return 0 ;
+ return (*gp->flush)(gp->target) ;
+}
diff --git a/src/libs6dns/s6dns_debug_dumpdt_pre_send.c b/src/libs6dns/s6dns_debug_dumpdt_pre_send.c
new file mode 100644
index 0000000..df8131d
--- /dev/null
+++ b/src/libs6dns/s6dns_debug_dumpdt_pre_send.c
@@ -0,0 +1,59 @@
+/* ISC license */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbtime.h>
+#include <skalibs/genwrite.h>
+#include <skalibs/ip46.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-analyze.h>
+#include <s6-dns/s6dns-debug.h>
+
+#ifdef SKALIBS_IPV6_ENABLED
+# define s6dns_ipfmt(buf, ip, is6) ((is6) ? ip6_fmt(buf, ip) : ip4_fmt(buf, ip))
+#else
+# define s6dns_ipfmt(buf, ip, is6) ip4_fmt(buf, ip)
+#endif
+
+int s6dns_debug_dumpdt_pre_send (s6dns_engine_t const *dt, void *data)
+{
+ genwrite_t *gp = data ;
+ char buf[LOCALTMN_FMT] ;
+ unsigned int len ;
+ if ((*gp->put)(gp->target, "Preparing to send via ", 22) < 22) return 0 ;
+ if ((*gp->put)(gp->target, dt->flagtcp ? "TCP" : "UDP", 3) < 3) return 0 ;
+ if ((*gp->put)(gp->target, " to ", 4) < 4) return 0 ;
+ len = dt->sa.s[4] & 1 ;
+ if ((*gp->put)(gp->target, len ? "cache" : "server", len ? 5 : 6) < (len ? 5 : 6)) return 0 ;
+ if ((*gp->put)(gp->target, " ", 1) < 1) return 0 ;
+ len = s6dns_ipfmt(buf, s6dns_ip46list_ip(&dt->servers, dt->curserver), s6dns_ip46list_is6(&dt->servers, dt->curserver)) ;
+ if ((*gp->put)(gp->target, buf, len) < (int)len) return 0 ;
+ if ((*gp->put)(gp->target, " with deadline ", 15) < 15) return 0 ;
+ {
+ localtmn_t l ;
+ if (!localtmn_from_tain(&l, &dt->localdeadline, 0))
+ {
+ if (errno != EOVERFLOW) return 0 ;
+ byte_copy(buf, 10, "\"infinite\"") ; len = 10 ;
+ }
+ else len = localtmn_fmt(buf, &l) ;
+ }
+ if ((*gp->put)(gp->target, buf, len) < (int)len) return 0 ;
+ if ((*gp->put)(gp->target, ", ", 2) < 2) return 0 ;
+ if (dt->flagstrict && (*gp->put)(gp->target, "strict, ", 8) < 8) return 0 ;
+ if ((*gp->put)(gp->target, "query id ", 9) < 9) return 0 ;
+ {
+ uint16 id ;
+ uint16_unpack_big(dt->sa.s + 2, &id) ;
+ len = uint16_fmt(buf, id) ;
+ }
+ if ((*gp->put)(gp->target, buf, len) < (int)len) return 0 ;
+ if ((*gp->put)(gp->target, ":\n", 2) < 2) return 0 ;
+ if (!s6dns_analyze_packet(gp, dt->sa.s + 2, dt->querylen - 2, 1)) return 0 ;
+ if ((*gp->put)(gp->target, "\n", 1) < 1) return 0 ;
+ return (*gp->flush)(gp->target) ;
+}
diff --git a/src/libs6dns/s6dns_debug_dumpdt_stderr.c b/src/libs6dns/s6dns_debug_dumpdt_stderr.c
new file mode 100644
index 0000000..46bdbea
--- /dev/null
+++ b/src/libs6dns/s6dns_debug_dumpdt_stderr.c
@@ -0,0 +1,7 @@
+/* ISC license */
+
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-debug.h>
+
+s6dns_debughook_t const s6dns_debug_dumpdt_stderr = S6DNS_DEBUG_DUMPDT_INIT((void *)&genwrite_stderr) ;
diff --git a/src/libs6dns/s6dns_debug_dumpdt_stdout.c b/src/libs6dns/s6dns_debug_dumpdt_stdout.c
new file mode 100644
index 0000000..9862549
--- /dev/null
+++ b/src/libs6dns/s6dns_debug_dumpdt_stdout.c
@@ -0,0 +1,7 @@
+/* ISC license */
+
+#include <skalibs/genwrite.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-debug.h>
+
+s6dns_debughook_t const s6dns_debug_dumpdt_stdout = S6DNS_DEBUG_DUMPDT_INIT((void *)&genwrite_stdout) ;
diff --git a/src/libs6dns/s6dns_debughook_zero.c b/src/libs6dns/s6dns_debughook_zero.c
new file mode 100644
index 0000000..f3006b2
--- /dev/null
+++ b/src/libs6dns/s6dns_debughook_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-engine.h>
+
+s6dns_debughook_t const s6dns_debughook_zero = S6DNS_DEBUGHOOK_ZERO ;
diff --git a/src/libs6dns/s6dns_domain_arpafromip4.c b/src/libs6dns/s6dns_domain_arpafromip4.c
new file mode 100644
index 0000000..d77938f
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_arpafromip4.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/uint.h>
+#include <s6-dns/s6dns-domain.h>
+
+void s6dns_domain_arpafromip4 (s6dns_domain_t *d, char const *ip)
+{
+ register unsigned int i = 0 ;
+ d->len = 0 ;
+ d->s[d->len++] = '.' ;
+ for (; i < 4 ; i++)
+ {
+ register unsigned int u = ((unsigned char *)ip)[3-i] ;
+ d->len += uint_fmt(d->s + d->len, u) ;
+ d->s[d->len++] = '.' ;
+ }
+ byte_copy(d->s + d->len, 13, "in-addr.arpa.") ; d->len += 13 ;
+}
diff --git a/src/libs6dns/s6dns_domain_arpafromip6.c b/src/libs6dns/s6dns_domain_arpafromip6.c
new file mode 100644
index 0000000..3b58127
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_arpafromip6.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/fmtscan.h>
+#include <s6-dns/s6dns-domain.h>
+
+void s6dns_domain_arpafromip6 (s6dns_domain_t *d, char const *ip6, unsigned int mask)
+{
+ register unsigned int i ;
+ if (mask > 128) mask = 128 ;
+ mask = mask ? 1 + ((mask-1) >> 2) : 0 ;
+ d->len = 0 ;
+ d->s[d->len++] = '.' ;
+ for (i = 32 - mask ; i < 32 ; i++)
+ {
+ unsigned char c = ip6[15-(i>>1)] ;
+ d->s[d->len++] = fmtscan_asc((i & 1) ? (c >> 4) : (c & 15)) ;
+ d->s[d->len++] = '.' ;
+ }
+ byte_copy(d->s + d->len, 9, "ip6.arpa.") ; d->len += 9 ;
+}
diff --git a/src/libs6dns/s6dns_domain_decode.c b/src/libs6dns/s6dns_domain_decode.c
new file mode 100644
index 0000000..db4ccf5
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_decode.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-domain.h>
+
+static inline unsigned int s6dns_domain_label_decode (char *s, unsigned int max)
+{
+ unsigned int len = *(unsigned char *)s ;
+ if ((len > 63) || (len >= max)) return (errno = EPROTO, 0) ;
+ *s = '.' ;
+ case_lowerb(s+1, len) ;
+ return len + 1 ;
+}
+
+int s6dns_domain_decode (s6dns_domain_t *d)
+{
+ unsigned int max = 255 ;
+ unsigned int pos = 0 ;
+ for (;;)
+ {
+ register unsigned int r = s6dns_domain_label_decode(d->s + pos, max - pos) ;
+ if (!r) return 0 ;
+ pos += r ;
+ if (r == 1) break ;
+ }
+ d->len = pos ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_domain_encode.c b/src/libs6dns/s6dns_domain_encode.c
new file mode 100644
index 0000000..61a790d
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_encode.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-domain.h>
+
+int s6dns_domain_encode (s6dns_domain_t *d)
+{
+ register char *s = d->s ;
+ register unsigned int len = d->len ;
+ if (!d->len || (*s != '.')) return (errno = EINVAL, 0) ;
+ while (len > 1)
+ {
+ register unsigned int n = byte_chr(s + 1, len - 1, '.') ;
+ if (n > 63) return (errno = EINVAL, 0) ;
+ *s = n++ ; s += n ; len -= n ;
+ }
+ if (!len) return (errno = EINVAL, 0) ;
+ *s = 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_domain_encodelist.c b/src/libs6dns/s6dns_domain_encodelist.c
new file mode 100644
index 0000000..23b4570
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_encodelist.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-domain.h>
+
+unsigned int s6dns_domain_encodelist (s6dns_domain_t *list, unsigned int n)
+{
+ register unsigned int i = 0 ;
+ for (; i < n ; i++)
+ if (!s6dns_domain_encode(list + i)) break ;
+ return i ;
+}
diff --git a/src/libs6dns/s6dns_domain_fromstring.c b/src/libs6dns/s6dns_domain_fromstring.c
new file mode 100644
index 0000000..9346e24
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_fromstring.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-domain.h>
+
+int s6dns_domain_fromstring (s6dns_domain_t *d, char const *s, unsigned int len)
+{
+ register unsigned int j = 1 ;
+ register unsigned int i = 0 ;
+ register unsigned int lastdot = 0 ;
+ d->s[0] = '.' ;
+ for (; i < len ; i++)
+ {
+ if (lastdot)
+ {
+ if ((j >= 255) || (lastdot++ >= 64)) return (errno = ENAMETOOLONG, 0) ;
+ d->s[j++] = s[i] ;
+ }
+ if (s[i] == '.') lastdot = 0 ;
+ else if (!lastdot)
+ {
+ i-- ;
+ lastdot = 1 ;
+ }
+ }
+ case_lowerb(d->s + 1, j-1) ;
+ d->len = j ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_domain_fromstring_noqualify_encode.c b/src/libs6dns/s6dns_domain_fromstring_noqualify_encode.c
new file mode 100644
index 0000000..a12f979
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_fromstring_noqualify_encode.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-domain.h>
+
+int s6dns_domain_fromstring_noqualify_encode (s6dns_domain_t *d, char const *name, unsigned int len)
+{
+ return s6dns_domain_fromstring(d, name, len)
+ && s6dns_domain_noqualify(d)
+ && s6dns_domain_encode(d) ;
+}
diff --git a/src/libs6dns/s6dns_domain_fromstring_qualify_encode.c b/src/libs6dns/s6dns_domain_fromstring_qualify_encode.c
new file mode 100644
index 0000000..dbdea02
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_fromstring_qualify_encode.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-domain.h>
+
+unsigned int s6dns_domain_fromstring_qualify_encode (s6dns_domain_t *list, char const *name, unsigned int len, char const *rules, unsigned int rulesnum)
+{
+ s6dns_domain_t d ;
+ if (!s6dns_domain_fromstring(&d, name, len)) return 0 ;
+ return s6dns_domain_encodelist(list, s6dns_domain_qualify(list, &d, rules, rulesnum)) ;
+}
diff --git a/src/libs6dns/s6dns_domain_noqualify.c b/src/libs6dns/s6dns_domain_noqualify.c
new file mode 100644
index 0000000..4f91171
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_noqualify.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <s6-dns/s6dns-domain.h>
+
+int s6dns_domain_noqualify (s6dns_domain_t *d)
+{
+ if (d->s[d->len-1] != '.')
+ {
+ if (d->len == 255) return (errno = ENAMETOOLONG, 0) ;
+ d->s[d->len++] = '.' ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_domain_qualify.c b/src/libs6dns/s6dns_domain_qualify.c
new file mode 100644
index 0000000..6194ab5
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_qualify.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-domain.h>
+
+unsigned int s6dns_domain_qualify (s6dns_domain_t *list, s6dns_domain_t const *d, char const *rules, unsigned int rulesnum)
+{
+ if (!d->len) return (errno = EINVAL, 0) ;
+ if (d->s[d->len - 1] == '.')
+ {
+ list[0] = *d ;
+ return 1 ;
+ }
+ else
+ {
+ register unsigned int i = 0 ;
+ for (; i < rulesnum ; i++)
+ {
+ unsigned int n = str_len(rules) ;
+ if (d->len + n >= 254) return (errno = ENAMETOOLONG, 0) ;
+ list[i] = *d ;
+ list[i].s[d->len] = '.' ;
+ byte_copy(list[i].s + d->len + 1, n, rules) ;
+ list[i].len += n+1 ;
+ rules += n+1 ;
+ }
+ return i ;
+ }
+}
diff --git a/src/libs6dns/s6dns_domain_tostring.c b/src/libs6dns/s6dns_domain_tostring.c
new file mode 100644
index 0000000..ff72893
--- /dev/null
+++ b/src/libs6dns/s6dns_domain_tostring.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-domain.h>
+
+unsigned int s6dns_domain_tostring (char *s, unsigned int max, s6dns_domain_t const *d)
+{
+ if ((unsigned int)d->len + 1 > max) return (errno = ENAMETOOLONG, 0) ;
+ if (!d->len || (d->s[0] != '.')) return (errno = EINVAL, 0) ;
+ if (d->len == 1)
+ {
+ s[0] = '.' ;
+ return 1 ;
+ }
+ else
+ {
+ byte_copy(s, d->len - 1, d->s + 1) ;
+ return d->len - 1 ;
+ }
+}
diff --git a/src/libs6dns/s6dns_engine.c b/src/libs6dns/s6dns_engine.c
new file mode 100644
index 0000000..4145ef7
--- /dev/null
+++ b/src/libs6dns/s6dns_engine.c
@@ -0,0 +1,372 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/error.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/mininetstring.h>
+#include <skalibs/socket.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/ip46.h>
+#include <skalibs/random.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message-internal.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+
+
+ /* Utility functions */
+
+static inline int qdomain_diff (char const *s1, unsigned int n1, char const *s2, unsigned int n2)
+{
+ return (n1 < n2) ? -1 : (n1 > n2) ? 1 : case_diffb(s1, n1, s2) ;
+}
+
+static int relevant (char const *q, unsigned int qlen, char const *ans, unsigned int anslen, int strict)
+{
+ {
+ s6dns_message_header_t h ;
+ uint16 id ;
+ s6dns_message_header_unpack(ans, &h) ;
+ if (!h.qr || h.opcode || h.z || (h.counts.qd != 1)) return 0 ;
+ if (h.rd != (q[2] & 1)) return 0 ;
+ if (strict && !h.aa && !(q[2] & 1)) return 0 ;
+ uint16_unpack_big(q, &id) ;
+ if (id != h.id) return 0 ;
+ }
+ {
+ char buf[255] ;
+ unsigned int pos = 12 ;
+ unsigned int n = s6dns_message_get_domain_internal(buf, 255, ans, anslen, &pos) ;
+ if (!n) return -1 ;
+ if (pos + 4 > anslen) return (errno = EPROTO, -1) ;
+ if (qdomain_diff(buf, n, q + 12, qlen - 16)) return 0 ;
+ if (byte_diff(q + qlen - 4, 4, ans + pos)) return 0 ;
+ }
+ return 1 ;
+}
+
+
+ /* Network core functions: transport-dependent */
+
+#ifdef SKALIBS_IPV6_ENABLED
+# define socketbind46(fd, ip, port, flag) ((flag) ? socket_bind6(fd, ip, port) : socket_bind4(fd, ip, port))
+# define socketudp46(flag) ((flag) ? socket_udp6() : socket_udp4())
+# define sockettcp46(flag) ((flag) ? socket_tcp6() : socket_tcp4())
+# define socketconnect46(fd, ip, port, flag) ((flag) ? socket_connect6(fd, ip, port) : socket_connect4(fd, ip, port))
+# define S6DNS_ENGINE_LOCAL0 IP6_ANY
+#else
+# define socketbind46(fd, ip, port, flag) ((void)(flag), socket_bind4(fd, ip, port))
+# define socketudp46(flag) socket_udp4()
+# define sockettcp46(flag) socket_tcp4()
+# define socketconnect46(fd, ip, port, flag) socket_connect4(fd, ip, port)
+# define S6DNS_ENGINE_LOCAL0 "\0\0\0"
+#endif
+
+static int randombind (int fd, int flag)
+{
+ register unsigned int i = 0 ;
+ for (; i < 10 ; i++)
+ if (socketbind46(fd, S6DNS_ENGINE_LOCAL0, 1025 + badrandom_int(64510), flag) >= 0) return 1 ;
+ return (socketbind46(fd, S6DNS_ENGINE_LOCAL0, 0, flag) >= 0) ;
+}
+
+static int thisudp (s6dns_engine_t *dt, tain_t const *stamp)
+{
+ for (;; dt->curserver++)
+ {
+ if (dt->curserver >= S6DNS_MAX_SERVERS)
+ {
+ dt->curserver = 0 ;
+ if (++dt->protostate >= 4) return -1 ;
+ }
+ if (byte_diff(s6dns_ip46list_ip(&dt->servers, dt->curserver), SKALIBS_IP_SIZE, S6DNS_ENGINE_LOCAL0)) break ;
+ }
+ if (badrandom_string(dt->sa.s + 2, 2) < 2) return 0 ; /* random query id */
+ dt->fd = socketudp46(s6dns_ip46list_is6(&dt->servers, dt->curserver)) ;
+ if (dt->fd < 0) return 0 ;
+ if (!randombind(dt->fd, s6dns_ip46list_is6(&dt->servers, dt->curserver))) goto err ; /* random source port */
+ if ((socketconnect46(dt->fd, s6dns_ip46list_ip(&dt->servers, dt->curserver), 53, s6dns_ip46list_is6(&dt->servers, dt->curserver)) < 0)
+ && (errno != EINPROGRESS)) goto err ;
+ tain_add(&dt->localdeadline, stamp, &tain_infinite_relative) ;
+ dt->flagreading = 0 ;
+ dt->flagwriting = 1 ;
+ if (dt->debughook && dt->debughook->pre_send) (*dt->debughook->pre_send)(dt, dt->debughook->external) ;
+ return 1 ;
+ err:
+ {
+ register int e = errno ;
+ fd_close(dt->fd) ; dt->fd = -1 ;
+ errno = e ;
+ }
+ return 0 ;
+}
+
+static int thistcp (s6dns_engine_t *dt, tain_t const *stamp)
+{
+ for (; dt->curserver < S6DNS_MAX_SERVERS ; dt->curserver++)
+ if (byte_diff(s6dns_ip46list_ip(&dt->servers, dt->curserver), SKALIBS_IP_SIZE, S6DNS_ENGINE_LOCAL0)) break ;
+ if (dt->curserver >= S6DNS_MAX_SERVERS) return -1 ;
+ if (badrandom_string(dt->sa.s + 2, 2) < 2) return 0 ; /* random query id */
+ dt->fd = sockettcp46(s6dns_ip46list_is6(&dt->servers, dt->curserver)) ;
+ if (dt->fd < 0) return 0 ;
+ if (!randombind(dt->fd, s6dns_ip46list_is6(&dt->servers, dt->curserver))) goto err ; /* random source port */
+ if ((socketconnect46(dt->fd, s6dns_ip46list_ip(&dt->servers, dt->curserver), 53, s6dns_ip46list_is6(&dt->servers, dt->curserver)) < 0)
+ && (errno != EINPROGRESS)) goto err ;
+ tain_addsec(&dt->localdeadline, stamp, 10) ;
+ dt->protostate = 0 ;
+ dt->flagtcp = dt->flagconnecting = dt->flagwriting = 1 ;
+ dt->flagreading = 0 ;
+ if (dt->debughook && dt->debughook->pre_send) (*dt->debughook->pre_send)(dt, dt->debughook->external) ;
+ return 1 ;
+ err:
+ {
+ register int e = errno ;
+ fd_close(dt->fd) ; dt->fd = -1 ;
+ errno = e ;
+ }
+ return 0 ;
+}
+
+
+ /* all the rest is transport-agnostic */
+
+static int s6dns_engine_prepare (s6dns_engine_t *dt, tain_t const *stamp, int istcp)
+{
+ for (;; dt->curserver++)
+ switch (istcp ? thistcp(dt, stamp) : thisudp(dt, stamp))
+ {
+ case -1 : return (errno = ENETUNREACH, 0) ;
+ case 0 : break ;
+ case 1 : return 1 ;
+ default : return (errno = EDOM, 0) ; /* can't happen */
+ }
+}
+
+static void prepare_next (s6dns_engine_t *dt, tain_t const *stamp, int istcp)
+{
+ if (!error_isagain(errno))
+ {
+ fd_close(dt->fd) ;
+ dt->curserver++ ;
+ if (s6dns_engine_prepare(dt, stamp, istcp)) errno = EAGAIN ;
+ }
+}
+
+static int s6dns_engine_write_udp (s6dns_engine_t *dt, tain_t const *stamp)
+{
+ static unsigned int const s6dns_engine_udp_timeouts[4] = { 1, 3, 11, 45 } ;
+ if (fd_send(dt->fd, dt->sa.s + 2, dt->querylen - 2, 0) < (int)(dt->querylen - 2))
+ return (prepare_next(dt, stamp, 0), 0) ;
+ tain_addsec(&dt->localdeadline, stamp, s6dns_engine_udp_timeouts[dt->protostate]) ;
+ dt->flagwriting = 0 ;
+ dt->flagreading = 1 ;
+ if (dt->debughook && dt->debughook->post_send) (*dt->debughook->post_send)(dt, dt->debughook->external) ;
+ return (errno = EAGAIN, 1) ;
+}
+
+static int s6dns_engine_write_tcp (s6dns_engine_t *dt, tain_t const *stamp)
+{
+ unsigned int r ;
+ r = allwrite(dt->fd, dt->sa.s + dt->protostate, dt->querylen - dt->protostate) ;
+ dt->protostate += r ;
+ if (r) dt->flagconnecting = 0 ;
+ if (dt->protostate < dt->sa.len)
+ {
+ if ((errno == ECONNRESET) && dt->flagconnecting) errno = EAGAIN ;
+ prepare_next(dt, stamp, 1) ;
+ return 0 ;
+ }
+ dt->protostate = 0 ;
+ tain_addsec(&dt->localdeadline, stamp, 10) ;
+ dt->flagwriting = 0 ;
+ dt->flagreading = 1 ;
+ if (dt->debughook && dt->debughook->post_send) (*dt->debughook->post_send)(dt, dt->debughook->external) ;
+ return (errno = EAGAIN, 1) ;
+}
+
+static int s6dns_engine_read_udp (s6dns_engine_t *dt, tain_t const *stamp)
+{
+ s6dns_message_header_t h ;
+ char buf[513] ;
+ register int r = fd_recv(dt->fd, buf, 513, 0) ;
+ if (r < 0) return (prepare_next(dt, stamp, 0), 0) ;
+ if ((r > 512) || (r < 12)) return (errno = EAGAIN, 0) ;
+ switch (relevant(dt->sa.s + 2, dt->querylen - 2, buf, r, dt->flagstrict))
+ {
+ case -1 : if (!dt->flagstrict) prepare_next(dt, stamp, 0) ; return 0 ;
+ case 0 : return (errno = EAGAIN, 0) ;
+ case 1 : break ;
+ default : return (errno = EDOM, 0) ; /* can't happen */
+ }
+ if (dt->debughook && dt->debughook->post_recv)
+ {
+ if (!stralloc_catb(&dt->sa, buf, r)) return 0 ;
+ (*dt->debughook->post_recv)(dt, dt->debughook->external) ;
+ dt->sa.len = dt->querylen ;
+ }
+ s6dns_message_header_unpack(buf, &h) ;
+ if (h.tc)
+ {
+ fd_close(dt->fd) ;
+ dt->curserver = 0 ;
+ dt->protostate = 0 ;
+ if (s6dns_engine_prepare(dt, stamp, 1)) errno = EAGAIN ;
+ return 0 ;
+ }
+ switch (h.rcode)
+ {
+ case 0 : case 3 : break ; /* normal operation */
+ case 1 : case 4 : case 5 :
+ byte_zero(s6dns_ip46list_ip(&dt->servers, dt->curserver), SKALIBS_IP_SIZE) ; /* do not query it again */
+ default : prepare_next(dt, stamp, 0) ; return 0 ;
+ }
+ if (!stralloc_copyb(&dt->sa, buf, r))
+ {
+ register int e = errno ;
+ fd_close(dt->fd) ; dt->fd = -1 ;
+ errno = e ;
+ return 0 ;
+ }
+ dt->querylen = 0 ;
+ fd_close(dt->fd) ; dt->fd = -1 ;
+ dt->flagreading = 0 ;
+ return 1 ;
+}
+
+static int s6dns_engine_read_tcp (s6dns_engine_t *dt, tain_t const *stamp)
+{
+ register int r = sanitize_read(mininetstring_read(dt->fd, &dt->sa, &dt->protostate)) ;
+ if (r < 0) return (prepare_next(dt, stamp, 1), 0) ;
+ else if (!r) return (errno = EAGAIN, 0) ;
+ else if ((dt->sa.len - dt->querylen) < 12)
+ {
+ errno = EPROTO ;
+ goto badanswer ;
+ }
+ else
+ {
+ s6dns_message_header_t h ;
+ switch (relevant(dt->sa.s + 2, dt->querylen - 2, dt->sa.s + dt->querylen, dt->sa.len - dt->querylen, dt->flagstrict))
+ {
+ case -1 : if (dt->flagstrict) { dt->sa.len = dt->querylen ; return 0 ; }
+ case 0 : goto badanswer ;
+ case 1 : break ;
+ default : dt->sa.len = dt->querylen ; return (errno = EDOM, 0) ; /* can't happen */
+ }
+ if (dt->debughook && dt->debughook->post_recv) (*dt->debughook->post_recv)(dt, dt->debughook->external) ;
+ s6dns_message_header_unpack(dt->sa.s + dt->querylen, &h) ;
+ if (h.tc) goto badanswer ;
+ switch (h.rcode)
+ {
+ case 0 : case 3 : break ; /* normal operation */
+ case 1 : case 4 : case 5 :
+ byte_zero(s6dns_ip46list_ip(&dt->servers, dt->curserver), SKALIBS_IP_SIZE) ; /* do not query it again */
+ default : goto badanswer ;
+ }
+ fd_close(dt->fd) ; dt->fd = -1 ;
+ dt->flagreading = 0 ;
+ return 1 ;
+ }
+ badanswer:
+ dt->sa.len = dt->querylen ;
+ prepare_next(dt, stamp, 1) ;
+ return 0 ;
+}
+
+
+void s6dns_engine_recycle (s6dns_engine_t *dt)
+{
+ dt->sa.len = 0 ;
+ dt->querylen = 0 ;
+ byte_zero(&dt->servers, sizeof(s6dns_ip46list_t)) ;
+ if (dt->fd >= 0)
+ {
+ register int e = errno ;
+ fd_close(dt->fd) ;
+ dt->fd = -1 ;
+ errno = e ;
+ }
+ dt->status = ECONNABORTED ;
+ dt->flagstrict = dt->flagtcp = dt->flagconnecting = dt->flagreading = dt->flagwriting = 0 ;
+}
+
+int s6dns_engine_timeout (s6dns_engine_t *dt, tain_t const *stamp)
+{
+ if (!error_isagain(dt->status)) return (errno = EINVAL, -1) ;
+ else if (tain_less(&dt->deadline, stamp)) goto yes ;
+ else if (!tain_less(&dt->localdeadline, stamp)) return 0 ;
+ else if (dt->flagwriting) goto yes ;
+ else if (!dt->flagreading) return 0 ;
+ fd_close(dt->fd) ;
+ dt->curserver++ ;
+ if (!s6dns_engine_prepare(dt, stamp, dt->flagtcp))
+ {
+ s6dns_engine_recycle(dt) ;
+ dt->status = errno ;
+ return -1 ;
+ }
+ return 0 ;
+ yes:
+ s6dns_engine_recycle(dt) ;
+ dt->status = ETIMEDOUT ;
+ return 1 ;
+}
+
+int s6dns_engine_event (s6dns_engine_t *dt, tain_t const *stamp)
+{
+ if (!error_isagain(dt->status)) return (errno = EINVAL, -1) ;
+ if (dt->flagwriting)
+ dt->flagtcp ? s6dns_engine_write_tcp(dt, stamp) : s6dns_engine_write_udp(dt, stamp) ;
+ else if (dt->flagreading)
+ {
+ if ((dt->flagtcp) ? s6dns_engine_read_tcp(dt, stamp) : s6dns_engine_read_udp(dt, stamp))
+ {
+ dt->status = 0 ;
+ return 1 ;
+ }
+ }
+ else return (errno = EINVAL, -1) ;
+ if (error_isagain(errno)) return 0 ;
+ s6dns_engine_recycle(dt) ;
+ dt->status = errno ;
+ return -1 ;
+}
+
+int s6dns_engine_init_r (s6dns_engine_t *dt, s6dns_ip46list_t const *servers, uint32 options, char const *q, unsigned int qlen, uint16 qtype, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t const *stamp)
+{
+ s6dns_message_header_t h = S6DNS_MESSAGE_HEADER_ZERO ;
+ if (!stralloc_ready(&dt->sa, qlen + 18)) return 0 ;
+ dt->deadline = *deadline ;
+ dt->localdeadline = *stamp ;
+ dt->querylen = qlen + 18 ;
+ dt->sa.len = dt->querylen ;
+ dt->servers = *servers ;
+ dt->debughook = dbh ;
+ dt->status = EAGAIN ;
+ dt->flagconnecting = dt->flagreading = dt->flagwriting = 0 ;
+ dt->flagstrict = !!(options & S6DNS_O_STRICT) ;
+ h.rd = !!(options & S6DNS_O_RECURSIVE) ;
+ h.counts.qd = 1 ;
+ uint16_pack_big(dt->sa.s, qlen + 16) ;
+ s6dns_message_header_pack(dt->sa.s + 2, &h) ;
+ byte_copy(dt->sa.s + 14, qlen, q) ;
+ uint16_pack_big(dt->sa.s + 14 + qlen, qtype) ;
+ uint16_pack_big(dt->sa.s + 16 + qlen, S6DNS_C_IN) ;
+ if (qlen > 496) dt->flagtcp = 1 ;
+ else
+ {
+ dt->flagtcp = 0 ;
+ dt->protostate = h.rd ;
+ }
+ if (!s6dns_engine_prepare(dt, stamp, dt->flagtcp))
+ {
+ s6dns_engine_recycle(dt) ;
+ return 0 ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_engine_free.c b/src/libs6dns/s6dns_engine_free.c
new file mode 100644
index 0000000..cfee465
--- /dev/null
+++ b/src/libs6dns/s6dns_engine_free.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-engine.h>
+
+void s6dns_engine_free (s6dns_engine_t *dt)
+{
+ s6dns_engine_recycle(dt) ;
+ stralloc_free(&dt->sa) ;
+ *dt = s6dns_engine_zero ;
+}
diff --git a/src/libs6dns/s6dns_engine_freen.c b/src/libs6dns/s6dns_engine_freen.c
new file mode 100644
index 0000000..10dd47e
--- /dev/null
+++ b/src/libs6dns/s6dns_engine_freen.c
@@ -0,0 +1,8 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-engine.h>
+
+void s6dns_engine_freen (s6dns_engine_t *dtl, unsigned int n)
+{
+ while (n--) s6dns_engine_free(dtl + n) ;
+}
diff --git a/src/libs6dns/s6dns_engine_here.c b/src/libs6dns/s6dns_engine_here.c
new file mode 100644
index 0000000..17a5f43
--- /dev/null
+++ b/src/libs6dns/s6dns_engine_here.c
@@ -0,0 +1,7 @@
+/* ISC license. */
+
+/* MT-unsafe */
+
+#include <s6-dns/s6dns-engine.h>
+
+s6dns_engine_t s6dns_engine_here = S6DNS_ENGINE_ZERO ;
diff --git a/src/libs6dns/s6dns_engine_nextdeadline.c b/src/libs6dns/s6dns_engine_nextdeadline.c
new file mode 100644
index 0000000..09102c7
--- /dev/null
+++ b/src/libs6dns/s6dns_engine_nextdeadline.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <skalibs/tai.h>
+#include <s6-dns/s6dns-engine.h>
+
+void s6dns_engine_nextdeadline (s6dns_engine_t const *dt, tain_t *deadline)
+{
+ if (tain_less(&dt->deadline, deadline)) *deadline = dt->deadline ;
+ if (tain_less(&dt->localdeadline, deadline)) *deadline = dt->localdeadline ;
+}
diff --git a/src/libs6dns/s6dns_engine_zero.c b/src/libs6dns/s6dns_engine_zero.c
new file mode 100644
index 0000000..ef3acaf
--- /dev/null
+++ b/src/libs6dns/s6dns_engine_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-engine.h>
+
+s6dns_engine_t const s6dns_engine_zero = S6DNS_ENGINE_ZERO ;
diff --git a/src/libs6dns/s6dns_fmt_domainlist.c b/src/libs6dns/s6dns_fmt_domainlist.c
new file mode 100644
index 0000000..d53050a
--- /dev/null
+++ b/src/libs6dns/s6dns_fmt_domainlist.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-fmt.h>
+
+unsigned int s6dns_fmt_domainlist (char *s, unsigned int max, s6dns_domain_t const *list, unsigned int n, char const *delim, unsigned int delimlen)
+{
+ unsigned int len = 0 ;
+ register unsigned int i = 0 ;
+ for (; i < n ; i++)
+ {
+ register unsigned int r = s6dns_domain_tostring(s + len, max - len, list + i) ;
+ if (!r) return 0 ;
+ len += r ;
+ if (i+1 < n)
+ {
+ if (len + delimlen > max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, delimlen, delim) ;
+ len += delimlen ;
+ }
+ }
+ return len ;
+}
diff --git a/src/libs6dns/s6dns_fmt_hinfo.c b/src/libs6dns/s6dns_fmt_hinfo.c
new file mode 100644
index 0000000..8c1d7c4
--- /dev/null
+++ b/src/libs6dns/s6dns_fmt_hinfo.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+
+unsigned int s6dns_fmt_hinfo (char *s, unsigned int max, s6dns_message_rr_hinfo_t const *hinfo)
+{
+ if ((unsigned int)hinfo->cpu.len + 1 + (unsigned int)hinfo->os.len > max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s, hinfo->cpu.len, hinfo->cpu.s) ;
+ s[hinfo->cpu.len] = ' ' ;
+ byte_copy(s + hinfo->cpu.len + 1, hinfo->os.len, hinfo->os.s) ;
+ return hinfo->cpu.len + 1 + hinfo->os.len ;
+}
diff --git a/src/libs6dns/s6dns_fmt_mx.c b/src/libs6dns/s6dns_fmt_mx.c
new file mode 100644
index 0000000..aba538b
--- /dev/null
+++ b/src/libs6dns/s6dns_fmt_mx.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+
+unsigned int s6dns_fmt_mx (char *s, unsigned int max, s6dns_message_rr_mx_t const *mx)
+{
+ char fmt[UINT16_FMT] ;
+ unsigned int len = uint16_fmt(fmt, mx->preference) ;
+ unsigned int r ;
+ if (len >= max) return 0 ;
+ fmt[len++] = ' ' ;
+ r = s6dns_domain_tostring(s + len, max - len, &mx->exchange) ;
+ if (!r) return 0 ;
+ byte_copy(s, len, fmt) ;
+ return len + r ;
+}
diff --git a/src/libs6dns/s6dns_fmt_soa.c b/src/libs6dns/s6dns_fmt_soa.c
new file mode 100644
index 0000000..e066c6a
--- /dev/null
+++ b/src/libs6dns/s6dns_fmt_soa.c
@@ -0,0 +1,44 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint32.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+
+unsigned int s6dns_fmt_soa (char *s, unsigned int max, s6dns_message_rr_soa_t const *soa)
+{
+ char fmt[UINT32_FMT] ;
+ unsigned int len = 0 ;
+ register unsigned int r = s6dns_domain_tostring(s + len, max - len, &soa->mname) ;
+ if (!r) return 0 ;
+ len += r ;
+ if (len >= max) return (errno = ENAMETOOLONG, 0) ;
+ s[len++] = ' ' ;
+ r = s6dns_domain_tostring(s + len, max - len, &soa->rname) ;
+ if (!r) return 0 ;
+ len += r ;
+ if (len >= max) return (errno = ENAMETOOLONG, 0) ;
+ s[len++] = ' ' ;
+ r = uint32_fmt(fmt, soa->serial) ;
+ if (len + r >= max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, r, fmt) ;
+ len += r ; s[len++] = ' ' ;
+ r = uint32_fmt(fmt, soa->refresh) ;
+ if (len + r >= max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, r, fmt) ;
+ len += r ; s[len++] = ' ' ;
+ r = uint32_fmt(fmt, soa->retry) ;
+ if (len + r >= max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, r, fmt) ;
+ len += r ; s[len++] = ' ' ;
+ r = uint32_fmt(fmt, soa->expire) ;
+ if (len + r >= max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, r, fmt) ;
+ len += r ; s[len++] = ' ' ;
+ r = uint32_fmt(fmt, soa->minimum) ;
+ if (len + r > max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, r, fmt) ;
+ len += r ;
+ return len ;
+}
diff --git a/src/libs6dns/s6dns_fmt_srv.c b/src/libs6dns/s6dns_fmt_srv.c
new file mode 100644
index 0000000..ad0944b
--- /dev/null
+++ b/src/libs6dns/s6dns_fmt_srv.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/bytestr.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-fmt.h>
+
+unsigned int s6dns_fmt_srv (char *s, unsigned int max, s6dns_message_rr_srv_t const *srv)
+{
+ char fmt[UINT16_FMT] ;
+ unsigned int len = 0 ;
+ register unsigned int r = uint16_fmt(fmt, srv->priority) ;
+ if (len + r >= max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, r, fmt) ;
+ len += r ; s[len++] = ' ' ;
+ r = uint16_fmt(fmt, srv->weight) ;
+ if (len + r >= max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, r, fmt) ;
+ len += r ; s[len++] = ' ' ;
+ r = uint16_fmt(fmt, srv->port) ;
+ if (len + r >= max) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(s + len, r, fmt) ;
+ len += r ; s[len++] = ' ' ;
+ r = s6dns_domain_tostring(s + len, max - len, &srv->target) ;
+ if (!r) return 0 ;
+ len += r ;
+ return len ;
+}
diff --git a/src/libs6dns/s6dns_message_counts_next.c b/src/libs6dns/s6dns_message_counts_next.c
new file mode 100644
index 0000000..2006775
--- /dev/null
+++ b/src/libs6dns/s6dns_message_counts_next.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-message.h>
+
+unsigned int s6dns_message_counts_next (s6dns_message_counts_t *counts)
+{
+ if (counts->qd) { counts->qd-- ; return 1 ; }
+ else if (counts->an) { counts->an-- ; return 2 ; }
+ else if (counts->ns) { counts->ns-- ; return 3 ; }
+ else if (counts->nr) { counts->nr-- ; return 4 ; }
+ else return 0 ;
+}
diff --git a/src/libs6dns/s6dns_message_counts_pack.c b/src/libs6dns/s6dns_message_counts_pack.c
new file mode 100644
index 0000000..51c0a78
--- /dev/null
+++ b/src/libs6dns/s6dns_message_counts_pack.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <s6-dns/s6dns-message.h>
+
+void s6dns_message_counts_pack (char *s, s6dns_message_counts_t const *counts)
+{
+ uint16_pack_big(s, counts->qd) ;
+ uint16_pack_big(s+2, counts->an) ;
+ uint16_pack_big(s+4, counts->ns) ;
+ uint16_pack_big(s+6, counts->nr) ;
+}
diff --git a/src/libs6dns/s6dns_message_counts_unpack.c b/src/libs6dns/s6dns_message_counts_unpack.c
new file mode 100644
index 0000000..968e5eb
--- /dev/null
+++ b/src/libs6dns/s6dns_message_counts_unpack.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <s6-dns/s6dns-message.h>
+
+void s6dns_message_counts_unpack (char const *s, s6dns_message_counts_t *counts)
+{
+ uint16_unpack_big(s, &counts->qd) ;
+ uint16_unpack_big(s+2, &counts->an) ;
+ uint16_unpack_big(s+4, &counts->ns) ;
+ uint16_unpack_big(s+6, &counts->nr) ;
+}
diff --git a/src/libs6dns/s6dns_message_counts_zero.c b/src/libs6dns/s6dns_message_counts_zero.c
new file mode 100644
index 0000000..b177aad
--- /dev/null
+++ b/src/libs6dns/s6dns_message_counts_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-message.h>
+
+s6dns_message_counts_t const s6dns_message_counts_zero = S6DNS_MESSAGE_COUNTS_ZERO ;
diff --git a/src/libs6dns/s6dns_message_get_domain.c b/src/libs6dns/s6dns_message_get_domain.c
new file mode 100644
index 0000000..b51adb5
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_domain.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message-internal.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_get_domain (s6dns_domain_t *d, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ return s6dns_message_get_domain_internal(d->s, 255, packet, packetlen, pos)
+ && s6dns_domain_decode(d) ;
+}
diff --git a/src/libs6dns/s6dns_message_get_domain_internal.c b/src/libs6dns/s6dns_message_get_domain_internal.c
new file mode 100644
index 0000000..357797d
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_domain_internal.c
@@ -0,0 +1,47 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/bytestr.h>
+#include "s6dns-message-internal.h"
+
+unsigned int s6dns_message_get_domain_internal (char *out, unsigned int outmax, char const *s, unsigned int len, unsigned int *pos)
+{
+ unsigned int w = 0 ; /* writing head */
+ unsigned int r = *pos ; /* reading head */
+ unsigned int jumps = 0 ;
+ register int hasjumped = 0 ;
+ for (;;)
+ {
+ unsigned char c ;
+ if (r >= len) return (errno = EPROTO, 0) ;
+ c = s[r] ;
+ if (c < 64) /* normal label */
+ {
+ if (r + ++c > len) return (errno = EPROTO, 0) ;
+ if (out)
+ {
+ if (w + c > outmax) return (errno = ENAMETOOLONG, 0) ;
+ byte_copy(out + w, c, s + r) ;
+ }
+ w += c ; r += c ; if (!hasjumped) *pos += c ;
+ if (c == 1) break ;
+ }
+ else if (c >= 192) /* pointer */
+ {
+ if (r + 1 >= len) return (errno = EPROTO, 0) ;
+ if (hasjumped)
+ {
+ if (++jumps > 1000) return (errno = EPROTO, 0) ;
+ }
+ else
+ {
+ *pos += 2 ;
+ hasjumped = 1 ;
+ }
+ r = (((unsigned int)c & 63) << 8) | (unsigned char)(s[r + 1]) ;
+ }
+ else return (errno = EPROTONOSUPPORT, 0) ; /* unsupported extension */
+ }
+ return w ;
+}
diff --git a/src/libs6dns/s6dns_message_get_hinfo.c b/src/libs6dns/s6dns_message_get_hinfo.c
new file mode 100644
index 0000000..c8ab821
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_hinfo.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_get_hinfo (s6dns_message_rr_hinfo_t_ref hinfo, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ return s6dns_message_get_string(&hinfo->cpu, packet, packetlen, pos)
+ && s6dns_message_get_string(&hinfo->os, packet, packetlen, pos) ;
+}
diff --git a/src/libs6dns/s6dns_message_get_mx.c b/src/libs6dns/s6dns_message_get_mx.c
new file mode 100644
index 0000000..6bca509
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_mx.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_get_mx (s6dns_message_rr_mx_t *mx, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ if (*pos + 3 > packetlen) return (errno = EPROTO, 0) ;
+ uint16_unpack_big(packet + *pos, &mx->preference) ; *pos += 2 ;
+ return s6dns_message_get_domain(&mx->exchange, packet, packetlen, pos) ;
+}
diff --git a/src/libs6dns/s6dns_message_get_soa.c b/src/libs6dns/s6dns_message_get_soa.c
new file mode 100644
index 0000000..c766231
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_soa.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint32.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_get_soa (s6dns_message_rr_soa_t *soa, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ if (!s6dns_message_get_domain(&soa->mname, packet, packetlen, pos)) return 0 ;
+ if (!s6dns_message_get_domain(&soa->rname, packet, packetlen, pos)) return 0 ;
+ if (*pos + 20 > packetlen) return (errno = EPROTO, 0) ;
+ uint32_unpack_big(packet + *pos, &soa->serial) ; *pos += 4 ;
+ uint32_unpack_big(packet + *pos, &soa->refresh) ; *pos += 4 ;
+ uint32_unpack_big(packet + *pos, &soa->retry) ; *pos += 4 ;
+ uint32_unpack_big(packet + *pos, &soa->expire) ; *pos += 4 ;
+ uint32_unpack_big(packet + *pos, &soa->minimum) ; *pos += 4 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_get_srv.c b/src/libs6dns/s6dns_message_get_srv.c
new file mode 100644
index 0000000..13ecf13
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_srv.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_get_srv (s6dns_message_rr_srv_t *srv, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ if (*pos + 7 > packetlen) return (errno = EPROTO, 0) ;
+ uint16_unpack_big(packet + *pos, &srv->priority) ; *pos += 2 ;
+ uint16_unpack_big(packet + *pos, &srv->weight) ; *pos += 2 ;
+ uint16_unpack_big(packet + *pos, &srv->port) ; *pos += 2 ;
+ return s6dns_message_get_domain(&srv->target, packet, packetlen, pos) ;
+}
diff --git a/src/libs6dns/s6dns_message_get_string.c b/src/libs6dns/s6dns_message_get_string.c
new file mode 100644
index 0000000..706da39
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_string.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include "s6dns-message-internal.h"
+
+int s6dns_message_get_string (s6dns_domain_t *d, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ register int r = s6dns_message_get_string_internal(d->s, 255, packet, packetlen, pos) ;
+ if (r < 0) return 0 ;
+ d->len = r ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_get_string_internal.c b/src/libs6dns/s6dns_message_get_string_internal.c
new file mode 100644
index 0000000..4f6eb0c
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_string_internal.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/bytestr.h>
+#include "s6dns-message-internal.h"
+
+int s6dns_message_get_string_internal (char *s, unsigned int max, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ register unsigned char len = ((unsigned char const *)packet)[*pos] ;
+ if (*pos + len + 1 > packetlen) return (errno = EPROTO, -1) ;
+ if (len > max) return (errno = ENAMETOOLONG, -1) ;
+ byte_copy(s, len, packet + *pos + 1) ;
+ *pos += len + 1 ;
+ return (int)len ;
+}
diff --git a/src/libs6dns/s6dns_message_get_strings.c b/src/libs6dns/s6dns_message_get_strings.c
new file mode 100644
index 0000000..161b554
--- /dev/null
+++ b/src/libs6dns/s6dns_message_get_strings.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-message.h>
+#include "s6dns-message-internal.h"
+
+int s6dns_message_get_strings (char *s, unsigned int rdlength, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ unsigned int max = rdlength, len = 0 ;
+ while (rdlength)
+ {
+ register unsigned int start = *pos ;
+ register int r = s6dns_message_get_string_internal(s + len, max - len, packet, packetlen, pos) ;
+ if (r < 0) return -1 ;
+ len += r ; rdlength -= *pos - start ;
+ }
+ return (int)len ;
+}
diff --git a/src/libs6dns/s6dns_message_header_pack.c b/src/libs6dns/s6dns_message_header_pack.c
new file mode 100644
index 0000000..99ffa8a
--- /dev/null
+++ b/src/libs6dns/s6dns_message_header_pack.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <s6-dns/s6dns-message.h>
+
+void s6dns_message_header_pack (char *s, s6dns_message_header_t const *h)
+{
+ uint16_pack_big(s, h->id) ;
+ s[2] = (h->qr << 7) | (h->opcode << 3) | (h->aa << 2) | (h->tc << 1) | h->rd ;
+ s[3] = (h->z << 4) | h->rcode ;
+ s6dns_message_counts_pack(s+4, &h->counts) ;
+}
diff --git a/src/libs6dns/s6dns_message_header_unpack.c b/src/libs6dns/s6dns_message_header_unpack.c
new file mode 100644
index 0000000..ae2b35c
--- /dev/null
+++ b/src/libs6dns/s6dns_message_header_unpack.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <s6-dns/s6dns-message.h>
+
+void s6dns_message_header_unpack (char const *s, s6dns_message_header_t *h)
+{
+ uint16_unpack_big(s, &h->id) ;
+ h->qr = s[2] & 0x8000U ? 1 : 0 ;
+ h->opcode = (s[2] >> 3) & 15 ;
+ h->aa = s[2] & 4 ? 1 : 0 ;
+ h->tc = s[2] & 2 ? 1 : 0 ;
+ h->rd = s[2] & 1 ;
+ h->ra = s[3] & 0x8000U ? 1 : 0 ;
+ h->z = (s[3] >> 4) & 7 ;
+ h->rcode = s[3] & 15 ;
+ s6dns_message_counts_unpack(s+4, &h->counts) ;
+}
diff --git a/src/libs6dns/s6dns_message_header_zero.c b/src/libs6dns/s6dns_message_header_zero.c
new file mode 100644
index 0000000..5695e34
--- /dev/null
+++ b/src/libs6dns/s6dns_message_header_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-message.h>
+
+s6dns_message_header_t const s6dns_message_header_zero = S6DNS_MESSAGE_HEADER_ZERO ;
diff --git a/src/libs6dns/s6dns_message_parse.c b/src/libs6dns/s6dns_message_parse.c
new file mode 100644
index 0000000..f4af437
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse.c
@@ -0,0 +1,37 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse (s6dns_message_header_t *h, char const *packet, unsigned int packetlen, s6dns_message_rr_func_t_ref f, void *data)
+{
+ s6dns_message_counts_t counts ;
+ unsigned int pos ;
+ unsigned int section ;
+ if (!s6dns_message_parse_init(h, &counts, packet, packetlen, &pos)) return 0 ;
+ switch (h->rcode)
+ {
+ case 0 : break ;
+ case 1 : return (errno = EBADMSG, 0) ;
+ case 2 : return (errno = EBUSY, 0) ;
+ case 3 : return (errno = ENOENT, 0) ;
+ case 4 : return (errno = ENOTSUP, 0) ;
+ case 5 : return (errno = ECONNREFUSED, 0) ;
+ default: return (errno = EIO, 0) ;
+ }
+ section = s6dns_message_parse_skipqd(&counts, packet, packetlen, &pos) ;
+ while (section)
+ {
+ s6dns_message_rr_t rr ;
+ if (!s6dns_message_parse_getrr(&rr, packet, packetlen, &pos)) return 0 ;
+ if (rr.rclass == S6DNS_C_IN)
+ {
+ register int r = (*f)(&rr, packet, packetlen, pos, section, data) ;
+ if (r < 1) return r ;
+ }
+ section = s6dns_message_parse_next(&counts, &rr, packet, packetlen, &pos) ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_answer_a.c b/src/libs6dns/s6dns_message_parse_answer_a.c
new file mode 100644
index 0000000..152b947
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_answer_a.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_answer_a (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_A) && (rr->rdlength == 4))
+ {
+ stralloc *data = stuff ;
+ if (!stralloc_catb(data, packet+pos, 4)) return -1 ;
+ }
+ (void)packetlen ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_answer_aaaa.c b/src/libs6dns/s6dns_message_parse_answer_aaaa.c
new file mode 100644
index 0000000..f1e71e5
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_answer_aaaa.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_answer_aaaa (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_AAAA) && (rr->rdlength == 16))
+ {
+ stralloc *data = stuff ;
+ if (!stralloc_catb(data, packet + pos, 16)) return -1 ;
+ }
+ (void)packetlen ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_answer_domain.c b/src/libs6dns/s6dns_message_parse_answer_domain.c
new file mode 100644
index 0000000..d09abc9
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_answer_domain.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_answer_domain (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ s6dns_dpag_t *data = stuff ;
+ if ((section == 2) && (rr->rtype == data->rtype))
+ {
+ s6dns_domain_t d ;
+ register unsigned int start = pos ;
+ if (!s6dns_message_get_domain(&d, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ if (!genalloc_append(s6dns_domain_t, &data->ds, &d)) return -1 ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_answer_hinfo.c b/src/libs6dns/s6dns_message_parse_answer_hinfo.c
new file mode 100644
index 0000000..aa53601
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_answer_hinfo.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_answer_hinfo (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_HINFO))
+ {
+ genalloc *data = stuff ;
+ s6dns_message_rr_hinfo_t hinfo ;
+ register unsigned int start = pos ;
+ if (!s6dns_message_get_hinfo(&hinfo, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ if (!genalloc_append(s6dns_message_rr_hinfo_t, data, &hinfo)) return -1 ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_answer_mx.c b/src/libs6dns/s6dns_message_parse_answer_mx.c
new file mode 100644
index 0000000..32f43c9
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_answer_mx.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_answer_mx (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_MX))
+ {
+ genalloc *data = stuff ;
+ s6dns_message_rr_mx_t mx ;
+ register unsigned int start = pos ;
+ if (!s6dns_message_get_mx(&mx, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ if (!genalloc_append(s6dns_message_rr_mx_t, data, &mx)) return -1 ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_answer_soa.c b/src/libs6dns/s6dns_message_parse_answer_soa.c
new file mode 100644
index 0000000..6c7b668
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_answer_soa.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_answer_soa (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_SOA))
+ {
+ genalloc *data = stuff ;
+ s6dns_message_rr_soa_t soa ;
+ register unsigned int start = pos ;
+ if (!s6dns_message_get_soa(&soa, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ if (!genalloc_append(s6dns_message_rr_soa_t, data, &soa)) return -1 ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_answer_srv.c b/src/libs6dns/s6dns_message_parse_answer_srv.c
new file mode 100644
index 0000000..31be348
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_answer_srv.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_answer_srv (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ if ((section == 2) && (rr->rtype == S6DNS_T_SRV))
+ {
+ genalloc *data = stuff ;
+ s6dns_message_rr_srv_t srv ;
+ register unsigned int start = pos ;
+ if (!s6dns_message_get_srv(&srv, packet, packetlen, &pos)) return 0 ;
+ if (rr->rdlength != pos - start) return (errno = EPROTO, 0) ;
+ if (!genalloc_append(s6dns_message_rr_srv_t, data, &srv)) return -1 ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_answer_strings.c b/src/libs6dns/s6dns_message_parse_answer_strings.c
new file mode 100644
index 0000000..09470c7
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_answer_strings.c
@@ -0,0 +1,36 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_answer_strings (s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int pos, unsigned int section, void *stuff)
+{
+ s6dns_mpag_t_ref data = stuff ;
+ if ((section == 2) && (rr->rtype == data->rtype))
+ {
+ unsigned int base = data->sa.len ;
+ int wasnull = !data->sa.s ;
+ unsigned int start = pos ;
+ register int r ;
+ if (!stralloc_readyplus(&data->sa, rr->rdlength + 1)) return -1 ;
+ errno = EPROTO ;
+ r = s6dns_message_get_strings(data->sa.s + data->sa.len, rr->rdlength, packet, packetlen, &pos) ;
+ if ((r < 0) || (rr->rdlength != pos - start))
+ {
+ if (wasnull) stralloc_free(&data->sa) ; else data->sa.len = base ;
+ return 0 ;
+ }
+ if (!genalloc_append(unsigned int, &data->offsets, &data->sa.len))
+ {
+ if (wasnull) stralloc_free(&data->sa) ; else data->sa.len = base ;
+ return -1 ;
+ }
+ errno = 0 ;
+ data->sa.len += r ;
+ stralloc_0(&data->sa) ;
+ }
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_getrr.c b/src/libs6dns/s6dns_message_parse_getrr.c
new file mode 100644
index 0000000..8db0c5d
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_getrr.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_getrr (s6dns_message_rr_t_ref rr, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ if (!s6dns_message_get_domain(&rr->name, packet, packetlen, pos)) return 0 ;
+ if (*pos + 10 > packetlen) return (errno = EPROTO, 0) ;
+ uint16_unpack_big(packet + *pos, &rr->rtype) ; *pos += 2 ;
+ uint16_unpack_big(packet + *pos, &rr->rclass) ; *pos += 2 ;
+ uint32_unpack_big(packet + *pos, &rr->ttl) ; *pos += 4 ;
+ uint16_unpack_big(packet + *pos, &rr->rdlength) ; *pos += 2 ;
+ if (*pos + rr->rdlength > packetlen) return (errno = EPROTO, 0) ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_init.c b/src/libs6dns/s6dns_message_parse_init.c
new file mode 100644
index 0000000..3b4efc5
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_init.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <s6-dns/s6dns-message.h>
+
+int s6dns_message_parse_init (s6dns_message_header_t *h, s6dns_message_counts_t *counts, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ if (packetlen < 12) return (errno = EPROTO, 0) ;
+ s6dns_message_header_unpack(packet, h) ;
+ *counts = h->counts ;
+ *pos = 12 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_next.c b/src/libs6dns/s6dns_message_parse_next.c
new file mode 100644
index 0000000..a7bbce6
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_next.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <s6-dns/s6dns-message.h>
+
+unsigned int s6dns_message_parse_next (s6dns_message_counts_t *counts, s6dns_message_rr_t const *rr, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ *pos += rr->rdlength ;
+ (void)packet ; (void)packetlen ;
+ return s6dns_message_counts_next(counts) ;
+}
diff --git a/src/libs6dns/s6dns_message_parse_skipqd.c b/src/libs6dns/s6dns_message_parse_skipqd.c
new file mode 100644
index 0000000..9b8c222
--- /dev/null
+++ b/src/libs6dns/s6dns_message_parse_skipqd.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-message.h>
+#include "s6dns-message-internal.h"
+
+unsigned int s6dns_message_parse_skipqd (s6dns_message_counts_t *counts, char const *packet, unsigned int packetlen, unsigned int *pos)
+{
+ for (;;)
+ {
+ register unsigned int r = s6dns_message_counts_next(counts) ;
+ if (r != 1) return r ;
+ if (!s6dns_message_get_domain_internal(0, 255, packet, packetlen, pos)) return 0 ;
+ *pos += 4 ;
+ }
+}
diff --git a/src/libs6dns/s6dns_rci_free.c b/src/libs6dns/s6dns_rci_free.c
new file mode 100644
index 0000000..d742ffe
--- /dev/null
+++ b/src/libs6dns/s6dns_rci_free.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <s6-dns/s6dns-rci.h>
+
+void s6dns_rci_free (s6dns_rci_t *rci)
+{
+ stralloc_free(&rci->rules) ;
+ *rci = s6dns_rci_zero ;
+}
diff --git a/src/libs6dns/s6dns_rci_here.c b/src/libs6dns/s6dns_rci_here.c
new file mode 100644
index 0000000..2f08513
--- /dev/null
+++ b/src/libs6dns/s6dns_rci_here.c
@@ -0,0 +1,7 @@
+/* ISC license. */
+
+/* MT-unsafe */
+
+#include <s6-dns/s6dns-rci.h>
+
+s6dns_rci_t s6dns_rci_here = S6DNS_RCI_ZERO ;
diff --git a/src/libs6dns/s6dns_rci_init.c b/src/libs6dns/s6dns_rci_init.c
new file mode 100644
index 0000000..a4f3fcf
--- /dev/null
+++ b/src/libs6dns/s6dns_rci_init.c
@@ -0,0 +1,173 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/bitarray.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/ip46.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-rci.h>
+
+static unsigned int readit (char const *file, char *buf, unsigned int max)
+{
+ register int r = openreadnclose(file, buf, max - 1) ;
+ if (r < 0)
+ {
+ if (errno != ENOENT) return 0 ;
+ else r = 0 ;
+ }
+ buf[r++] = '\n' ;
+ return (unsigned int)r ;
+}
+
+static inline int s6dns_rci_init_servers (s6dns_rci_t *rci, char const *file, char *tmp, unsigned int max, unsigned int *size)
+{
+ ip46_t tmplist[S6DNS_MAX_SERVERS] ;
+ unsigned int num = 0 ;
+ char const *x = env_get("DNSCACHEIP") ;
+ if (x) ip46_scanlist(tmplist, S6DNS_MAX_SERVERS, x, &num) ;
+ if (!num)
+ {
+ unsigned int i = 0 ;
+ *size = readit(file, tmp, max) ;
+ if (!*size) return 0 ;
+ while ((i < *size) && (num < S6DNS_MAX_SERVERS))
+ {
+ register unsigned int j = byte_chr(tmp + i, *size - i, '\n') ;
+ if ((i + j < *size) && (j > 13U) && !byte_diff("nameserver", 10, tmp + i))
+ {
+ register unsigned int k = 0 ;
+ while ((tmp[i+10+k] == ' ') || (tmp[i+10+k] == '\t')) k++ ;
+ if (k && ip46_scan(tmp+i+10+k, tmplist + num)) num++ ;
+ }
+ i += j + 1 ;
+ }
+ }
+ if (!num)
+ {
+ num = 1 ;
+ byte_copy(tmplist[0].ip, SKALIBS_IP_SIZE, S6DNS_LOCALHOST_IP) ;
+#ifdef SKALIBS_IPV6_ENABLED
+ tmplist[0].is6 = 1 ;
+#endif
+ }
+
+ {
+ register unsigned int i = 0 ;
+ byte_zero(&rci->servers, sizeof(s6dns_ip46list_t)) ;
+ for (; i < num ; i++)
+ {
+ byte_copy(rci->servers.ip + SKALIBS_IP_SIZE * i, SKALIBS_IP_SIZE, tmplist[i].ip) ;
+#ifdef SKALIBS_IPV6_ENABLED
+ if (ip46_is6(tmplist+i)) bitarray_set(rci->servers.is6, i) ;
+#endif
+ }
+ }
+ return 1 ;
+}
+
+static inline int stringrules (stralloc *rules, char const *s, unsigned int *num)
+{
+ unsigned int n = 0 ;
+ int crunching = 1 ;
+ int wasnull = !rules->s ;
+ unsigned int base = rules->len ;
+ char c = ' ' ;
+ while (c)
+ {
+ c = *s++ ;
+ if (byte_chr(" \t\n\r", 5, c) < 5)
+ {
+ if (!crunching)
+ {
+ if ((rules->s[rules->len - 1] != '.') && !stralloc_catb(rules, ".", 1)) goto err ;
+ if (!stralloc_0(rules)) goto err ;
+ n++ ;
+ crunching = 1 ;
+ }
+ }
+ else
+ {
+ if (crunching) crunching = 0 ;
+ if (!stralloc_catb(rules, &c, 1)) goto err ;
+ }
+ }
+ *num += n ;
+ return 1 ;
+
+ err:
+ if (wasnull) stralloc_free(rules) ;
+ else rules->len = base ;
+ return 0 ;
+}
+
+static inline int s6dns_rci_init_rules (s6dns_rci_t_ref rci, char const *file, char *tmp, unsigned int max, unsigned int *size)
+{
+ unsigned int num = 0 ;
+ char const *x = env_get("DNSQUALIFY") ;
+ if (x)
+ {
+ if (!stringrules(&rci->rules, x, &num)) return 0 ;
+ }
+ else
+ {
+ unsigned int i = 0 ;
+ if (!*size)
+ {
+ *size = readit(file, tmp, max) ;
+ if (!*size) return 0 ;
+ }
+ while (i < *size)
+ {
+ register unsigned int j = byte_chr(tmp + i, *size - i, '\n') ;
+ if ((i + j < *size) && (j > 8U)
+ && (!byte_diff("domain", 6, tmp + i) || !byte_diff("search", 6, tmp + i))
+ && ((tmp[i+6] == ' ') || (tmp[i+6] == '\t') || (tmp[i+6] == '\r')))
+ {
+ unsigned int k = 0 ;
+ int copying = 0 ;
+ register int notsearching = (tmp[i] != 's') ;
+ if (!stralloc_readyplus(&rci->rules, j)) return 0 ;
+ for (; 6 + k < j ; k++)
+ {
+ char c = tmp[i+7+k] ;
+ if ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n'))
+ {
+ if (copying)
+ {
+ copying = 0 ;
+ if ((tmp[i+6+k] != '.') && !stralloc_catb(&rci->rules, ".", 1)) return 0 ;
+ if (!stralloc_0(&rci->rules)) return 0 ;
+ num++ ;
+ if (notsearching) break ;
+ }
+ }
+ else
+ {
+ copying = 1 ;
+ if (!stralloc_catb(&rci->rules, &c, 1)) return 0 ;
+ }
+ }
+ }
+ i += j + 1 ;
+ }
+ }
+ if (!stralloc_0(&rci->rules)) return 0 ; /* empty rule to finish */
+ num++ ;
+ rci->rulesnum = num ;
+ stralloc_shrink(&rci->rules) ;
+ return 1 ;
+}
+
+int s6dns_rci_init (s6dns_rci_t *rci, char const *file)
+{
+ char tmp[4096] ;
+ unsigned int size = 0 ;
+ if (!s6dns_rci_init_servers(rci, file, tmp, 4096, &size)) return 0 ;
+ if (!s6dns_rci_init_rules(rci, file, tmp, 4096, &size)) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6dns/s6dns_rci_zero.c b/src/libs6dns/s6dns_rci_zero.c
new file mode 100644
index 0000000..384b42a
--- /dev/null
+++ b/src/libs6dns/s6dns_rci_zero.c
@@ -0,0 +1,7 @@
+/* ISC license. */
+
+/* MT-unsafe */
+
+#include <s6-dns/s6dns-rci.h>
+
+s6dns_rci_t const s6dns_rci_zero = S6DNS_RCI_ZERO ;
diff --git a/src/libs6dns/s6dns_resolve_core.c b/src/libs6dns/s6dns_resolve_core.c
new file mode 100644
index 0000000..0b75f07
--- /dev/null
+++ b/src/libs6dns/s6dns_resolve_core.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolve_core_r (s6dns_domain_t const *d, uint16 qtype, s6dns_engine_t *dt, s6dns_ip46list_t const *servers, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ return s6dns_engine_init_r(dt, servers, S6DNS_O_RECURSIVE, d->s, d->len, qtype, dbh, deadline, stamp)
+ && s6dns_resolve_loop_r(dt, deadline, stamp) ;
+}
diff --git a/src/libs6dns/s6dns_resolve_dpag.c b/src/libs6dns/s6dns_resolve_dpag.c
new file mode 100644
index 0000000..7c735b8
--- /dev/null
+++ b/src/libs6dns/s6dns_resolve_dpag.c
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-rci.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolve_dpag_r (genalloc *ds, char const *name, unsigned int len, uint16 qtype, int qualif, s6dns_engine_t *dt, s6dns_rci_t const *rci, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ s6dns_dpag_t data ;
+ register int r ;
+ data.ds = *ds ;
+ data.rtype = qtype ;
+ r = s6dns_resolve_r(name, len, qtype, &s6dns_message_parse_answer_domain, &data, qualif, dt, rci, dbh, deadline, stamp) ;
+ *ds = data.ds ;
+ return r ;
+}
diff --git a/src/libs6dns/s6dns_resolve_mpag.c b/src/libs6dns/s6dns_resolve_mpag.c
new file mode 100644
index 0000000..a6e04cb
--- /dev/null
+++ b/src/libs6dns/s6dns_resolve_mpag.c
@@ -0,0 +1,23 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-rci.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolve_mpag_r (stralloc *sa, genalloc *offsets, char const *name, unsigned int len, uint16 qtype, s6dns_message_rr_func_t_ref parsefunc, int qualif, s6dns_engine_t *dt, s6dns_rci_t const *rci, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ s6dns_mpag_t data ;
+ register int r ;
+ data.sa = *sa ;
+ data.offsets = *offsets ;
+ data.rtype = qtype ;
+ r = s6dns_resolve_r(name, len, qtype, parsefunc, &data, qualif, dt, rci, dbh, deadline, stamp) ;
+ *sa = data.sa ;
+ *offsets = data.offsets ;
+ return r ;
+}
diff --git a/src/libs6dns/s6dns_resolve_name4.c b/src/libs6dns/s6dns_resolve_name4.c
new file mode 100644
index 0000000..7bb504a
--- /dev/null
+++ b/src/libs6dns/s6dns_resolve_name4.c
@@ -0,0 +1,23 @@
+/* ISC license. */
+
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolve_name4_r (genalloc *list, char const *ip, s6dns_engine_t *dt, s6dns_ip46list_t const *servers, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ s6dns_dpag_t data ;
+ s6dns_domain_t d ;
+ register int r ;
+ s6dns_domain_arpafromip4(&d, ip) ;
+ s6dns_domain_encode(&d) ;
+ data.ds = *list ;
+ data.rtype = S6DNS_T_PTR ;
+ r = s6dns_resolve_parse_r(&d, data.rtype, &s6dns_message_parse_answer_domain, &data, dt, servers, dbh, deadline, stamp) ;
+ *list = data.ds ;
+ return r ;
+}
diff --git a/src/libs6dns/s6dns_resolve_name6.c b/src/libs6dns/s6dns_resolve_name6.c
new file mode 100644
index 0000000..1af3945
--- /dev/null
+++ b/src/libs6dns/s6dns_resolve_name6.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolve_name6_r (genalloc *list, char const *ip, s6dns_engine_t *dt, s6dns_ip46list_t const *servers, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ s6dns_dpag_t data ;
+ s6dns_domain_t d ;
+ register int r ;
+ s6dns_domain_arpafromip6(&d, ip, 128) ;
+ s6dns_domain_encode(&d) ;
+ data.ds = *list ;
+ data.rtype = S6DNS_T_PTR ;
+ r = s6dns_resolve_parse_r(&d, data.rtype, &s6dns_message_parse_answer_domain, &data, dt, servers, dbh, deadline, stamp) ;
+ *list = data.ds ;
+ return r ;
+}
diff --git a/src/libs6dns/s6dns_resolve_parse.c b/src/libs6dns/s6dns_resolve_parse.c
new file mode 100644
index 0000000..b00804f
--- /dev/null
+++ b/src/libs6dns/s6dns_resolve_parse.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolve_parse_r (s6dns_domain_t const *d, uint16 qtype, s6dns_message_rr_func_t_ref parsefunc, void *data, s6dns_engine_t *dt, s6dns_ip46list_t const *servers, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ register int r ;
+ if (!s6dns_resolve_core_r(d, qtype, dt, servers, dbh, deadline, stamp)) return -1 ;
+ {
+ s6dns_message_header_t h ;
+ r = s6dns_message_parse(&h, s6dns_engine_packet(dt), s6dns_engine_packetlen(dt), parsefunc, data) ;
+ }
+ s6dns_engine_recycle(dt) ;
+ return r ;
+}
diff --git a/src/libs6dns/s6dns_resolven_loop.c b/src/libs6dns/s6dns_resolven_loop.c
new file mode 100644
index 0000000..6e0c3b4
--- /dev/null
+++ b/src/libs6dns/s6dns_resolven_loop.c
@@ -0,0 +1,54 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-resolve.h>
+
+ /*
+ This is basically a synchronous interface to s6dns_engine.
+ It resolves n dts at the same time.
+ */
+
+int s6dns_resolven_loop (s6dns_engine_t *dt, unsigned int n, unsigned int or, tain_t const *deadline, tain_t *stamp)
+{
+ iopause_fd x[n] ;
+ unsigned int count = 0 ;
+ for (;;)
+ {
+ tain_t localdeadline = *deadline ;
+ register int r ;
+ register unsigned int i = 0 ;
+ register unsigned int j = 0 ;
+ for (; i < n ; i++) if (dt[i].status == EAGAIN)
+ {
+ s6dns_engine_nextdeadline(dt + i, &localdeadline) ;
+ x[j].fd = dt[i].fd ;
+ x[j].events = (s6dns_engine_isreadable(dt + i) ? IOPAUSE_READ : 0) | (s6dns_engine_iswritable(dt + i) ? IOPAUSE_WRITE : 0) ;
+ j++ ;
+ }
+ if (!j) break ;
+ r = iopause_stamp(x, j, &localdeadline, stamp) ;
+ if (r < 0) return -1 ;
+ else if (!r)
+ {
+ if (tain_less(deadline, stamp)) return (errno = ETIMEDOUT, -1) ;
+ for (i = 0 ; i < n ; i++) if (dt[i].status == EAGAIN)
+ if (s6dns_engine_timeout(dt + i, stamp) && (or >= 2)) return i ;
+ }
+ else
+ {
+ for (i = 0 ; i < n ; i++) if (dt[i].status == EAGAIN)
+ {
+ r = s6dns_engine_event(dt + i, stamp) ;
+ if (or)
+ {
+ if (r && ((r > 0) || (or >= 2))) return i ;
+ }
+ else if (r > 0) count++ ;
+ }
+ }
+ }
+ return or ? (errno = ENOENT, -1) : (int)count ;
+}
diff --git a/src/libs6dns/s6dns_resolven_parse.c b/src/libs6dns/s6dns_resolven_parse.c
new file mode 100644
index 0000000..78289bc
--- /dev/null
+++ b/src/libs6dns/s6dns_resolven_parse.c
@@ -0,0 +1,47 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolven_parse_r (s6dns_resolve_t *list, unsigned int n, s6dns_ip46list_t const *servers, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ s6dns_engine_t dtl[n] ;
+ register unsigned int i = 0 ;
+ for (; i < n ; i++) list[i].status = ECONNABORTED ;
+ for (i = 0 ; i < n ; i++)
+ {
+ dtl[i] = s6dns_engine_zero ;
+ if (!s6dns_engine_init_r(dtl + i, servers, list[i].options, list[i].q.s, list[i].q.len, list[i].qtype, dbh, &list[i].deadline, stamp))
+ {
+ list[i].status = errno ;
+ s6dns_engine_freen(dtl, i) ;
+ return 0 ;
+ }
+ list[i].status = EAGAIN ;
+ }
+
+ if (s6dns_resolven_loop(dtl, n, 0, deadline, stamp) < 0) goto err ;
+
+ for (i = 0 ; i < n ; i++)
+ {
+ if (dtl[i].status) list[i].status = dtl[i].status ;
+ else
+ {
+ s6dns_message_header_t h ;
+ register int r = s6dns_message_parse(&h, s6dns_engine_packet(dtl + i), s6dns_engine_packetlen(dtl + i), list[i].parsefunc, list[i].data) ;
+ if (r < 0) goto err ;
+ list[i].status = r ? 0 : errno ;
+ }
+ }
+ s6dns_engine_freen(dtl, n) ;
+ return 1 ;
+
+ err:
+ s6dns_engine_freen(dtl, n) ;
+ return 0 ;
+}
diff --git a/src/libs6dns/s6dns_resolvenoq.c b/src/libs6dns/s6dns_resolvenoq.c
new file mode 100644
index 0000000..3462076
--- /dev/null
+++ b/src/libs6dns/s6dns_resolvenoq.c
@@ -0,0 +1,16 @@
+ /* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolvenoq_r (char const *name, unsigned int len, uint16 qtype, s6dns_message_rr_func_t_ref parsefunc, void *data, s6dns_engine_t *dt, s6dns_ip46list_t const *servers, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ s6dns_domain_t d ;
+ if (!s6dns_domain_fromstring_noqualify_encode(&d, name, len)) return -1 ;
+ return s6dns_resolve_parse_r(&d, qtype, parsefunc, data, dt, servers, dbh, deadline, stamp) ;
+}
diff --git a/src/libs6dns/s6dns_resolvenoq_aaaaa.c b/src/libs6dns/s6dns_resolvenoq_aaaaa.c
new file mode 100644
index 0000000..b280fe0
--- /dev/null
+++ b/src/libs6dns/s6dns_resolvenoq_aaaaa.c
@@ -0,0 +1,50 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/tai.h>
+#include <skalibs/ip46.h>
+#include <s6-dns/s6dns-constants.h>
+#include <s6-dns/s6dns-ip46.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolvenoq_aaaaa_r (genalloc *ips, char const *name, unsigned int len, s6dns_ip46list_t const *servers, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ stralloc sa[2] = { STRALLOC_ZERO, STRALLOC_ZERO } ;
+ s6dns_resolve_t blob[2] ;
+ if (!s6dns_domain_fromstring_noqualify_encode(&blob[0].q, name, len)) return -1 ;
+ blob[0].qtype = S6DNS_T_AAAA ;
+ blob[0].options = S6DNS_O_RECURSIVE ;
+ blob[0].deadline = *deadline ;
+ blob[0].parsefunc = &s6dns_message_parse_answer_aaaa ;
+ blob[0].data = &sa[0] ;
+ blob[1].q = blob[0].q ;
+ blob[1].qtype = S6DNS_T_A ;
+ blob[1].options = S6DNS_O_RECURSIVE ;
+ blob[1].deadline = *deadline ;
+ blob[1].parsefunc = &s6dns_message_parse_answer_a ;
+ blob[1].data = &sa[1] ;
+ if (!s6dns_resolven_parse_r(blob, 2, servers, dbh, deadline, stamp)) return -1 ;
+ if (blob[0].status && blob[1].status) return (errno = blob[1].status, 0) ;
+ if (!genalloc_readyplus(ip46_t, ips, (sa[0].len >> 4) + (sa[1].len >> 2)))
+ {
+ stralloc_free(&sa[0]) ;
+ stralloc_free(&sa[1]) ;
+ return -1 ;
+ }
+ {
+ unsigned int n = genalloc_len(ip46_t, ips) ;
+ int e = (!!sa[0].len << 1) | !!sa[1].len ;
+ register unsigned int i = 0 ;
+ for (; i < (sa[0].len >> 4) ; i++)
+ ip46_from_ip6(genalloc_s(ip46_t, ips) + n++, sa[0].s + (i << 4)) ;
+ for (i = 0 ; i < (sa[1].len >> 2) ; i++)
+ ip46_from_ip4(genalloc_s(ip46_t, ips) + n++, sa[1].s + (i << 2)) ;
+ genalloc_setlen(ip46_t, ips, n) ;
+ stralloc_free(&sa[0]) ;
+ stralloc_free(&sa[1]) ;
+ return e ;
+ }
+}
diff --git a/src/libs6dns/s6dns_resolveq.c b/src/libs6dns/s6dns_resolveq.c
new file mode 100644
index 0000000..c43db1f
--- /dev/null
+++ b/src/libs6dns/s6dns_resolveq.c
@@ -0,0 +1,89 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/error.h>
+#include <skalibs/tai.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-rci.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolveq_r (char const *name, unsigned int len, uint16 qtype, s6dns_message_rr_func_t_ref parsefunc, void *data, s6dns_rci_t const *rci, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ s6dns_engine_t dtl[rci->rulesnum] ;
+ unsigned int best = 0 ;
+ unsigned int n ;
+ int e = 0 ;
+ register unsigned int i = 0 ;
+ {
+ s6dns_domain_t domains[rci->rulesnum] ;
+ n = s6dns_domain_fromstring_qualify_encode(domains, name, len, rci->rules.s, rci->rulesnum) ;
+ if (!n) return -1 ;
+ for (; i < n ; i++)
+ {
+ dtl[i] = s6dns_engine_zero ;
+ if (!s6dns_engine_init_r(dtl + i, &rci->servers, S6DNS_O_RECURSIVE, domains[i].s, domains[i].len, qtype, dbh, deadline, stamp))
+ {
+ s6dns_engine_freen(dtl, i) ;
+ return -1 ;
+ }
+ }
+ }
+
+ /*
+ Wait until the "best" answer arrives, then scan until a positive answer
+ is found.
+
+ dtl[i].status == EAGAIN : query still pending
+ other nonzero dtl[i].status : error, give up
+ dtl[i].status == 0 : answer #i has arrived, in which case parse it;
+ r < 0 : error, give up
+ r > 0 : positive answer, return it
+ r == 0 : negative answer. If it's non-fatal (i.e. NXDOMAIN),
+ then move on to the next best FQDN.
+ */
+
+ for (;;)
+ {
+ register int k = s6dns_resolven_loop(dtl, n, 1, deadline, stamp) ;
+ if (k < 0) goto err ;
+ if ((unsigned int)k == best)
+ {
+ for (;; best++)
+ {
+ s6dns_message_header_t h ;
+ register int r ;
+ if (best >= n) goto notfound ;
+ if (error_isagain(dtl[best].status)) break ;
+ if (dtl[best].status) { errno = dtl[best].status ; goto err ; }
+ r = s6dns_message_parse(&h, s6dns_engine_packet(dtl + best), s6dns_engine_packetlen(dtl + best), parsefunc, data) ;
+ if (r < 0) goto err ;
+ else if (r) goto found ;
+ else switch (errno)
+ {
+ case EBUSY :
+ case ENOENT :
+ case ECONNREFUSED :
+ case EIO :
+ break ;
+ default : goto err ;
+ }
+ if (!best) e = errno ;
+ }
+ }
+ }
+
+ found:
+ s6dns_engine_freen(dtl, n) ;
+ return 1 ;
+
+ notfound:
+ s6dns_engine_freen(dtl, n) ;
+ return (errno = e, 0) ;
+
+ err:
+ s6dns_engine_freen(dtl, n) ;
+ return -1 ;
+}
diff --git a/src/libs6dns/s6dns_resolveq_aaaaa.c b/src/libs6dns/s6dns_resolveq_aaaaa.c
new file mode 100644
index 0000000..825e68e
--- /dev/null
+++ b/src/libs6dns/s6dns_resolveq_aaaaa.c
@@ -0,0 +1,96 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/error.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/tai.h>
+#include <skalibs/ip46.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+#include <s6-dns/s6dns-engine.h>
+#include <s6-dns/s6dns-rci.h>
+#include <s6-dns/s6dns-resolve.h>
+
+int s6dns_resolveq_aaaaa_r (genalloc *ips, char const *name, unsigned int len, s6dns_rci_t const *rci, s6dns_debughook_t const *dbh, tain_t const *deadline, tain_t *stamp)
+{
+ s6dns_engine_t dtl[rci->rulesnum << 1] ;
+ stralloc data = STRALLOC_ZERO ;
+ unsigned int best = 0 ;
+ unsigned int n ;
+ int e = 0 ;
+ register unsigned int i = 0 ;
+ {
+ s6dns_domain_t domains[rci->rulesnum] ;
+ n = s6dns_domain_fromstring_qualify_encode(domains, name, len, rci->rules.s, rci->rulesnum) ;
+ if (!n) return -1 ;
+ for (; i < n ; i++)
+ {
+ dtl[i<<1] = s6dns_engine_zero ;
+ if (!s6dns_engine_init_r(dtl + (i<<1), &rci->servers, S6DNS_O_RECURSIVE, domains[i].s, domains[i].len, S6DNS_T_AAAA, dbh, deadline, stamp))
+ {
+ s6dns_engine_freen(dtl, i<<1) ;
+ return -1 ;
+ }
+ dtl[(i<<1)+1] = s6dns_engine_zero ;
+ if (!s6dns_engine_init_r(dtl + (i<<1) + 1, &rci->servers, S6DNS_O_RECURSIVE, domains[i].s, domains[i].len, S6DNS_T_A, dbh, deadline, stamp))
+ {
+ s6dns_engine_freen(dtl, (i<<1)+1) ;
+ return -1 ;
+ }
+ }
+ }
+
+ for (;;)
+ {
+ register int k = s6dns_resolven_loop(dtl, n << 1, 1, deadline, stamp) ;
+ if (k < 0) goto err ;
+ if ((unsigned int)k == best)
+ {
+ for (;; best++)
+ {
+ s6dns_message_header_t h ;
+ register int r ;
+ if (best >= n << 1) goto notfound ;
+ if (error_isagain(dtl[best].status)) break ;
+ if (dtl[best].status) { errno = dtl[best].status ; goto err ; }
+ r = s6dns_message_parse(&h, s6dns_engine_packet(dtl + best), s6dns_engine_packetlen(dtl + best), (best & 1) ? &s6dns_message_parse_answer_a : s6dns_message_parse_answer_aaaa, &data) ;
+ if (r < 0) goto err ;
+ else if (r) goto found ;
+ else switch (errno)
+ {
+ case EBUSY :
+ case ENOENT :
+ case ECONNREFUSED :
+ case EIO :
+ break ;
+ default : goto err ;
+ }
+ if (!best) e = errno ;
+ }
+ }
+ }
+
+ found:
+ s6dns_engine_freen(dtl, n<<1) ;
+ {
+ register unsigned int len = data.len >> ((best & 1) ? 2 : 4) ;
+ register unsigned int i = 0 ;
+ register unsigned int base = genalloc_len(ip46_t, ips) ;
+ if (!genalloc_readyplus(ip46_t, ips, len)) return -1 ;
+ for (; i < len ; i++)
+ ip46_from_ip(genalloc_s(ip46_t, ips) + base + i, data.s + (i << ((best & 1) ? 2 : 4)), !(best & 1)) ;
+ genalloc_setlen(ip46_t, ips, base + len) ;
+ }
+ stralloc_free(&data) ;
+ return 1 ;
+
+ notfound:
+ s6dns_engine_freen(dtl, n<<1) ;
+ return (errno = e, 0) ;
+
+ err:
+ s6dns_engine_freen(dtl, n<<1) ;
+ return -1 ;
+}
diff --git a/src/skadns/deps-exe/skadnsd b/src/skadns/deps-exe/skadnsd
new file mode 100755
index 0000000..4181fd6
--- /dev/null
+++ b/src/skadns/deps-exe/skadnsd
@@ -0,0 +1,4 @@
+-ls6dns
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/skadns/deps-lib/skadns b/src/skadns/deps-lib/skadns
new file mode 100644
index 0000000..570a6f5
--- /dev/null
+++ b/src/skadns/deps-lib/skadns
@@ -0,0 +1,10 @@
+skadns_cancel.o
+skadns_end.o
+skadns_packet.o
+skadns_packetlen.o
+skadns_release.o
+skadns_send.o
+skadns_start.o
+skadns_startf.o
+skadns_update.o
+skadns_zero.o
diff --git a/src/skadns/skadns_cancel.c b/src/skadns/skadns_cancel.c
new file mode 100644
index 0000000..13db918
--- /dev/null
+++ b/src/skadns/skadns_cancel.c
@@ -0,0 +1,23 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/error.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6-dns/skadns.h>
+
+int skadns_cancel (skadns_t *a, uint16 id, tain_t const *deadline, tain_t *stamp)
+{
+ char pack[3] = "--q" ;
+ char err ;
+ register skadnsanswer_t *p = GENSETDYN_P(skadnsanswer_t, &a->q, id) ;
+ if (!error_isagain(p->status)) return skadns_release(a, id) ;
+ uint16_pack_big(pack, id) ;
+ if (!skaclient_send(&a->connection, pack, 3, &skaclient_default_cb, &err, deadline, stamp)) return 0 ;
+ if (!err) return gensetdyn_delete(&a->q, id) ;
+ if (err != ENOENT) return (errno = err, 0) ;
+ p->status = ECANCELED ;
+ return 1 ;
+}
diff --git a/src/skadns/skadns_end.c b/src/skadns/skadns_end.c
new file mode 100644
index 0000000..6f5f01b
--- /dev/null
+++ b/src/skadns/skadns_end.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <skalibs/alloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6-dns/skadns.h>
+
+static int skadnsanswer_free (char *p, void *stuff)
+{
+ register skadnsanswer_t *q = (skadnsanswer_t_ref)p ;
+ alloc_free(&q->data) ;
+ (void)stuff ;
+ return 1 ;
+}
+
+void skadns_end (skadns_t *a)
+{
+ skaclient_end(&a->connection) ;
+ genalloc_free(uint16, &a->list) ;
+ gensetdyn_iter(&a->q, &skadnsanswer_free, 0) ;
+ gensetdyn_free(&a->q) ;
+ *a = skadns_zero ;
+}
diff --git a/src/skadns/skadns_packet.c b/src/skadns/skadns_packet.c
new file mode 100644
index 0000000..c34b3a1
--- /dev/null
+++ b/src/skadns/skadns_packet.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/gensetdyn.h>
+#include <s6-dns/skadns.h>
+
+char const *skadns_packet (skadns_t const *a, uint16 id)
+{
+ register skadnsanswer_t *p = GENSETDYN_P(skadnsanswer_t, &a->q, id) ;
+ switch (p->status)
+ {
+ case 0 : return (char const *)p->data ;
+ default : return (errno = p->status, (char const *)0) ;
+ }
+}
diff --git a/src/skadns/skadns_packetlen.c b/src/skadns/skadns_packetlen.c
new file mode 100644
index 0000000..34cbb28
--- /dev/null
+++ b/src/skadns/skadns_packetlen.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/gensetdyn.h>
+#include <s6-dns/skadns.h>
+
+int skadns_packetlen (skadns_t const *a, uint16 id)
+{
+ register skadnsanswer_t *p = GENSETDYN_P(skadnsanswer_t, &a->q, id) ;
+ switch (p->status)
+ {
+ case 0 : return p->len ;
+ default : return (errno = p->status, -1) ;
+ }
+}
diff --git a/src/skadns/skadns_release.c b/src/skadns/skadns_release.c
new file mode 100644
index 0000000..9fba737
--- /dev/null
+++ b/src/skadns/skadns_release.c
@@ -0,0 +1,26 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/alloc.h>
+#include <skalibs/gensetdyn.h>
+#include <s6-dns/skadns.h>
+
+int skadns_release (skadns_t *a, uint16 id)
+{
+ register skadnsanswer_t *p = GENSETDYN_P(skadnsanswer_t, &a->q, id) ;
+ switch (p->status)
+ {
+ case 0 :
+ alloc_free(p->data) ; p->data = 0 ; p->len = 0 ;
+ break ;
+ case EAGAIN :
+ case ECANCELED :
+ return (errno = EBUSY, 0) ;
+ case EINVAL :
+ return (errno = EINVAL, 0) ;
+ default : break ;
+ }
+ p->status = EINVAL ;
+ return gensetdyn_delete(&a->q, id) ;
+}
diff --git a/src/skadns/skadns_send.c b/src/skadns/skadns_send.c
new file mode 100644
index 0000000..7ee04c7
--- /dev/null
+++ b/src/skadns/skadns_send.c
@@ -0,0 +1,41 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/siovec.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/skadns.h>
+
+static skadnsanswer_t const skadnsanswer_initial = { .status = EAGAIN, .data = 0, .len = 0 } ;
+
+int skadns_send (skadns_t *a, uint16 *u, s6dns_domain_t const *d, uint16 qtype, tain_t const *limit, tain_t const *deadline, tain_t *stamp)
+{
+ unsigned int i ;
+ char tmp[17] = "--Q" ;
+ char err ;
+ siovec_t v[2] = { { .s = tmp, .len = 17 }, { .s = d->s, .len = d->len } } ;
+ if (!gensetdyn_new(&a->q, &i)) return 0 ;
+ uint16_pack_big(tmp, (uint16)i) ;
+ uint16_pack_big(tmp + 3, qtype) ;
+ if (limit) tain_pack(tmp + 5, limit) ; else byte_zero(tmp + 5, 12) ;
+ if (!skaclient_sendv(&a->connection, v, 2, &skaclient_default_cb, &err, deadline, stamp))
+ {
+ register int e = errno ;
+ gensetdyn_delete(&a->q, i) ;
+ errno = e ;
+ return 0 ;
+ }
+ if (err)
+ {
+ gensetdyn_delete(&a->q, i) ;
+ return (errno = err, 0) ;
+ }
+ *GENSETDYN_P(skadnsanswer_t, &a->q, i) = skadnsanswer_initial ;
+ *u = i ;
+ return 1 ;
+}
diff --git a/src/skadns/skadns_start.c b/src/skadns/skadns_start.c
new file mode 100644
index 0000000..3dccb54
--- /dev/null
+++ b/src/skadns/skadns_start.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <s6-dns/skadns.h>
+
+int skadns_start (skadns_t *a, char const *path, tain_t const *deadline, tain_t *stamp)
+{
+ return skaclient_start_b(&a->connection, &a->buffers, path, SKADNS_BANNER1, SKADNS_BANNER1_LEN, SKADNS_BANNER2, SKADNS_BANNER2_LEN, deadline, stamp) ;
+}
diff --git a/src/skadns/skadns_startf.c b/src/skadns/skadns_startf.c
new file mode 100644
index 0000000..ac19a0c
--- /dev/null
+++ b/src/skadns/skadns_startf.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/environ.h>
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <s6-dns/skadns.h>
+
+int skadns_startf (skadns_t *a, tain_t const *deadline, tain_t *stamp)
+{
+ static char const *const cargv[2] = { SKADNSD_PROG, 0 } ;
+ return skaclient_startf_b(&a->connection, &a->buffers, cargv[0], cargv, (char const *const *)environ, SKACLIENT_OPTION_WAITPID, SKADNS_BANNER1, SKADNS_BANNER1_LEN, SKADNS_BANNER2, SKADNS_BANNER2_LEN, deadline, stamp) ;
+}
diff --git a/src/skadns/skadns_update.c b/src/skadns/skadns_update.c
new file mode 100644
index 0000000..50f3611
--- /dev/null
+++ b/src/skadns/skadns_update.c
@@ -0,0 +1,45 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/alloc.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <s6/skadns.h>
+
+static int msghandler (unixmessage_t const *m, void *context)
+{
+ skadns_t *a = (skadns_t *)context ;
+ skadnsanswer_t *p ;
+ uint16 id ;
+ if (m->len < 3 || m->nfds) return (errno = EPROTO, 0) ;
+ uint16_unpack_big(m->s, &id) ;
+ p = GENSETDYN_P(skadnsanswer_t, &a->q, id) ;
+ if (p->status == ECANCELED)
+ {
+ p->status = EINVAL ;
+ return gensetdyn_delete(&a->q, id) ;
+ }
+ if (!error_isagain(p->status)) return (errno = EINVAL, 0) ;
+ if (!genalloc_readyplus(uint16, &a->list, 1)) return 0 ;
+ if (!m->s[2])
+ {
+ p->data = alloc(m->len-3) ;
+ if (!p->data) return 0 ;
+ byte_copy(p->data, m->len-3, m->s+3) ;
+ p->len = m->len-3 ;
+ }
+ p->status = m->s[2] ;
+ genalloc_append(uint16, &a->list, &id) ;
+ return 1 ;
+}
+
+int skadns_update (skadns_t *a)
+{
+ genalloc_setlen(uint16, &a->list, 0) ;
+ return skaclient_update(&a->connection, &msghandler, a) ;
+}
diff --git a/src/skadns/skadns_zero.c b/src/skadns/skadns_zero.c
new file mode 100644
index 0000000..1842d2e
--- /dev/null
+++ b/src/skadns/skadns_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6-dns/skadns.h>
+
+skadns_t const skadns_zero = SKADNS_ZERO ;
diff --git a/src/skadns/skadnsd.c b/src/skadns/skadnsd.c
new file mode 100644
index 0000000..7b5419f
--- /dev/null
+++ b/src/skadns/skadnsd.c
@@ -0,0 +1,199 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <signal.h>
+#include <skalibs/uint16.h>
+#include <skalibs/error.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/sig.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/iopause.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <s6-dns/s6dns.h>
+#include <s6-dns/skadns.h>
+
+typedef struct dnsio_s dnsio_t, *dnsio_t_ref ;
+struct dnsio_s
+{
+ unsigned int xindex ;
+ s6dns_engine_t dt ;
+ uint16 id ;
+} ;
+#define DNSIO_ZERO { .xindex = SKADNS_MAXCONCURRENCY, .dt = S6DNS_ENGINE_ZERO, .id = 0 }
+
+static dnsio_t a[SKADNS_MAXCONCURRENCY] ;
+static unsigned int sp = 0 ;
+
+static void remove (unsigned int i)
+{
+ dnsio_t tmp ;
+ tmp = a[sp-1] ;
+ a[--sp] = a[i] ;
+ a[i] = tmp ;
+}
+
+static void fail (unsigned int i)
+{
+ char pack[3] ;
+ unixmessage_t m = { .s = pack, len = 3, .fds = 0, .nfds = 0 } ;
+ uint16_pack_big(pack, a[i].id) ;
+ pack[2] = a[i].dt.status ;
+ s6dns_engine_recycle(&a[i].dt) ;
+ remove(i) ;
+ if (!unixmessage_put(unixmessage_sender_x, &m))
+ strerr_diefu1sys(111, "unixmessage_put") ;
+}
+
+static void answer (char c)
+{
+ unixmessage_t m = { .s = &c, .len = 1, .fds = 0, .nfds = 0 } ;
+ if (!unixmessage_put(unixmessage_sender_1, &m))
+ strerr_diefu1sys(111, "unixmessage_put") ;
+}
+
+static int parse_protocol (unixmessage_t const *m, void *context)
+{
+ uint16 id ;
+ if (m->len < 3 || m->nfds) strerr_dief1x(100, "invalid client request") ;
+ uint16_unpack_big(m->s, &id) ;
+ switch (m->s[2]) /* protocol parsing */
+ {
+ case 'Q' : /* send a query */
+ {
+ tain_t limit ;
+ uint16 qtype ;
+ if (m->len < 21) strerr_dief1x(100, "invalid client request") ;
+ if (sp >= SKADNS_MAXCONCURRENCY)
+ {
+ answer(ENFILE) ;
+ break ;
+ }
+ uint16_unpack_big(m->s + 3, &qtype) ;
+ if (byte_diff(m->s + 5, 12, "\0\0\0\0\0\0\0\0\0\0\0"))
+ tain_unpack(m->s + 5, &limit) ;
+ else tain_add_g(&limit, &tain_infinite_relative) ;
+ if (!s6dns_engine_init_g(&a[sp].dt, &s6dns_rci_here.servers, 1, m->s + 17, m->len - 17, qtype, &limit))
+ {
+ answer(errno) ;
+ break ;
+ }
+ a[sp++].id = id ;
+ answer(0) ;
+ break ;
+ }
+ case 'q' : /* cancel a query */
+ {
+ register unsigned int i = 0 ;
+ for (; i < sp ; i++) if (a[i].id == id) break ;
+ if (i >= sp)
+ {
+ answer(ENOENT) ;
+ break ;
+ }
+ s6dns_engine_recycle(&a[i].dt) ;
+ remove(i) ;
+ answer(0) ;
+ break ;
+ }
+ default : strerr_dief1x(100, "invalid client request") ;
+ }
+ (void)context ;
+ return 1 ;
+}
+
+int main (void)
+{
+ PROG = "skadnsd" ;
+
+ if (ndelay_on(0) < 0) strerr_diefu2sys(111, "ndelay_on ", "0") ;
+ if (ndelay_on(1) < 0) strerr_diefu2sys(111, "ndelay_on ", "1") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ tain_now_g() ;
+ if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ;
+
+ {
+ tain_t deadline ;
+ tain_addsec_g(&deadline, 2) ;
+ if (!skaclient_server_01x_init_g(SKADNS_BANNER1, SKADNS_BANNER1_LEN, SKADNS_BANNER2, SKADNS_BANNER2_LEN, &deadline))
+ strerr_diefu1sys(111, "sync with client") ;
+ }
+ {
+ static dnsio_t const zero = DNSIO_ZERO ;
+ register unsigned int i = 0 ;
+ for (; i < SKADNS_MAXCONCURRENCY ; i++) a[i] = zero ;
+ }
+
+ for (;;)
+ {
+ iopause_fd x[3 + sp] ;
+ register int r ;
+
+ x[0].fd = 0 ; x[0].events = IOPAUSE_EXCEPT | IOPAUSE_READ ;
+ x[1].fd = 1 ; x[1].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_1) ? 0 : IOPAUSE_WRITE) ;
+ x[2].fd = unixmessage_sender_fd(unixmessage_sender_x) ;
+ x[2].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_x) ? 0 : IOPAUSE_WRITE) ;
+ {
+ tain_t deadline = TAIN_INFINITE ;
+ register unsigned int i = 0 ;
+ for (; i < sp ; i++)
+ {
+ register unsigned int j = 3 + i ;
+ s6dns_engine_nextdeadline(&a[i].dt, &deadline) ;
+ x[j].fd = a[i].dt.fd ;
+ x[j].events = 0 ;
+ if (s6dns_engine_isreadable(&a[i].dt)) x[j].events |= IOPAUSE_READ ;
+ if (s6dns_engine_iswritable(&a[i].dt)) x[j].events |= IOPAUSE_WRITE ;
+ a[i].xindex = j ;
+ }
+ r = iopause_g(x, 3 + sp, &deadline) ;
+ }
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ if (!r)
+ {
+ register unsigned int i = 0 ;
+ for (; i < sp ; i++)
+ if (s6dns_engine_timeout_g(&a[i].dt)) fail(i--) ;
+ continue ;
+ }
+
+ if (x[1].revents & IOPAUSE_WRITE)
+ if ((unixmessage_sender_flush(unixmessage_sender_1) < 0) && !error_isagain(errno))
+ strerr_diefu1sys(111, "flush stdout") ;
+ if (x[2].revents & IOPAUSE_WRITE)
+ if ((unixmessage_sender_flush(unixmessage_sender_x) < 0) && !error_isagain(errno))
+ strerr_diefu1sys(111, "flush asyncout") ;
+
+ {
+ register unsigned int i = 0 ;
+ for (; i < sp ; i++) if (x[a[i].xindex].revents)
+ {
+ register int r = s6dns_engine_event_g(&a[i].dt) ;
+ if (r < 0) fail(i--) ;
+ else if (r)
+ {
+ char pack[3] ;
+ siovec_t v[2] = { { .s = pack, .len = 3 }, { .s = s6dns_engine_packet(&a[i].dt), .len = s6dns_engine_packetlen(&a[i].dt) } } ;
+ unixmessage_v_t mv = { .v = v, .vlen = 2, .fds = 0, .nfds = 0 } ;
+ uint16_pack_big(pack, a[i].id) ;
+ pack[2] = 0 ;
+ if (!unixmessage_putv(unixmessage_sender_x, v, 2))
+ strerr_diefu1sys(111, "unixmessage_put") ;
+ s6dns_engine_recycle(&a[i].dt) ;
+ remove(i--) ;
+ }
+ }
+ }
+
+ if (!unixmessage_receiver_isempty(unixmessage_receiver_0) || x[0].revents & IOPAUSE_READ)
+ {
+ if (unixmessage_handle(unixmessage_receiver_0, &parse_protocol, 0) < 0)
+ {
+ if (errno == EPIPE) break ; /* normal exit */
+ strerr_diefu1sys(111, "handle messages from client") ;
+ }
+ }
+ }
+ return 0 ;
+}
diff --git a/tools/gen-deps.sh b/tools/gen-deps.sh
new file mode 100755
index 0000000..af31259
--- /dev/null
+++ b/tools/gen-deps.sh
@@ -0,0 +1,79 @@
+#!/bin/sh -e
+
+. package/info
+
+echo '#'
+echo '# This file has been generated by tools/gen-deps.sh'
+echo '#'
+echo
+
+for dir in src/include/${package} src/* ; do
+ for file in $(ls -1 $dir | grep -- \\.h$) ; do
+ {
+ grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ;
+ grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2
+ } | sort -u | {
+ deps=
+ while read dep ; do
+ if echo $dep | grep -q "^${package}/" ; then
+ deps="$deps src/include/$dep"
+ elif test -f "${dir}/$dep" ; then
+ deps="$deps ${dir}/$dep"
+ else
+ deps="$deps src/include-local/$dep"
+ fi
+ done
+ if test -n "$deps" ; then
+ echo "${dir}/${file}:${deps}"
+ fi
+ }
+ done
+done
+
+for dir in src/* ; do
+ for file in $(ls -1 $dir | grep -- \\.c$) ; do
+ {
+ grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ;
+ grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2
+ } | sort -u | {
+ deps=" ${dir}/$file"
+ while read dep ; do
+ if echo $dep | grep -q "^${package}/" ; then
+ deps="$deps src/include/$dep"
+ elif test -f "${dir}/$dep" ; then
+ deps="$deps ${dir}/$dep"
+ else
+ deps="$deps src/include-local/$dep"
+ fi
+ done
+ o=$(echo $file | sed s/\\.c$/.o/)
+ lo=$(echo $file | sed s/\\.c$/.lo/)
+ echo "${dir}/${o} ${dir}/${lo}:${deps}"
+ }
+ done
+done
+echo
+
+for dir in $(ls -1 src | grep -v ^include) ; do
+ for file in $(ls -1 src/$dir/deps-lib) ; do
+ deps=
+ while read dep ; do
+ deps="$deps src/$dir/$dep"
+ done < src/$dir/deps-lib/$file
+ echo "lib$file.a: $deps"
+ if test -x "src/$dir/deps-lib/$file" ; then
+ echo "lib${file}.so: $(echo "$deps" | sed 's/\.o/.lo/g')"
+ fi
+ done
+
+ for file in $(ls -1 src/$dir/deps-exe) ; do
+ deps=
+ while read dep ; do
+ if echo $dep | grep -q -- \\.o$ ; then
+ dep="src/$dir/$dep"
+ fi
+ deps="$deps $dep"
+ done < src/$dir/deps-exe/$file
+ echo "$file: src/$dir/$file.o$deps"
+ done
+done
diff --git a/tools/install.sh b/tools/install.sh
new file mode 100755
index 0000000..89f9428
--- /dev/null
+++ b/tools/install.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+usage() {
+ echo "usage: $0 [-D] [-l] [-m mode] src dst" 1>&2
+ exit 1
+}
+
+mkdirp=false
+symlink=false
+mode=0755
+
+while getopts Dlm: name ; do
+ case "$name" in
+ D) mkdirp=true ;;
+ l) symlink=true ;;
+ m) mode=$OPTARG ;;
+ ?) usage ;;
+ esac
+done
+shift $(($OPTIND - 1))
+
+test "$#" -eq 2 || usage
+src=$1
+dst=$2
+tmp="$dst.tmp.$$"
+
+case "$dst" in
+ */) echo "$0: $dst ends in /" 1>&2 ; exit 1 ;;
+esac
+
+set -C
+set -e
+
+if $mkdirp ; then
+ umask 022
+ case "$2" in
+ */*) mkdir -p "${dst%/*}" ;;
+ esac
+fi
+
+trap 'rm -f "$tmp"' EXIT INT QUIT TERM HUP
+
+umask 077
+
+if $symlink ; then
+ ln -s "$src" "$tmp"
+else
+ cat < "$1" > "$tmp"
+ chmod "$mode" "$tmp"
+fi
+
+mv -f "$tmp" "$dst"
+if test -d "$dst" ; then
+ rm -f "$dst/$(basename $tmp)"
+ if $symlink ; then
+ mkdir "$tmp"
+ ln -s "$src" "$tmp/$(basename $dst)"
+ mv -f "$tmp/$(basename $dst)" "${dst%/*}"
+ rmdir "$tmp"
+ else
+ echo "$0: $dst is a directory" 1>&2
+ exit 1
+ fi
+fi