summaryrefslogtreecommitdiff
path: root/src/libs6dns
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 /src/libs6dns
downloads6-dns-416ef5e2bf59bb2e45066a1d5d91ac677c0f48e5.tar.xz
Initial commit
Diffstat (limited to 'src/libs6dns')
-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
90 files changed, 2622 insertions, 0 deletions
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 ;
+}