summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cache/dcache-internal.h18
-rw-r--r--src/cache/dcache_add.c85
-rw-r--r--src/cache/dcache_clean_expired.c20
-rw-r--r--src/cache/dcache_delete.c19
-rw-r--r--src/cache/dcache_free.c22
-rw-r--r--src/cache/dcache_init.c55
-rw-r--r--src/cache/dcache_load.c81
-rw-r--r--src/cache/dcache_save.c73
-rw-r--r--src/cache/dcache_search.c15
-rw-r--r--src/cache/deps-lib/dcache8
-rw-r--r--src/common/deps-lib/shibari-common10
-rw-r--r--src/common/shibari_log_answer.c36
-rw-r--r--src/common/shibari_log_exit.c14
-rw-r--r--src/common/shibari_log_query.c18
-rw-r--r--src/common/shibari_log_queryplus.c24
-rw-r--r--src/common/shibari_log_start.c17
-rw-r--r--src/common/shibari_util_get_prefixlen.c14
-rw-r--r--src/common/shibari_util_qtype_num.c121
-rw-r--r--src/common/shibari_util_qtype_str.c285
-rw-r--r--src/common/shibari_util_rcode_str.c41
-rw-r--r--src/include/shibari/cache.h8
-rw-r--r--src/include/shibari/client.h6
-rw-r--r--src/include/shibari/common.h9
-rw-r--r--src/include/shibari/constants.h109
-rw-r--r--src/include/shibari/dcache.h54
-rw-r--r--src/include/shibari/log.h20
-rw-r--r--src/include/shibari/packet.h39
-rw-r--r--src/include/shibari/server.h10
-rw-r--r--src/include/shibari/shibari.h11
-rw-r--r--src/include/shibari/tdb.h29
-rw-r--r--src/include/shibari/util.h14
-rw-r--r--src/server/deps-exe/shibari-server-tcp4
-rw-r--r--src/server/deps-exe/shibari-server-udp6
-rw-r--r--src/server/deps-lib/shibari-server13
-rw-r--r--src/server/shibari-server-tcp.c235
-rw-r--r--src/server/shibari-server-udp.c207
-rw-r--r--src/server/shibari_packet_add_glue.c48
-rw-r--r--src/server/shibari_packet_add_rr.c46
-rw-r--r--src/server/shibari_packet_assert_authority.c18
-rw-r--r--src/server/shibari_packet_begin.c32
-rw-r--r--src/server/shibari_packet_end.c13
-rw-r--r--src/server/shibari_packet_init.c14
-rw-r--r--src/server/shibari_packet_tdb_answer_query.c93
-rw-r--r--src/server/shibari_tdb_entry_parse.c56
-rw-r--r--src/server/shibari_tdb_extract_domain.c17
-rw-r--r--src/server/shibari_tdb_find_authority.c46
-rw-r--r--src/server/shibari_tdb_read_entry.c22
47 files changed, 2155 insertions, 0 deletions
diff --git a/src/cache/dcache-internal.h b/src/cache/dcache-internal.h
new file mode 100644
index 0000000..2a2e36f
--- /dev/null
+++ b/src/cache/dcache-internal.h
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#ifndef SHIBARI_DCACHE_INTERNAL_H
+#define SHIBARI_DCACHE_INTERNAL_H
+
+#include <stdint.h>
+
+#include <skalibs/avlnode.h>
+#include <skalibs/gensetdyn.h>
+
+#include <shibari/dcache.h>
+
+#define DNODE(z, i) GENSETDYN_P(dcache_node_t, &(z)->storage, i)
+#define DCACHE_NODE_OVERHEAD (32 + sizeof(dcache_node_t) + 3 * sizeof(avlnode))
+
+extern void dcache_delete (dcache_t *, uint32_t) ;
+
+#endif
diff --git a/src/cache/dcache_add.c b/src/cache/dcache_add.c
new file mode 100644
index 0000000..7260726
--- /dev/null
+++ b/src/cache/dcache_add.c
@@ -0,0 +1,85 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/alloc.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avlnode.h>
+#include <skalibs/avltree.h>
+
+#include <shibari/dcache.h>
+#include "dcache-internal.h"
+
+static void uniquify (avltree const *tree, tain *stamp)
+{
+ static tain const nano = { .sec = TAI_ZERO, .nano = 1 } ;
+ uint32_t dummy ;
+ while (avltree_search(tree, stamp, &dummy))
+ tain_add(stamp, stamp, &nano) ;
+}
+
+static inline void dcache_gc_by_entry (dcache_t *z, uint64_t max)
+{
+ while (z->size > max)
+ {
+ uint32_t oldest ;
+ if (!avltree_min(&z->by_entry, &oldest)) break ;
+ dcache_delete(z, oldest) ;
+ }
+}
+
+static inline int dcache_add_node (dcache_t *z, dcache_node_t const *node)
+{
+ uint32_t i ;
+ dcache_node_t *y ;
+ if (!gensetdyn_new(&z->storage, &i)) return 0 ;
+ y = DNODE(z, i) ; *y = *node ;
+ uniquify(&z->by_entry, &y->entry) ;
+ uniquify(&z->by_expire, &y->expire) ;
+ if (!avltree_insert(&z->by_key, i)) goto err1 ;
+ if (!avltree_insert(&z->by_entry, i)) goto err2 ;
+ if (!avltree_insert(&z->by_expire, i)) goto err3 ;
+ return 1 ;
+
+ err3:
+ avltree_delete(&z->by_entry, &y->entry) ;
+ err2:
+ avltree_delete(&z->by_key, &y->key) ;
+ err1:
+ gensetdyn_delete(&z->storage, i) ;
+ return 0 ;
+}
+
+static inline int dcache_add_unbounded (dcache_t *z, char const *key, uint16_t keylen, char const *data, uint16_t datalen, tain const *expire, tain const *stamp)
+{
+ uint32_t len = (uint32_t)keylen + (uint32_t)datalen ;
+ dcache_node_t y = { .key = { .s = alloc(len) } } ;
+ if (!y.key.s) return 0 ;
+ memcpy(y.key.s, key, keylen) ;
+ memcpy(y.key.s + keylen, data, datalen) ;
+ y.key.len = keylen ;
+ y.datalen = datalen ;
+ y.entry = *stamp ;
+ y.expire = *expire ;
+ if (!dcache_add_node(z, &y))
+ {
+ alloc_free(y.key.s) ;
+ return 0 ;
+ }
+ z->size += DCACHE_NODE_OVERHEAD + len ;
+ z->motion += DCACHE_NODE_OVERHEAD + len ;
+ return 1 ;
+}
+
+int dcache_add (dcache_t *z, uint64_t max, char const *key, uint16_t keylen, char const *data, uint16_t datalen, tain const *expire, tain const *stamp)
+{
+ uint64_t size = DCACHE_NODE_OVERHEAD + keylen + datalen ;
+ if (size > max) return (errno = EINVAL, 0) ;
+ if (z->size > max - size) dcache_clean_expired(z, stamp) ;
+ if (z->size > max - size) dcache_gc_by_entry(z, max - size) ;
+ return dcache_add_unbounded(z, key, keylen, data, datalen, expire, stamp) ;
+}
diff --git a/src/cache/dcache_clean_expired.c b/src/cache/dcache_clean_expired.c
new file mode 100644
index 0000000..0e23443
--- /dev/null
+++ b/src/cache/dcache_clean_expired.c
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#include <stdint.h>
+
+#include <skalibs/tai.h>
+#include <skalibs/avltree.h>
+
+#include <shibari/dcache.h>
+#include "dcache-internal.h"
+
+void dcache_clean_expired (dcache_t *z, tain const *stamp)
+{
+ for (;;)
+ {
+ uint32_t i ;
+ if (!avltree_min(&z->by_expire, &i)) break ;
+ if (tain_less(stamp, &DNODE(z, i)->expire)) break ;
+ dcache_delete(z, i) ;
+ }
+}
diff --git a/src/cache/dcache_delete.c b/src/cache/dcache_delete.c
new file mode 100644
index 0000000..92a5fcc
--- /dev/null
+++ b/src/cache/dcache_delete.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <skalibs/alloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+
+#include <shibari/dcache.h>
+#include "dcache-internal.h"
+
+void dcache_delete (dcache_t *z, uint32_t i)
+{
+ dcache_node_t *y = DNODE(z, i) ;
+ avltree_delete(&z->by_expire, &y->expire) ;
+ avltree_delete(&z->by_entry, &y->entry) ;
+ avltree_delete(&z->by_key, &y->key) ;
+ alloc_free(y->key.s) ;
+ z->size -= DCACHE_NODE_OVERHEAD + y->key.len + y->datalen ;
+ gensetdyn_delete(&z->storage, i) ;
+}
diff --git a/src/cache/dcache_free.c b/src/cache/dcache_free.c
new file mode 100644
index 0000000..16c074e
--- /dev/null
+++ b/src/cache/dcache_free.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <skalibs/alloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+
+#include <shibari/dcache.h>
+
+static void dcache_node_free (void *p)
+{
+ alloc_free(((dcache_node_t *)p)->key.s) ;
+}
+
+void dcache_free (dcache_t *z)
+{
+ static dcache_t const dcache_zero = DCACHE_ZERO ;
+ avltree_free(&z->by_expire) ;
+ avltree_free(&z->by_entry) ;
+ avltree_free(&z->by_key) ;
+ gensetdyn_deepfree(&z->storage, &dcache_node_free) ;
+ *z = dcache_zero ;
+}
diff --git a/src/cache/dcache_init.c b/src/cache/dcache_init.c
new file mode 100644
index 0000000..d42ec62
--- /dev/null
+++ b/src/cache/dcache_init.c
@@ -0,0 +1,55 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+
+#include <shibari/dcache.h>
+
+static int key_cmp (void const *a, void const *b, void *x)
+{
+ dcache_key_t const *ka = a ;
+ dcache_key_t const *kb = b ;
+ if (ka->len < kb->len) return -1 ;
+ if (kb->len < ka->len) return 1 ;
+ (void)x ;
+ return memcmp(ka->s, kb->s, ka->len) ;
+}
+
+static int tain_cmp (void const *a, void const *b, void *x)
+{
+ tain const *ta = a ;
+ tain const *tb = b ;
+ (void)x ;
+ return tain_less(ta, tb) ? -1 : tain_less(tb, ta) ;
+}
+
+static void *key_dtok (uint32_t d, void *x)
+{
+ return &GENSETDYN_P(dcache_node_t, (gensetdyn *)x, d)->key ;
+}
+
+static void *entry_dtok (uint32_t d, void *x)
+{
+ return &GENSETDYN_P(dcache_node_t, (gensetdyn *)x, d)->entry ;
+}
+
+static void *expire_dtok (uint32_t d, void *x)
+{
+ return &GENSETDYN_P(dcache_node_t, (gensetdyn *)x, d)->expire ;
+}
+
+
+void dcache_init (dcache_t *z, uint64_t max)
+{
+ gensetdyn_init(&z->storage, sizeof(dcache_node_t), max >> 9, 3, 8) ;
+ avltree_init(&z->by_key, max >> 9, 3, 8, &key_dtok, &key_cmp, &z->storage) ;
+ avltree_init(&z->by_entry, max >> 9, 3, 8, &entry_dtok, &tain_cmp, &z->storage) ;
+ avltree_init(&z->by_expire, max >> 9, 3, 8, &expire_dtok, &tain_cmp, &z->storage) ;
+ z->size = 0 ;
+ z->motion = 0 ;
+}
diff --git a/src/cache/dcache_load.c b/src/cache/dcache_load.c
new file mode 100644
index 0000000..a0ff233
--- /dev/null
+++ b/src/cache/dcache_load.c
@@ -0,0 +1,81 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <skalibs/posixishard.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint64.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+
+#include <shibari/dcache.h>
+
+static inline int dcache_load_node (dcache_t *z, uint64_t max, buffer *b)
+{
+ tain entry = { .nano = 0 } ;
+ tain expire = { .nano = 0 } ;
+ uint16_t keylen ;
+ uint16_t datalen ;
+ char pack[TAI_PACK * 2 + 4] ;
+ ssize_t r = buffer_get(b, pack, TAI_PACK * 2 + 4) ;
+ if (!r) return 0 ;
+ if (r < TAI_PACK * 2 + 4) return -1 ;
+ tai_unpack(pack, tain_secp(&entry)) ;
+ tai_unpack(pack + TAI_PACK, tain_secp(&expire)) ;
+ uint16_unpack_big(pack + TAI_PACK * 2, &keylen) ;
+ uint16_unpack_big(pack + TAI_PACK * 2 + 2, &datalen) ;
+ {
+ uint32_t len = (uint32_t)keylen + (uint32_t)datalen ;
+ char blob[len+1] ; /* 128 kB max, it's ok */
+ r = buffer_get(b, blob, len+1) ;
+ if (!r) return (errno = EPIPE, -1) ;
+ if (r < len) return -1 ;
+ if (blob[len]) return (errno = EPROTO, -1) ;
+ if (!dcache_add(z, max, blob, keylen, blob + keylen, datalen, &expire, &entry)) return -1 ;
+ }
+ return 1 ;
+}
+
+static inline int dcache_load_from_buffer (dcache_t *z, uint64_t max, buffer *b)
+{
+ {
+ char banner[sizeof(DCACHE_MAGIC) - 1] ;
+ char pack[8] ;
+ if (buffer_get(b, banner, sizeof(DCACHE_MAGIC) - 1) < sizeof(DCACHE_MAGIC) - 1)
+ return 0 ;
+ if (memcmp(banner, DCACHE_MAGIC, sizeof(DCACHE_MAGIC) - 1)) return 0 ;
+ if (buffer_get(b, pack, 8) < 8) return 0 ;
+ uint64_unpack_big(pack, &z->size) ;
+ if (buffer_get(b, pack, 8) < 8) return 0 ;
+ uint64_unpack_big(pack, &z->motion) ;
+ }
+ for (;;)
+ {
+ int r = dcache_load_node(z, max, b) ;
+ if (r < 0) return 0 ;
+ if (!r) break ;
+ }
+ return 1 ;
+}
+
+#define N 8192
+
+int dcache_load (dcache_t *z, uint64_t max, char const *file)
+{
+ char buf[N] ;
+ buffer b ;
+ int fd = open_readb(file) ;
+ if (fd == -1) return 0 ;
+ buffer_init(&b, &buffer_read, fd, buf, N) ;
+ if (!dcache_load_from_buffer(z, max, &b)) goto err ;
+ fd_close(fd) ;
+ return 1 ;
+
+ err:
+ dcache_free(z) ;
+ fd_close(fd) ;
+ return 0 ;
+}
diff --git a/src/cache/dcache_save.c b/src/cache/dcache_save.c
new file mode 100644
index 0000000..7277771
--- /dev/null
+++ b/src/cache/dcache_save.c
@@ -0,0 +1,73 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint64.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/gensetdyn.h>
+
+#include <shibari/dcache.h>
+
+static int write_node_iter (void *data, void *aux)
+{
+ dcache_node_t *y = data ;
+ buffer *b = aux ;
+ char pack[TAI_PACK * 2 + 4] ;
+ tai_pack(pack, tain_secp(&y->entry)) ;
+ tai_pack(pack + TAI_PACK, tain_secp(&y->expire)) ;
+ uint16_pack(pack + TAI_PACK * 2, y->key.len) ;
+ uint16_pack(pack + TAI_PACK * 2 + 2, y->datalen) ;
+ if (buffer_put(b, pack, TAI_PACK * 2 + 4) == -1) return 0 ;
+ if (buffer_put(b, y->key.s, y->key.len + y->datalen) == -1) return 0 ;
+ if (buffer_put(b, "", 1) == -1) return 0 ;
+ return 1 ;
+}
+
+static inline int dcache_save_to_buffer (dcache_t const *z, buffer *b)
+{
+ char pack[16] ;
+ if (buffer_puts(b, DCACHE_MAGIC) == -1) return 0 ;
+ uint64_pack_big(pack, z->size) ;
+ uint64_pack_big(pack + 8, z->motion) ;
+ if (buffer_put(b, pack, 16) < 16) return 0 ;
+
+ /* XXX: can gensetdyn_iter blow up the stack if z->storage is huge? */
+ if (gensetdyn_iter_nocancel((gensetdyn *)&z->storage, gensetdyn_n(&z->storage), &write_node_iter, b) < gensetdyn_n(&z->storage)) return 0 ;
+
+ return buffer_flush(b) ;
+}
+
+#define N 8192
+
+int dcache_save (dcache_t const *z, char const *file)
+{
+ size_t len = strlen(file) ;
+ int fd ;
+ buffer b ;
+ char buf[N] ;
+ char tmp[len + 20] ;
+ memcpy(tmp, file, len) ;
+ memcpy(tmp + len, ":dcache_save:XXXXXX", 20) ;
+ fd = mkstemp(tmp) ;
+ if (fd == -1) return 0 ;
+ buffer_init(&b, &buffer_write, fd, buf, N) ;
+ if (!dcache_save_to_buffer(z, &b) || fsync(fd) < 0) goto err2 ;
+ fd_close(fd) ;
+ if (rename(tmp, file) == -1) goto err1 ;
+ return 1 ;
+
+ err2:
+ fd_close(fd) ;
+ err1:
+ unlink_void(tmp) ;
+ return 0 ;
+}
diff --git a/src/cache/dcache_search.c b/src/cache/dcache_search.c
new file mode 100644
index 0000000..0239b88
--- /dev/null
+++ b/src/cache/dcache_search.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <stdint.h>
+
+#include <skalibs/avltree.h>
+
+#include <shibari/dcache.h>
+#include "dcache-internal.h"
+
+dcache_node_t *dcache_search (dcache_t *z, char const *key, uint16_t keylen)
+{
+ uint32_t i ;
+ dcache_key_t k = { .s = (char *)key, .len = keylen } ;
+ return avltree_search(&z->by_key, &k, &i) ? DNODE(z, i) : 0 ;
+}
diff --git a/src/cache/deps-lib/dcache b/src/cache/deps-lib/dcache
new file mode 100644
index 0000000..fcc09a0
--- /dev/null
+++ b/src/cache/deps-lib/dcache
@@ -0,0 +1,8 @@
+dcache_add.o
+dcache_clean_expired.o
+dcache_delete.o
+dcache_free.o
+dcache_init.o
+dcache_load.o
+dcache_save.o
+dcache_search.o
diff --git a/src/common/deps-lib/shibari-common b/src/common/deps-lib/shibari-common
new file mode 100644
index 0000000..a5c44a4
--- /dev/null
+++ b/src/common/deps-lib/shibari-common
@@ -0,0 +1,10 @@
+shibari_log_answer.o
+shibari_log_exit.o
+shibari_log_query.o
+shibari_log_queryplus.o
+shibari_log_start.o
+shibari_util_qtype_num.o
+shibari_util_qtype_str.o
+shibari_util_rcode_str.o
+shibari_util_get_prefixlen.o
+-lskarnet
diff --git a/src/common/shibari_log_answer.c b/src/common/shibari_log_answer.c
new file mode 100644
index 0000000..f34cee5
--- /dev/null
+++ b/src/common/shibari_log_answer.c
@@ -0,0 +1,36 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/strerr.h>
+
+#include <s6-dns/s6dns-message.h>
+
+#include <shibari/util.h>
+#include <shibari/log.h>
+
+void shibari_log_answer (uint32_t v, s6dns_message_header_t const *hdr, uint16_t len)
+{
+ if (v < 2) return ;
+ if (hdr->rcode)
+ {
+ char fmtr[UINT16_FMT] ;
+ fmtr[uint16_fmt(fmtr, hdr->rcode)] = 0 ;
+ strerr_warni4x("answer ", fmtr, " ", shibari_util_rcode_str(hdr->rcode)) ;
+ }
+ else
+ {
+ size_t pos = 0 ;
+ char fmt[UINT16_FMT << 2] ;
+ char fmtl[UINT16_FMT] ;
+ pos += uint16_fmt(fmt + pos, hdr->counts.qd) ;
+ fmt[pos++] = '+' ;
+ pos += uint16_fmt(fmt + pos, hdr->counts.an) ;
+ fmt[pos++] = '+' ;
+ pos += uint16_fmt(fmt + pos, hdr->counts.ns) ;
+ fmt[pos++] = '+' ;
+ pos += uint16_fmt(fmt + pos, hdr->counts.qd) ;
+ fmt[pos] = 0 ;
+ fmtl[uint16_fmt(fmtl, len)] = 0 ;
+ strerr_warni5x("answer 0 noerror ", fmt, " len ", fmtl, hdr->tc ? " tc" : "") ;
+ }
+}
diff --git a/src/common/shibari_log_exit.c b/src/common/shibari_log_exit.c
new file mode 100644
index 0000000..8e383c6
--- /dev/null
+++ b/src/common/shibari_log_exit.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <skalibs/types.h>
+#include <skalibs/strerr.h>
+
+#include <shibari/log.h>
+
+void shibari_log_exit (uint32_t v, int e)
+{
+ char fmt[UINT_FMT] ;
+ if (v < 2) return ;
+ fmt[uint_fmt(fmt, (unsigned int)e)] = 0 ;
+ strerr_warni2x("exit ", fmt) ;
+}
diff --git a/src/common/shibari_log_query.c b/src/common/shibari_log_query.c
new file mode 100644
index 0000000..b04ee27
--- /dev/null
+++ b/src/common/shibari_log_query.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <skalibs/strerr.h>
+
+#include <s6-dns/s6dns-domain.h>
+
+#include <shibari/util.h>
+#include <shibari/log.h>
+
+void shibari_log_query (uint32_t v, s6dns_domain_t const *q, uint16_t qtype)
+{
+ char qs[256] ;
+ s6dns_domain_t qe ;
+ if (v < 2) return ;
+ qe = *q ;
+ if (!s6dns_domain_encode(&qe) || !s6dns_domain_tostring(qs, 256, &qe)) return ;
+ strerr_warni4x("query ", shibari_util_qtype_str(qtype), " ", qs) ;
+}
diff --git a/src/common/shibari_log_queryplus.c b/src/common/shibari_log_queryplus.c
new file mode 100644
index 0000000..7e7fecc
--- /dev/null
+++ b/src/common/shibari_log_queryplus.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/ip46.h>
+#include <skalibs/strerr.h>
+
+#include <s6-dns/s6dns-domain.h>
+
+#include <shibari/util.h>
+#include <shibari/log.h>
+
+void shibari_log_queryplus (uint32_t v, s6dns_domain_t const *q, uint16_t qtype, ip46 const *ip, uint16_t port)
+{
+ char qs[256] ;
+ char fmti[IP46_FMT] ;
+ char fmtp[UINT16_FMT] ;
+ s6dns_domain_t qe ;
+ if (v < 2) return ;
+ qe = *q ;
+ if (!s6dns_domain_encode(&qe) || !s6dns_domain_tostring(qs, 256, &qe)) return ;
+ fmti[ip46_fmt(fmti, ip)] = 0 ;
+ fmtp[uint16_fmt(fmtp, port)] = 0 ;
+ strerr_warni8x("query ", shibari_util_qtype_str(qtype), " ", qs, " ip ", fmti, " port ", fmtp) ;
+}
diff --git a/src/common/shibari_log_start.c b/src/common/shibari_log_start.c
new file mode 100644
index 0000000..214dc60
--- /dev/null
+++ b/src/common/shibari_log_start.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/ip46.h>
+#include <skalibs/strerr.h>
+
+#include <shibari/log.h>
+
+void shibari_log_start (uint32_t v, ip46 const *ip, uint16_t port)
+{
+ char fmti[IP46_FMT] ;
+ char fmtp[UINT16_FMT] ;
+ if (v < 2) return ;
+ fmti[ip46_fmt(fmti, ip)] = 0 ;
+ fmtp[uint16_fmt(fmtp, port)] = 0 ;
+ strerr_warni4x("start ip ", fmti, " port ", fmtp) ;
+}
diff --git a/src/common/shibari_util_get_prefixlen.c b/src/common/shibari_util_get_prefixlen.c
new file mode 100644
index 0000000..784a3ee
--- /dev/null
+++ b/src/common/shibari_util_get_prefixlen.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <shibari/util.h>
+
+int shibari_util_get_prefixlen (char const *name, uint16_t namelen, char const *zone, uint16_t zonelen)
+{
+ return
+ namelen < zonelen ? -1 :
+ memcmp(name + namelen - zonelen, zone, zonelen) ? -1 :
+ namelen - zonelen ;
+}
diff --git a/src/common/shibari_util_qtype_num.c b/src/common/shibari_util_qtype_num.c
new file mode 100644
index 0000000..350e268
--- /dev/null
+++ b/src/common/shibari_util_qtype_num.c
@@ -0,0 +1,121 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include <shibari/util.h>
+
+struct map_s
+{
+ char const *s ;
+ uint16_t num ;
+} ;
+
+static int map_cmp (void const *a, void const *b)
+{
+ return strcasecmp((char const *)a, ((struct map_s const *)b)->s) ;
+}
+
+#define BSEARCH(key, array) bsearch(key, (array), sizeof(array)/sizeof(struct map_s), sizeof(struct map_s), &map_cmp)
+
+static struct map_s const qtype_table[] =
+{
+ { "*", 255 },
+ { "A", 1 },
+ { "A6", 38 },
+ { "AAAA", 28 },
+ { "AFSDB", 18 },
+ { "AMTRELAY", 260 },
+ { "ANY", 255 },
+ { "APL", 42 },
+ { "ATMA", 34 },
+ { "AVC", 258 },
+ { "AXFR", 252 },
+ { "CAA", 257 },
+ { "CDNSKEY", 60 },
+ { "CDS", 59 },
+ { "CERT", 37 },
+ { "CNAME", 5 },
+ { "CSYNC", 62 },
+ { "DHCID", 49 },
+ { "DLV", 32769 },
+ { "DNAME", 39 },
+ { "DNSKEY", 48 },
+ { "DOA", 259 },
+ { "DS", 43 },
+ { "EID", 31 },
+ { "EUI48", 108 },
+ { "EUI64", 109 },
+ { "GID", 102 },
+ { "GPOS", 27 },
+ { "HINFO", 13 },
+ { "HIP", 55 },
+ { "HTTPS", 65 },
+ { "IPSECKEY", 45 },
+ { "ISDN", 20 },
+ { "IXFR", 251 },
+ { "KEY", 25 },
+ { "KX", 36 },
+ { "L32", 105 },
+ { "L64", 106 },
+ { "LOC", 29 },
+ { "LP", 107 },
+ { "MAILA", 254 },
+ { "MAILB", 253 },
+ { "MB", 7 },
+ { "MD", 3 },
+ { "MF", 4 },
+ { "MG", 8 },
+ { "MINFO", 14 },
+ { "MR", 9 },
+ { "MX", 15 },
+ { "NAPTR", 35 },
+ { "NID", 104 },
+ { "NIMLOC", 32 },
+ { "NINFO", 56 },
+ { "NS", 2 },
+ { "NSAP", 22 },
+ { "NSAP-PTR", 23 },
+ { "NSEC", 47 },
+ { "NSEC3", 50 },
+ { "NSEC3PARAM", 51 },
+ { "NULL", 10 },
+ { "NXT", 30 },
+ { "OPENPGPKEY", 61 },
+ { "OPT", 41 },
+ { "PTR", 12 },
+ { "PX", 26 },
+ { "RESINFO", 261 },
+ { "RKEY", 57 },
+ { "RP", 17 },
+ { "RRSIG", 46 },
+ { "RT", 21 },
+ { "SIG", 24 },
+ { "SINK", 40 },
+ { "SMIMEA", 53 },
+ { "SOA", 6 },
+ { "SPF", 99 },
+ { "SRV", 33 },
+ { "SSHFP", 44 },
+ { "SVCB", 64 },
+ { "TA", 32768 },
+ { "TALINK", 58 },
+ { "TKEY", 249 },
+ { "TLSA", 52 },
+ { "TSIG", 250 },
+ { "TXT", 16 },
+ { "UID", 101 },
+ { "UINFO", 100 },
+ { "UNSPEC", 103 },
+ { "URI", 256 },
+ { "WKS", 11 },
+ { "X25", 19 },
+ { "ZONEMD", 63 }
+} ;
+
+uint16_t shibari_util_qtype_num (char const *s)
+{
+ struct map_s const *p = BSEARCH(s, qtype_table) ;
+ return p ? p->num : 0 ;
+}
diff --git a/src/common/shibari_util_qtype_str.c b/src/common/shibari_util_qtype_str.c
new file mode 100644
index 0000000..97b701d
--- /dev/null
+++ b/src/common/shibari_util_qtype_str.c
@@ -0,0 +1,285 @@
+/* ISC license. */
+
+#include <stdint.h>
+
+#include <shibari/util.h>
+
+static char const *const qtype_table[262] =
+{
+ "error",
+ "A",
+ "NS",
+ "MD",
+ "MF",
+ "CNAME",
+ "SOA",
+ "MB"
+ "MG",
+ "MR",
+ "NULL",
+ "WKS",
+ "PTR",
+ "HINFO",
+ "MINFO",
+ "MX",
+ "TXT",
+ "RP",
+ "AFSDB",
+ "X25",
+ "ISDN",
+ "RT",
+ "NSAP",
+ "NSAP-PTR",
+ "SIG",
+ "KEY",
+ "PX",
+ "GPOS",
+ "AAAA",
+ "LOC",
+ "NXT",
+ "EID",
+ "NIMLOC",
+ "SRV",
+ "ATMA",
+ "NAPTR",
+ "KX",
+ "CERT",
+ "A6",
+ "DNAME",
+ "SINK",
+ "OPT",
+ "APL",
+ "DS",
+ "SSHFP",
+ "IPSECKEY",
+ "RRSIG",
+ "NSEC",
+ "DNSKEY",
+ "DHCID",
+ "NSEC3",
+ "NSEC3PARAM",
+ "TLSA",
+ "SMIMEA",
+ "unassigned",
+ "HIP",
+ "NINFO",
+ "RKEY",
+ "TALINK",
+ "CDS",
+ "CDNSKEY",
+ "OPENPGPKEY",
+ "CSYNC",
+ "ZONEMD",
+ "SVCB",
+ "HTTPS",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "SPF",
+ "UINFO",
+ "UID",
+ "GID",
+ "UNSPEC",
+ "NID",
+ "L32",
+ "L64",
+ "LP",
+ "EUI48",
+ "EUI64",
+
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+
+ "TKEY",
+ "TSIG",
+ "IXFR",
+ "AXFR",
+ "MAILB",
+ "MAILA",
+ "*",
+ "URI",
+ "CAA",
+ "AVC",
+ "DOA",
+ "AMTRELAY",
+ "RESINFO"
+} ;
+
+
+char const *shibari_util_qtype_str (uint16_t qtype)
+{
+ if (qtype < 262) return qtype_table[qtype] ;
+ if (qtype < 32768) return "unassigned" ;
+ if (qtype == 32768) return "TA" ;
+ if (qtype == 32769) return "DLV" ;
+ if (qtype < 65279) return "unassigned" ;
+ if (qtype < 65535) return "private" ;
+ return "reserved" ;
+}
diff --git a/src/common/shibari_util_rcode_str.c b/src/common/shibari_util_rcode_str.c
new file mode 100644
index 0000000..142cc0e
--- /dev/null
+++ b/src/common/shibari_util_rcode_str.c
@@ -0,0 +1,41 @@
+/* ISC license. */
+
+#include <stdint.h>
+
+#include <shibari/util.h>
+
+char const *shibari_util_rcode_str (uint16_t rcode)
+{
+ static char const *const rcode_table[24] =
+ {
+ "noerror",
+ "formerr",
+ "servfail",
+ "nxdomain",
+ "notimp",
+ "refused",
+ "yxdomain",
+ "yxrrset",
+ "nxrrset",
+ "notauth",
+ "notzone",
+ "dsotypeni",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "unassigned",
+ "badsig",
+ "badkey",
+ "badtime",
+ "badmode",
+ "badname",
+ "badalg",
+ "badtrunc",
+ "badcookie"
+ } ;
+ if (rcode < 24) return rcode_table[rcode] ;
+ if (rcode < 3841) return "unassigned" ;
+ if (rcode < 4096) return "private" ;
+ if (rcode < 65535) return "unassigned" ;
+ return "reserved" ;
+}
diff --git a/src/include/shibari/cache.h b/src/include/shibari/cache.h
new file mode 100644
index 0000000..d0c6004
--- /dev/null
+++ b/src/include/shibari/cache.h
@@ -0,0 +1,8 @@
+/* ISC license. */
+
+#ifndef SHIBARI_CACHE_H
+#define SHIBARI_CACHE_H
+
+#include <shibari/dcache.h>
+
+#endif
diff --git a/src/include/shibari/client.h b/src/include/shibari/client.h
new file mode 100644
index 0000000..03f8b1b
--- /dev/null
+++ b/src/include/shibari/client.h
@@ -0,0 +1,6 @@
+/* ISC license. */
+
+#ifndef SHIBARI_CLIENT_H
+#define SHIBARI_CLIENT_H
+
+#endif
diff --git a/src/include/shibari/common.h b/src/include/shibari/common.h
new file mode 100644
index 0000000..d36d055
--- /dev/null
+++ b/src/include/shibari/common.h
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#ifndef SHIBARI_COMMON_H
+#define SHIBARI_COMMON_H
+
+#include <shibari/constants.h>
+#include <shibari/util.h>
+
+#endif
diff --git a/src/include/shibari/constants.h b/src/include/shibari/constants.h
new file mode 100644
index 0000000..a4f86f3
--- /dev/null
+++ b/src/include/shibari/constants.h
@@ -0,0 +1,109 @@
+/* ISC license. */
+
+#ifndef SHIBARI_CONSTANTS_H
+#define SHIBARI_CONSTANTS_H
+
+enum shibari_qclass_e
+{
+ SHIBARI_C_IN = 1,
+ SHIBARI_C_CH = 3,
+ SHIBARI_C_HS = 4,
+ SHIBARI_C_NONE = 254,
+ SHIBARI_C_ANY = 255,
+} ;
+
+enum shibari_qtype_e
+{
+ SHIBARI_T_A = 1,
+ SHIBARI_T_NS = 2,
+ SHIBARI_T_MD = 3,
+ SHIBARI_T_MF = 4,
+ SHIBARI_T_CNAME = 5,
+ SHIBARI_T_SOA = 6,
+ SHIBARI_T_MB = 7,
+ SHIBARI_T_MG = 8,
+ SHIBARI_T_MR = 9,
+ SHIBARI_T_NULL = 10,
+ SHIBARI_T_WKS = 11,
+ SHIBARI_T_PTR = 12,
+ SHIBARI_T_HINFO = 13,
+ SHIBARI_T_MINFO = 14,
+ SHIBARI_T_MX = 15,
+ SHIBARI_T_TXT = 16,
+ SHIBARI_T_RP = 17,
+ SHIBARI_T_AFSDB = 18,
+ SHIBARI_T_X25 = 19,
+ SHIBARI_T_ISDN = 20,
+ SHIBARI_T_RT = 21,
+ SHIBARI_T_NSAP = 22,
+ SHIBARI_T_NSAP_PTR = 23,
+ SHIBARI_T_SIG = 24,
+ SHIBARI_T_KEY = 25,
+ SHIBARI_T_PX = 26,
+ SHIBARI_T_GPOS = 27,
+ SHIBARI_T_AAAA = 28,
+ SHIBARI_T_LOC = 29,
+ SHIBARI_T_NXT = 30,
+ SHIBARI_T_EID = 31,
+ SHIBARI_T_NIMLOC = 32,
+ SHIBARI_T_SRV = 33,
+ SHIBARI_T_ATMA = 34,
+ SHIBARI_T_NAPTR = 35,
+ SHIBARI_T_KX = 36,
+ SHIBARI_T_CERT = 37,
+ SHIBARI_T_A6 = 38,
+ SHIBARI_T_DNAME = 39,
+ SHIBARI_T_SINK = 40,
+ SHIBARI_T_OPT = 41,
+ SHIBARI_T_APL = 42,
+ SHIBARI_T_DS = 43,
+ SHIBARI_T_SSHFP = 44,
+ SHIBARI_T_IPSECKEY = 45,
+ SHIBARI_T_RRSIG = 46,
+ SHIBARI_T_NSEC = 47,
+ SHIBARI_T_DNSKEY = 48,
+ SHIBARI_T_DHCID = 49,
+ SHIBARI_T_NSEC3 = 50,
+ SHIBARI_T_NSEC3PARAM = 51,
+ SHIBARI_T_TLSA = 52,
+ SHIBARI_T_SMIMEA = 53,
+ SHIBARI_T_HIP = 55,
+ SHIBARI_T_NINFO = 56,
+ SHIBARI_T_RKEY = 57,
+ SHIBARI_T_TALINK = 58,
+ SHIBARI_T_CDS = 59,
+ SHIBARI_T_CDNSKEY = 60,
+ SHIBARI_T_OPENPGPKEY = 61,
+ SHIBARI_T_CSYNC = 62,
+ SHIBARI_T_ZONEMD = 63,
+ SHIBARI_T_SVCB = 64,
+ SHIBARI_T_HTTPS = 65,
+ SHIBARI_T_SPF = 99,
+ SHIBARI_T_UINFO = 100,
+ SHIBARI_T_UID = 101,
+ SHIBARI_T_GID = 102,
+ SHIBARI_T_UNSPEC = 103,
+ SHIBARI_T_NID = 104,
+ SHIBARI_T_L32 = 105,
+ SHIBARI_T_L64 = 106,
+ SHIBARI_T_LP = 107,
+ SHIBARI_T_EUI48 = 108,
+ SHIBARI_T_EUI64 = 109,
+ SHIBARI_T_TKEY = 249,
+ SHIBARI_T_TSIG = 250,
+ SHIBARI_T_IXFR = 251,
+ SHIBARI_T_AXFR = 252,
+ SHIBARI_T_MAILB = 253,
+ SHIBARI_T_MAILA = 254,
+ SHIBARI_T_ANY = 255,
+ SHIBARI_T_URI = 256,
+ SHIBARI_T_CAA = 257,
+ SHIBARI_T_AVC = 258,
+ SHIBARI_T_DOA = 259,
+ SHIBARI_T_AMIRELAY = 260,
+ SHIBARI_T_RESINFO = 261,
+ SHIBARI_T_TA = 32768,
+ SHIBARI_T_DLV = 32769
+} ;
+
+#endif
diff --git a/src/include/shibari/dcache.h b/src/include/shibari/dcache.h
new file mode 100644
index 0000000..6e0d0ab
--- /dev/null
+++ b/src/include/shibari/dcache.h
@@ -0,0 +1,54 @@
+/* ISC license. */
+
+#ifndef SHIBARI_DCACHE_H
+#define SHIBARI_DCACHE_H
+
+#include <stdint.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+
+#define DCACHE_MAGIC "--DCACHE--\n"
+
+typedef struct dcache_key_s dcache_key_t, *dcache_key_t_ref ;
+struct dcache_key_s
+{
+ char *s ;
+ uint16_t len ;
+} ;
+
+typedef struct dcache_node_s dcache_node_t, *dcache_node_t_ref ;
+struct dcache_node_s
+{
+ dcache_key_t key ;
+ uint16_t datalen ;
+ tain entry ;
+ tain expire ;
+} ;
+
+typedef struct dcache_s dcache_t, *dcache_t_ref ;
+struct dcache_s
+{
+ gensetdyn storage ; /* dcache_node_t */
+ avltree by_key ;
+ avltree by_entry ;
+ avltree by_expire ;
+ uint64_t size ;
+ uint64_t motion ;
+} ;
+#define DCACHE_ZERO { .storage = GENSETDYN_ZERO, .by_key = AVLTREE_ZERO, .by_entry = AVLTREE_ZERO, .by_expire = AVLTREE_ZERO, .size = 0, .motion = 0 }
+
+extern void dcache_init (dcache_t *, uint64_t) ;
+extern dcache_node_t *dcache_search (dcache_t *, char const *, uint16_t) ;
+extern int dcache_add (dcache_t *, uint64_t, char const *, uint16_t, char const *, uint16_t, tain const *, tain const *) ;
+#define dcache_add_g(d, max, key, keylen, data, datalen, expire) dcache_add(d, max, key, keylen, data, datalen, (expire), &STAMP)
+extern void dcache_clean_expired (dcache_t *, tain const *) ;
+#define dcache_clean_expired_g(d) dcache_clean_expired((d), &STAMP)
+extern void dcache_free (dcache_t *) ;
+
+extern int dcache_save (dcache_t const *, char const *) ;
+extern int dcache_load (dcache_t *, uint64_t, char const *) ;
+
+#endif
diff --git a/src/include/shibari/log.h b/src/include/shibari/log.h
new file mode 100644
index 0000000..a01e499
--- /dev/null
+++ b/src/include/shibari/log.h
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#ifndef SHIBARI_LOG_H
+#define SHIBARI_LOG_H
+
+#include <stdint.h>
+
+#include <skalibs/ip46.h>
+
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+
+extern void shibari_log_start (uint32_t, ip46 const *, uint16_t) ;
+extern void shibari_log_exit (uint32_t, int) ;
+
+extern void shibari_log_query (uint32_t, s6dns_domain_t const *, uint16_t) ;
+extern void shibari_log_queryplus (uint32_t, s6dns_domain_t const *, uint16_t, ip46 const *, uint16_t) ;
+extern void shibari_log_answer (uint32_t, s6dns_message_header_t const *, uint16_t) ;
+
+#endif
diff --git a/src/include/shibari/packet.h b/src/include/shibari/packet.h
new file mode 100644
index 0000000..1f4fa93
--- /dev/null
+++ b/src/include/shibari/packet.h
@@ -0,0 +1,39 @@
+/* ISC license. */
+
+#ifndef SHIBARI_PACKET_H
+#define SHIBARI_PACKET_H
+
+#include <stdint.h>
+
+#include <skalibs/cdb.h>
+#include <skalibs/tai.h>
+
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+
+#include <shibari/tdb.h>
+
+typedef struct shibari_packet_s shibari_packet, *shibari_packet_ref ;
+struct shibari_packet_s
+{
+ s6dns_message_header_t hdr ;
+ char *buf ;
+ uint16_t max ;
+ uint16_t pos ;
+ uint8_t flagtcp : 1 ;
+} ;
+#define SHIBARI_PACKET_ZERO { .hdr = S6DNS_MESSAGE_HEADER_ZERO, .buf = "", .pos = 0, .flagtcp = 0 }
+#define SHIBARI_PACKET_INIT(rbuf, rmax, tcp) { .hdr = S6DNS_MESSAGE_HEADER_ZERO, .buf = tcp ? rbuf + 2 : rbuf, .max = tcp ? rmax - 2 : rmax, .pos = 0, .flagtcp = !!tcp }
+
+extern void shibari_packet_init (shibari_packet *, char *, uint32_t, int) ;
+
+extern void shibari_packet_begin (shibari_packet *, uint16_t, s6dns_domain_t const *, uint16_t) ;
+extern void shibari_packet_end (shibari_packet *) ;
+
+extern int shibari_packet_add_rr (shibari_packet *, shibari_tdb_entry const *, int, uint16_t, unsigned int) ;
+extern unsigned int shibari_packet_add_glue (shibari_packet *, cdb const *, char const *, uint16_t, uint16_t, char const *, uint16_t, uint16_t, uint16_t, char const *, tain const *) ;
+extern unsigned int shibari_packet_assert_authority (shibari_packet *, cdb const *, char const *, uint16_t, uint16_t, char const *, tain const *) ;
+
+extern unsigned int shibari_packet_tdb_answer_query (shibari_packet *, cdb const *, s6dns_message_header_t const *, s6dns_domain_t const *, uint16_t, char const *, tain const *) ;
+
+#endif
diff --git a/src/include/shibari/server.h b/src/include/shibari/server.h
new file mode 100644
index 0000000..db5af40
--- /dev/null
+++ b/src/include/shibari/server.h
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#ifndef SHIBARI_SERVER_H
+#define SHIBARI_SERVER_H
+
+#include <shibari/log.h>
+#include <shibari/tdb.h>
+#include <shibari/packet.h>
+
+#endif
diff --git a/src/include/shibari/shibari.h b/src/include/shibari/shibari.h
new file mode 100644
index 0000000..a9242d3
--- /dev/null
+++ b/src/include/shibari/shibari.h
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#ifndef SHIBARI_H
+#define SHIBARI_H
+
+#include <shibari/common.h>
+#include <shibari/client.h>
+#include <shibari/cache.h>
+#include <shibari/server.h>
+
+#endif
diff --git a/src/include/shibari/tdb.h b/src/include/shibari/tdb.h
new file mode 100644
index 0000000..f61df5b
--- /dev/null
+++ b/src/include/shibari/tdb.h
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#ifndef SHIBARI_TDB_H
+#define SHIBARI_TDB_H
+
+#include <stdint.h>
+
+#include <skalibs/cdb.h>
+#include <skalibs/tai.h>
+
+#include <s6-dns/s6dns-message.h>
+
+typedef struct shibari_tdb_entry_s shibari_tdb_entry, *shibari_tdb_entry_ref ;
+struct shibari_tdb_entry_s
+{
+ uint16_t type ;
+ uint16_t len ;
+ uint32_t ttl ;
+ uint32_t flags ;
+ cdb_data key ;
+ cdb_data data ;
+} ;
+
+extern int shibari_tdb_entry_parse (shibari_tdb_entry *, char const *, uint16_t, uint16_t, unsigned int, char const *, tain const *) ;
+extern int shibari_tdb_read_entry (cdb const *, cdb_find_state *, shibari_tdb_entry *, char const *, uint16_t, uint16_t, unsigned int, char const *, tain const *, uint32_t *) ;
+extern int shibari_tdb_extract_domain (shibari_tdb_entry const *, cdb_data *) ;
+extern int shibari_tdb_find_authority (cdb const *, char const *, uint16_t, char const *, tain const *, int *) ;
+
+#endif
diff --git a/src/include/shibari/util.h b/src/include/shibari/util.h
new file mode 100644
index 0000000..82092a3
--- /dev/null
+++ b/src/include/shibari/util.h
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#ifndef SHIBARI_UTIL_H
+#define SHIBARI_UTIL_H
+
+#include <stdint.h>
+
+extern char const *shibari_util_qtype_str (uint16_t) ;
+extern uint16_t shibari_util_qtype_num (char const *) ;
+extern char const *shibari_util_rcode_str (uint16_t) ;
+
+extern int shibari_util_get_prefixlen (char const *, uint16_t, char const *, uint16_t) ;
+
+#endif
diff --git a/src/server/deps-exe/shibari-server-tcp b/src/server/deps-exe/shibari-server-tcp
new file mode 100644
index 0000000..c25f645
--- /dev/null
+++ b/src/server/deps-exe/shibari-server-tcp
@@ -0,0 +1,4 @@
+${LIBSHIBARI_SERVER}
+${LIBSHIBARI_COMMON}
+-ls6dns
+-lskarnet
diff --git a/src/server/deps-exe/shibari-server-udp b/src/server/deps-exe/shibari-server-udp
new file mode 100644
index 0000000..0c1f81d
--- /dev/null
+++ b/src/server/deps-exe/shibari-server-udp
@@ -0,0 +1,6 @@
+${LIBSHIBARI_SERVER}
+${LIBSHIBARI_COMMON}
+-ls6dns
+-ls6
+-lskarnet
+${SOCKET_LIB}
diff --git a/src/server/deps-lib/shibari-server b/src/server/deps-lib/shibari-server
new file mode 100644
index 0000000..7c5b981
--- /dev/null
+++ b/src/server/deps-lib/shibari-server
@@ -0,0 +1,13 @@
+shibari_packet_init.o
+shibari_packet_begin.o
+shibari_packet_end.o
+shibari_packet_add_rr.o
+shibari_tdb_entry_parse.o
+shibari_tdb_extract_domain.o
+shibari_tdb_find_authority.o
+shibari_tdb_read_entry.o
+shibari_packet_add_glue.o
+shibari_packet_assert_authority.o
+shibari_packet_tdb_answer_query.o
+-ls6dns
+-lskarnet
diff --git a/src/server/shibari-server-tcp.c b/src/server/shibari-server-tcp.c
new file mode 100644
index 0000000..e646a44
--- /dev/null
+++ b/src/server/shibari-server-tcp.c
@@ -0,0 +1,235 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/types.h>
+#include <skalibs/strerr.h>
+#include <skalibs/buffer.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/sig.h>
+#include <skalibs/tai.h>
+#include <skalibs/ip46.h>
+#include <skalibs/cdb.h>
+#include <skalibs/unix-timed.h>
+
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+
+#include <shibari/common.h>
+#include <shibari/server.h>
+
+#define PROGNAME "shibari-server-tcp"
+#define USAGE PROGNAME " [ -v verbosity ] [ -f cdbfile ] [ -r timeout ] [ -w timeout ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+#define QMAX 2048
+#define RMAX 65535
+
+static uint32_t verbosity = 1 ;
+
+static inline void get_socket_info (ip46 *localip, uint16_t *localport, ip46 *remoteip, uint16_t *remoteport)
+{
+ char const *x = getenv("PROTO") ;
+ if (!x) strerr_dienotset(100, "PROTO") ;
+ {
+ size_t protolen = strlen(x) ;
+ char var[protolen + 11] ;
+ memcpy(var, x, protolen) ;
+ memcpy(var + protolen, "LOCALIP", 8) ;
+ x = getenv(var) ;
+ if (!x) strerr_dienotset(100, var) ;
+ if (!ip46_scan(x, localip)) strerr_dieinvalid(100, var) ;
+ memcpy(var + protolen + 5, "PORT", 5) ;
+ x = getenv(var) ;
+ if (!x) strerr_dienotset(100, var) ;
+ if (!uint160_scan(x, localport)) strerr_dieinvalid(100, var) ;
+ memcpy(var + protolen, "REMOTEIP", 9) ;
+ x = getenv(var) ;
+ if (!x) strerr_dienotset(100, var) ;
+ if (!ip46_scan(x, remoteip)) strerr_dieinvalid(100, var) ;
+ memcpy(var + protolen + 6, "PORT", 5) ;
+ x = getenv(var) ;
+ if (!x) strerr_dienotset(100, var) ;
+ if (!uint160_scan(x, remoteport)) strerr_dieinvalid(100, var) ;
+ }
+}
+
+static void add (shibari_packet *pkt, shibari_tdb_entry const *entry, int prefixlen, uint16_t id, s6dns_domain_t const *zone, tain const *deadline)
+{
+ if (!shibari_packet_add_rr(pkt, entry, prefixlen, 0, 2))
+ {
+ shibari_packet_end(pkt) ;
+ if (!buffer_timed_put_g(buffer_1, pkt->buf - 2, pkt->pos + 2, deadline))
+ strerr_diefu1sys(111, "write to stdout") ;
+ shibari_packet_begin(pkt, id, zone, SHIBARI_T_AXFR) ;
+ if (!shibari_packet_add_rr(pkt, entry, prefixlen, 0, 2))
+ strerr_dief1x(101, "can't happen: record too long to fit in single packet") ;
+ }
+}
+
+static inline int axfr (char const *axfrok, char const *loc, cdb const *tdb, s6dns_message_header_t const *qhdr, s6dns_domain_t const *zone, shibari_packet *pkt, tain const *deadline, tain const *wstamp)
+{
+ shibari_tdb_entry soa ;
+ shibari_tdb_entry cur ;
+ uint32_t pos = CDB_TRAVERSE_INIT() ;
+ if (!axfrok) return 5 ;
+ if (axfrok[0] != '*')
+ {
+ s6dns_domain_t decoded = *zone ;
+ unsigned int zonelen ;
+ size_t len = strlen(axfrok) + 1 ;
+ char buf[256] ;
+ if (!s6dns_domain_decode(&decoded)) return 1 ;
+ zonelen = s6dns_domain_tostring(buf, 256, &decoded) ;
+ while (len >= zonelen)
+ {
+ if (!strncmp(buf, axfrok, zonelen) && (!axfrok[zonelen] || strchr("/,; \t\n", axfrok[zonelen]))) break ;
+ axfrok += zonelen + 1 ;
+ len -= zonelen + 1 ;
+ }
+ if (len < zonelen) return 5 ;
+ }
+
+ {
+ cdb_find_state state = CDB_FIND_STATE_ZERO ;
+ int r = shibari_tdb_read_entry(tdb, &state, &soa, zone->s, zone->len, SHIBARI_T_SOA, 0, loc, wstamp, 0) ;
+ if (r == -1) return 2 ;
+ if (!r) return 9 ;
+ }
+
+ shibari_packet_begin(pkt, qhdr->id, zone, SHIBARI_T_AXFR) ;
+ pkt->hdr.aa = 1 ;
+ add(pkt, &soa, 0, qhdr->id, zone, deadline) ;
+
+ for (;;)
+ {
+ cdb_data data ;
+ int prefixlen ;
+ int r = cdb_traverse_next(tdb, &cur.key, &data, &pos) ;
+ if (r == -1) return 2 ;
+ if (!r) break ;
+ prefixlen = shibari_util_get_prefixlen(cur.key.s, cur.key.len, zone->s, zone->len) ;
+ if (prefixlen == -1) continue ;
+ r = shibari_tdb_entry_parse(&cur, data.s, data.len, SHIBARI_T_ANY, 2, loc, wstamp) ;
+ if (r == -1) return 2 ;
+ if (!r) continue ;
+ if (cur.type == SHIBARI_T_SOA) continue ;
+ add(pkt, &cur, prefixlen, qhdr->id, zone, deadline) ;
+ }
+
+ add(pkt, &soa, 0, qhdr->id, zone, deadline) ;
+ shibari_packet_end(pkt) ;
+ return 0 ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ cdb tdb = CDB_ZERO ;
+ char const *axfrok = getenv("AXFR") ;
+ char const *loc = getenv("LOC") ;
+ tain rtto = TAIN_INFINITE_RELATIVE, wtto = TAIN_INFINITE_RELATIVE ;
+ ip46 localip, remoteip ;
+ uint16_t localport, remoteport ;
+ char progbuf[sizeof(PROGNAME) + 5 + PID_FMT] = PROGNAME ": pid " ;
+ char buf[RMAX + 2] ;
+ shibari_packet pkt = SHIBARI_PACKET_INIT(buf, RMAX + 2, 1) ;
+ PROG = "shibari-server-tcp" ;
+
+ {
+ size_t pos = sizeof(PROGNAME) + 5 ;
+ pos += pid_fmt(progbuf + pos, getpid()) ;
+ progbuf[pos++] = 0 ;
+ }
+
+ {
+ char const *tdbfile = "data.cdb" ;
+ uint32_t r = 0, w = 0 ;
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "v:f:r:w:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'v' : if (!uint320_scan(l.arg, &verbosity)) dieusage() ; break ;
+ case 'f' : tdbfile = l.arg ; break ;
+ case 'r' : if (!uint320_scan(l.arg, &r)) dieusage() ; break ;
+ case 'w' : if (!uint320_scan(l.arg, &w)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (r) tain_from_millisecs(&rtto, r) ;
+ if (w) tain_from_millisecs(&wtto, w) ;
+ get_socket_info(&localip, &localport, &remoteip, &remoteport) ;
+ PROG = progbuf ;
+ if (!cdb_init(&tdb, tdbfile)) strerr_diefu2sys(111, "open DNS database file ", tdbfile) ;
+ }
+
+ if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ tain_now_set_stopwatch_g() ;
+ shibari_log_start(verbosity, &remoteip, remoteport) ;
+
+ for (;;)
+ {
+ tain wstamp ;
+ size_t w ;
+ tain deadline ;
+ s6dns_message_header_t hdr ;
+ s6dns_message_counts_t counts ;
+ s6dns_domain_t name ;
+ unsigned int rcode ;
+ uint16_t qtype ;
+ uint16_t len ;
+ tain_add_g(&deadline, &rtto) ;
+ w = buffer_timed_get_g(buffer_0, buf, 2, &deadline) ;
+ if (w == 1) strerr_dief1x(1, "invalid request") ;
+ if (!w)
+ {
+ if (errno != EPIPE && errno != ETIMEDOUT)
+ strerr_diefu1sys(111, "read from stdin") ;
+ else break ;
+ }
+ uint16_unpack_big(buf, &len) ;
+ if (len > QMAX) strerr_dief1x(1, "request too large") ;
+ if (buffer_timed_get_g(buffer_0, buf, len, &deadline) < len)
+ strerr_diefu1sys(111, "read from stdin") ;
+
+ if (!s6dns_message_parse_init(&hdr, &counts, buf, len, &rcode))
+ strerr_diefu1sys(111, "parse message") ;
+ if (hdr.opcode) { rcode = 4 ; goto answer ; }
+ if (!s6dns_message_parse_question(&counts, &name, &qtype, buf, len, &rcode) || !s6dns_domain_encode(&name))
+ {
+ rcode = errno == ENOTSUP ? 4 : 1 ;
+ goto answer ;
+ }
+ shibari_log_query(verbosity, &name, qtype) ;
+ tain_add_g(&deadline, &wtto) ;
+ tain_wallclock_read(&wstamp) ;
+ rcode = qtype == SHIBARI_T_AXFR ?
+ axfr(axfrok, loc, &tdb, &hdr, &name, &pkt, &deadline, &wstamp) :
+ shibari_packet_tdb_answer_query(&pkt, &tdb, &hdr, &name, qtype, loc, &wstamp) ;
+
+ answer:
+ if (rcode && rcode != 3)
+ {
+ shibari_packet_begin(&pkt, hdr.id, &name, qtype) ;
+ pkt.hdr.rcode = rcode ;
+ shibari_packet_end(&pkt) ;
+ }
+ shibari_log_answer(verbosity, &pkt.hdr, pkt.pos) ;
+ if (!buffer_timed_put_g(buffer_1, buf, pkt.pos + 2, &deadline)
+ || !buffer_timed_flush_g(buffer_1, &deadline))
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+
+ shibari_log_exit(verbosity, 0) ;
+ return 0 ;
+}
diff --git a/src/server/shibari-server-udp.c b/src/server/shibari-server-udp.c
new file mode 100644
index 0000000..d834c94
--- /dev/null
+++ b/src/server/shibari-server-udp.c
@@ -0,0 +1,207 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/types.h>
+#include <skalibs/error.h>
+#include <skalibs/strerr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/tai.h>
+#include <skalibs/socket.h>
+#include <skalibs/ip46.h>
+#include <skalibs/cdb.h>
+#include <skalibs/sig.h>
+
+#include <s6/accessrules.h>
+
+#include <shibari/common.h>
+#include <shibari/server.h>
+
+#define USAGE "shibari-server-udp [ -v verbosity ] [ -d notif ] [ -f cdbfile ] [ -i rulesdir | -x rulesfile ] [ -p port ] ip"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+#define VAR "LOC"
+
+static char const *tdbfile = "data.cdb" ;
+static cdb tdb = CDB_ZERO ;
+static cdb rules = CDB_ZERO ;
+static char const *rulesfile = 0 ;
+static unsigned int rulestype = 0 ;
+static int cont = 1 ;
+static uint32_t verbosity = 1 ;
+
+static void on_term (int s)
+{
+ (void)s ;
+ cont = 0 ;
+}
+
+static void on_hup (int s)
+{
+ cdb newtdb = CDB_ZERO ;
+ (void)s ;
+ if (!cdb_init(&newtdb, tdbfile))
+ {
+ if (verbosity) strerr_warnwu2sys("reopen DNS data file ", tdbfile) ;
+ }
+ else
+ {
+ cdb_free(&tdb) ;
+ tdb = newtdb ;
+ }
+ if (rulestype == 2)
+ {
+ cdb newrules = CDB_ZERO ;
+ if (!cdb_init(&newrules, rulesfile))
+ {
+ if (verbosity) strerr_warnwu2sys("reopen access rules file ", rulesfile) ;
+ }
+ else
+ {
+ cdb_free(&rules) ;
+ rules = newrules ;
+ }
+ }
+}
+
+static int check_rules (ip46 const *remoteip, s6_accessrules_params_t *params, char const **loc)
+{
+ s6_accessrules_result_t r ;
+ params->env.len = 0 ;
+ params->exec.len = 0 ;
+ r = rulestype == 2 ?
+ s6_accessrules_ip46_cdb(remoteip, &rules, params) :
+ s6_accessrules_ip46_fs(remoteip, rulesfile, params) ;
+ if (r != S6_ACCESSRULES_ALLOW) return 0 ;
+
+ if (params->env.len)
+ {
+ char const *p ;
+ if (params->env.s[params->env.len - 1])
+ {
+ if (verbosity)
+ {
+ char fmt[IP46_FMT] ;
+ fmt[ip46_fmt(fmt, remoteip)] = 0 ;
+ strerr_warnw6x("invalid environment parameters in rules ", rulestype == 2 ? "cdb " : "directory ", rulesfile, " for ip ", fmt, " - denying connection") ;
+ }
+ return 0 ;
+ }
+ p = memmem(params->env.s, params->env.len - 1, VAR "=", sizeof(VAR)) ;
+ if (p && (p == params->env.s || !p[-1])) *loc = p + sizeof(VAR) ;
+ }
+ return 1 ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ;
+ int s ;
+ unsigned int notif = 0 ;
+ char buf[512] ;
+ shibari_packet pkt = SHIBARI_PACKET_INIT(buf, 512, 0) ;
+ uint16_t localport = 53 ;
+ ip46 localip ;
+
+ PROG = "shibari-server-udp" ;
+
+ {
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "v:d:f:i:x:p:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'v' : if (!uint320_scan(l.arg, &verbosity)) dieusage() ; break ;
+ case 'd' : if (!uint0_scan(l.arg, &notif)) dieusage() ; break ;
+ case 'f' : tdbfile = l.arg ; break ;
+ case 'i' : rulesfile = l.arg ; rulestype = 1 ; break ;
+ case 'x' : rulesfile = l.arg ; rulestype = 2 ; break ;
+ case 'p' : if (!uint160_scan(l.arg, &localport)) dieusage() ; break ;
+ default : strerr_dieusage(10, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+
+ if (!argc) dieusage() ;
+ if (!ip46_scan(argv[0], &localip)) dieusage() ;
+
+ if (notif)
+ {
+ if (notif < 3) strerr_dief1x(100, "notification fd cannot be 0, 1 or 2") ;
+ if (fcntl(notif, F_GETFD) == -1) strerr_diefu1sys(111, "check notification fd") ;
+ }
+
+ close(0) ;
+ close(1) ;
+ s = socket_udp46_b(ip46_is6(&localip)) ;
+ if (s == -1) strerr_diefu1sys(111, "create socket") ;
+ if (socket_bind46_reuse(s, &localip, localport) == -1) strerr_diefu1sys(111, "bind socket") ;
+
+ if (!cdb_init(&tdb, tdbfile)) strerr_diefu2sys(111, "open cdb file ", tdbfile) ;
+ if (rulestype == 2 && !cdb_init(&rules, rulesfile)) strerr_diefu2sys(111, "open rules file ", rulesfile) ;
+ if (!sig_catch(SIGHUP, &on_hup)) strerr_diefu1sys(111, "catch SIGHUP") ;
+ if (!sig_catch(SIGTERM, &on_term)) strerr_diefu1sys(111, "catch SIGTERM") ;
+
+ shibari_log_start(verbosity, &localip, localport) ;
+ if (notif)
+ {
+ write(notif, "\n", 1) ;
+ close(notif) ;
+ }
+
+ for (; cont ; sig_unblock(SIGHUP))
+ {
+ tain wstamp ;
+ char const *loc = 0 ;
+ s6dns_message_header_t hdr ;
+ s6dns_message_counts_t counts ;
+ s6dns_domain_t name ;
+ unsigned int rcode ;
+ ssize_t r ;
+ uint16_t qtype ;
+ uint16_t remoteport ;
+ ip46 remoteip ;
+
+ r = socket_recv46(s, buf, 512, &remoteip, &remoteport) ;
+ if (r == -1) strerr_diefu1sys(111, "recv from socket") ;
+ if (!r) strerr_dief1x(111, "huh? got EOF on a connection-less socket") ;
+ sig_block(SIGHUP) ;
+ if (rulestype && !check_rules(&remoteip, &params, &loc)) continue ;
+ if (!s6dns_message_parse_init(&hdr, &counts, buf, r, &rcode)) continue ;
+ if (hdr.opcode) { rcode = 4 ; goto answer ; }
+ if (!s6dns_message_parse_question(&counts, &name, &qtype, buf, r, &rcode) || !s6dns_domain_encode(&name))
+ {
+ rcode = errno == ENOTSUP ? 4 : 1 ;
+ goto answer ;
+ }
+ shibari_log_queryplus(verbosity, &name, qtype, &remoteip, remoteport) ;
+ tain_wallclock_read(&wstamp) ;
+ rcode = shibari_packet_tdb_answer_query(&pkt, &tdb, &hdr, &name, qtype, loc, &wstamp) ;
+
+ answer:
+ if (rcode && rcode != 3)
+ {
+ shibari_packet_begin(&pkt, hdr.id, &name, qtype) ;
+ pkt.hdr.rcode = rcode ;
+ shibari_packet_end(&pkt) ;
+ }
+ shibari_log_answer(verbosity, &pkt.hdr, pkt.pos) ;
+ if (socket_send46(s, buf, pkt.pos, &remoteip, remoteport) < pkt.pos && verbosity)
+ strerr_warnwu1sys("send answer") ;
+ }
+
+ shibari_log_exit(verbosity, 0) ;
+ return 0 ;
+}
diff --git a/src/server/shibari_packet_add_glue.c b/src/server/shibari_packet_add_glue.c
new file mode 100644
index 0000000..4a0abf1
--- /dev/null
+++ b/src/server/shibari_packet_add_glue.c
@@ -0,0 +1,48 @@
+/* ISC license. */
+
+#include <skalibs/cdb.h>
+
+#include <shibari/constants.h>
+#include <shibari/util.h>
+#include <shibari/tdb.h>
+#include <shibari/packet.h>
+
+static int shibari_packet_add_glue_for_rr (shibari_packet *pkt, cdb const *tdb, char const *s, uint16_t len, uint16_t prefixlen, uint16_t offset, char const *loc, tain const *stamp)
+{
+ cdb_find_state state = CDB_FIND_STATE_ZERO ;
+ for (;;)
+ {
+ shibari_tdb_entry entry ;
+ int r = shibari_tdb_read_entry(tdb, &state, &entry, s, len, SHIBARI_T_ANY, 0, loc, stamp, 0) ;
+ if (r == -1) return 2 ;
+ if (!r) break ;
+ if (entry.type != SHIBARI_T_A && entry.type != SHIBARI_T_AAAA) continue ;
+ if (!shibari_packet_add_rr(pkt, &entry, prefixlen, offset, 4))
+ {
+ pkt->hdr.tc = 1 ;
+ return 0 ;
+ }
+ }
+ return -1 ;
+}
+
+unsigned int shibari_packet_add_glue (shibari_packet *pkt, cdb const *tdb, char const *s, uint16_t len, uint16_t qtype, char const *z, uint16_t zlen, uint16_t zoffset, uint16_t wildpos, char const *loc, tain const *stamp)
+{
+ cdb_find_state state = CDB_FIND_STATE_ZERO ;
+ for (;;)
+ {
+ shibari_tdb_entry entry ;
+ cdb_data domain ;
+ int zprefixlen, sprefixlen ;
+ int r = shibari_tdb_read_entry(tdb, &state, &entry, s + wildpos, len - wildpos, qtype, !!wildpos, loc, stamp, 0) ;
+ if (r == -1) return 2 ;
+ if (!r) break ;
+ if (!shibari_tdb_extract_domain(&entry, &domain)) continue ;
+ zprefixlen = shibari_util_get_prefixlen(domain.s, domain.len, z, zlen) ;
+ if (zprefixlen == -1) continue ;
+ sprefixlen = shibari_util_get_prefixlen(domain.s, domain.len, s, len) ;
+ r = shibari_packet_add_glue_for_rr(pkt, tdb, domain.s, domain.len, sprefixlen == -1 ? zprefixlen : sprefixlen, sprefixlen == -1 ? zoffset : 0, loc, stamp) ;
+ if (r >= 0) return r ;
+ }
+ return 0 ;
+}
diff --git a/src/server/shibari_packet_add_rr.c b/src/server/shibari_packet_add_rr.c
new file mode 100644
index 0000000..b92e8dd
--- /dev/null
+++ b/src/server/shibari_packet_add_rr.c
@@ -0,0 +1,46 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+
+#include <shibari/constants.h>
+#include <shibari/packet.h>
+
+int shibari_packet_add_rr (shibari_packet *p, shibari_tdb_entry const *entry, int prefixlen, uint16_t offset, unsigned int section)
+{
+ uint16_t *count[4] = { &p->hdr.counts.qd, &p->hdr.counts.an, &p->hdr.counts.ns, &p->hdr.counts.nr } ;
+ uint16_t rrlen = 10 + entry->data.len + (entry->flags & 1 ? 2 : 0) + (prefixlen >= 0 ? prefixlen + 2 : entry->key.len) ;
+ if (p->max - p->pos < rrlen) return 0 ;
+ if (entry->flags & 1)
+ {
+ p->buf[p->pos++] = 1 ;
+ p->buf[p->pos++] = '*' ;
+ }
+ if (prefixlen >= 0)
+ {
+ memcpy(p->buf + p->pos, entry->key.s, prefixlen) ;
+ p->pos += prefixlen ;
+ uint16_pack_big(p->buf + p->pos, 49164 + offset) ;
+ p->pos += 2 ;
+ }
+ else
+ {
+ memcpy(p->buf + p->pos, entry->key.s, entry->key.len) ;
+ p->pos += entry->key.len ;
+ }
+ uint16_pack_big(p->buf + p->pos, entry->type) ;
+ p->pos += 2 ;
+ uint16_pack_big(p->buf + p->pos, SHIBARI_C_IN) ;
+ p->pos += 2 ;
+ uint32_pack_big(p->buf + p->pos, entry->ttl) ;
+ p->pos += 4 ;
+ uint16_pack_big(p->buf + p->pos, entry->data.len) ;
+ p->pos += 2 ;
+ memcpy(p->buf + p->pos, entry->data.s, entry->data.len) ;
+ p->pos += entry->data.len ;
+ (*count[section-1])++ ;
+ return 1 ;
+}
diff --git a/src/server/shibari_packet_assert_authority.c b/src/server/shibari_packet_assert_authority.c
new file mode 100644
index 0000000..18c8299
--- /dev/null
+++ b/src/server/shibari_packet_assert_authority.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <skalibs/cdb.h>
+
+#include <shibari/constants.h>
+#include <shibari/util.h>
+#include <shibari/tdb.h>
+#include <shibari/packet.h>
+
+unsigned int shibari_packet_assert_authority (shibari_packet *pkt, cdb const *tdb, char const *z, uint16_t zlen, uint16_t zoffset, char const *loc, tain const *stamp)
+{
+ cdb_find_state state = CDB_FIND_STATE_ZERO ;
+ shibari_tdb_entry soa ;
+ int r = shibari_tdb_read_entry(tdb, &state, &soa, z, zlen, SHIBARI_T_SOA, 0, loc, stamp, 0) ;
+ if (r <= 0) return 2 ;
+ if (!shibari_packet_add_rr(pkt, &soa, 0, zoffset, 3)) pkt->hdr.tc = 1 ;
+ return 0 ;
+}
diff --git a/src/server/shibari_packet_begin.c b/src/server/shibari_packet_begin.c
new file mode 100644
index 0000000..5ea7b16
--- /dev/null
+++ b/src/server/shibari_packet_begin.c
@@ -0,0 +1,32 @@
+/* ISC license. */
+
+#include <string.h>
+
+#include <skalibs/uint16.h>
+
+#include <shibari/constants.h>
+#include <shibari/packet.h>
+
+void shibari_packet_begin (shibari_packet *p, uint16_t id, s6dns_domain_t const *q, uint16_t qtype)
+{
+ p->hdr.id = id ;
+ p->hdr.qr = 1 ;
+ p->hdr.opcode = 0 ;
+ p->hdr.aa = 0 ;
+ p->hdr.tc = 0 ;
+ p->hdr.rd = 0 ;
+ p->hdr.ra = 0 ;
+ p->hdr.z = 0 ;
+ p->hdr.rcode = 0 ;
+ p->hdr.counts.qd = 1 ;
+ p->hdr.counts.an = 0 ;
+ p->hdr.counts.ns = 0 ;
+ p->hdr.counts.nr = 0 ;
+ p->pos = 12 ;
+ memcpy(p->buf + p->pos, q->s, q->len) ;
+ p->pos += q->len ;
+ uint16_pack_big(p->buf + p->pos, qtype) ;
+ p->pos += 2 ;
+ uint16_pack_big(p->buf + p->pos, SHIBARI_C_IN) ;
+ p->pos += 2 ;
+}
diff --git a/src/server/shibari_packet_end.c b/src/server/shibari_packet_end.c
new file mode 100644
index 0000000..41aca87
--- /dev/null
+++ b/src/server/shibari_packet_end.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+
+#include <s6-dns/s6dns-message.h>
+
+#include <shibari/packet.h>
+
+void shibari_packet_end (shibari_packet *p)
+{
+ s6dns_message_header_pack(p->buf, &p->hdr) ;
+ if (p->flagtcp) uint16_pack_big(p->buf - 2, p->pos) ;
+}
diff --git a/src/server/shibari_packet_init.c b/src/server/shibari_packet_init.c
new file mode 100644
index 0000000..a0aff97
--- /dev/null
+++ b/src/server/shibari_packet_init.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <s6-dns/s6dns-message.h>
+
+#include <shibari/packet.h>
+
+void shibari_packet_init (shibari_packet *p, char *buf, uint32_t max, int istcp)
+{
+ p->hdr = s6dns_message_header_zero ;
+ p->buf = istcp ? buf + 2 : buf ;
+ p->max = istcp ? max - 2 : max ;
+ p->pos = 0 ;
+ p->flagtcp = !!istcp ;
+}
diff --git a/src/server/shibari_packet_tdb_answer_query.c b/src/server/shibari_packet_tdb_answer_query.c
new file mode 100644
index 0000000..a22927f
--- /dev/null
+++ b/src/server/shibari_packet_tdb_answer_query.c
@@ -0,0 +1,93 @@
+/* ISC license. */
+
+#include <skalibs/cdb.h>
+
+#include <shibari/constants.h>
+#include <shibari/tdb.h>
+#include <shibari/packet.h>
+
+static unsigned int childzone (shibari_packet *pkt, cdb const *tdb, s6dns_domain_t const *q, char const *loc, tain const *stamp, uint16_t nplen, uint16_t zplen)
+{
+ cdb_find_state state = CDB_FIND_STATE_ZERO ;
+ unsigned int gr ;
+ for (;;)
+ {
+ shibari_tdb_entry ns ;
+ int r = shibari_tdb_read_entry(tdb, &state, &ns, q->s + nplen, q->len - nplen, SHIBARI_T_NS, 0, loc, stamp, 0) ;
+ if (r == -1) return 2 ;
+ if (!r) break ;
+ r = shibari_packet_add_rr(pkt, &ns, nplen, 0, 3) ;
+ if (!r) { pkt->hdr.tc = 1 ; goto end ; }
+ }
+ gr = shibari_packet_add_glue(pkt, tdb, q->s + nplen, q->len - nplen, SHIBARI_T_NS, q->s + zplen, q->len - zplen, zplen, 0, loc, stamp) ;
+ if (gr > 0) return gr ;
+ end:
+ shibari_packet_end(pkt) ;
+ return 0 ;
+}
+
+unsigned int shibari_packet_tdb_answer_query (shibari_packet *pkt, cdb const *tdb, s6dns_message_header_t const *qhdr, s6dns_domain_t const *q, uint16_t qtype, char const *loc, tain const *stamp)
+{
+ unsigned int rcode = 0 ;
+ cdb_find_state state = CDB_FIND_STATE_ZERO ;
+ uint32_t flagyxdomain = 0 ;
+ int nplen, zplen ;
+ uint16_t gluetype = 0 ;
+ uint16_t wildpos = 0 ;
+
+ shibari_packet_begin(pkt, qhdr->id, q, qtype) ;
+ pkt->hdr.rd = qhdr->rd ;
+ zplen = shibari_tdb_find_authority(tdb, q->s, q->len, loc, stamp, &nplen) ;
+ switch (zplen)
+ {
+ case -2 : return 9 ;
+ case -1 : return 2 ;
+ default : break ;
+ }
+ if (nplen >= 0 && nplen < zplen)
+ return childzone(pkt, tdb, q, loc, stamp, nplen, zplen) ;
+
+ pkt->hdr.aa = 1 ; /* we're in the zone, man */
+
+ while (wildpos <= zplen)
+ {
+ for (;;)
+ {
+ shibari_tdb_entry entry ;
+ int r = shibari_tdb_read_entry(tdb, &state, &entry, q->s + wildpos, q->len + wildpos, qtype, !!wildpos, loc, stamp, &flagyxdomain) ;
+ if (r == -1) return 2 ;
+ if (!r) break ;
+ if (!shibari_packet_add_rr(pkt, &entry, 0, 0, 2))
+ {
+ pkt->hdr.tc = 1 ;
+ return 0 ;
+ }
+ switch (entry.type)
+ {
+ case SHIBARI_T_NS :
+ case SHIBARI_T_MX :
+ case SHIBARI_T_CNAME : /* we're not supposed to but meh */
+ gluetype = entry.type ;
+ default : break ;
+ }
+ }
+ if (pkt->hdr.counts.an) break ;
+ wildpos += 1 + q->s[wildpos] ;
+ }
+
+ if (!flagyxdomain) pkt->hdr.rcode = 3 ;
+
+ if (!pkt->hdr.counts.an)
+ {
+ unsigned int r = shibari_packet_assert_authority(pkt, tdb, q->s + zplen, q->len - zplen, zplen, loc, stamp) ;
+ if (r) return r ;
+ }
+ else if (gluetype)
+ {
+ unsigned int r = shibari_packet_add_glue(pkt, tdb, q->s, q->len, gluetype, q->s + zplen, q->len - zplen, zplen, wildpos, loc, stamp) ;
+ if (r) return r ;
+ }
+
+ shibari_packet_end(pkt) ;
+ return rcode ;
+}
diff --git a/src/server/shibari_tdb_entry_parse.c b/src/server/shibari_tdb_entry_parse.c
new file mode 100644
index 0000000..61f076f
--- /dev/null
+++ b/src/server/shibari_tdb_entry_parse.c
@@ -0,0 +1,56 @@
+/* ISC license. */
+
+#include <stdint.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/tai.h>
+
+#include <shibari/constants.h>
+#include <shibari/tdb.h>
+
+int shibari_tdb_entry_parse (shibari_tdb_entry *out, char const *s, uint16_t len, uint16_t qtype, unsigned int wild, char const *loc, tain const *stamp)
+{
+ tai ttd ;
+ uint32_t ttl ;
+ uint32_t flags = 0 ;
+ uint16_t type ;
+ if (len < 15) return -1 ;
+ uint16_unpack_big(s, &type) ;
+ if (qtype != SHIBARI_T_ANY && qtype != type && type != SHIBARI_T_CNAME) return 0 ;
+ s += 3 ; len -= 3 ;
+ switch (s[-1])
+ {
+ case '+' : flags |= 1 ;
+ case '>' :
+ if (len < 14) return -1 ;
+ if (loc && loc[0] && (loc[0] != s[0] || loc[1] != s[1])) return 0 ;
+ s += 2 ; len -= 2 ;
+ break ;
+ case '*' : flags |= 1 ;
+ case '=' : break ;
+ default : return -1 ;
+ }
+ if (wild < 2 && wild != (flags & 1)) return 0 ;
+ uint32_unpack_big(s, &ttl) ;
+ s += 4 ; len -= 4 ;
+ tai_unpack(s, &ttd) ;
+ s += 8 ; len -= 8 ;
+ if (tai_sec(&ttd))
+ {
+ if (!ttl == !tai_less(tain_secp(stamp), &ttd)) return 0 ;
+ if (!ttl)
+ {
+ tai t ;
+ tai_sub(&t, &ttd, tain_secp(stamp)) ;
+ if (tai_sec(&t) < 2) ttl = 2 ;
+ else if (tai_sec(&t) > 3600 && qtype != SHIBARI_T_ANY) ttl = 3600 ;
+ }
+ }
+ out->ttl = ttl ;
+ out->flags = flags ;
+ out->type = type ;
+ out->data.s = s ;
+ out->data.len = len ;
+ return 1 ;
+}
diff --git a/src/server/shibari_tdb_extract_domain.c b/src/server/shibari_tdb_extract_domain.c
new file mode 100644
index 0000000..dfa6009
--- /dev/null
+++ b/src/server/shibari_tdb_extract_domain.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <shibari/constants.h>
+#include <shibari/tdb.h>
+
+int shibari_tdb_extract_domain (shibari_tdb_entry const *entry, cdb_data *domain)
+{
+ switch (entry->type)
+ {
+ case SHIBARI_T_CNAME :
+ case SHIBARI_T_NS :
+ *domain = entry->data ; break ;
+ case SHIBARI_T_MX : domain->s = entry->data.s + 2 ; domain->len = entry->data.len - 2 ; break ;
+ default : return 0 ;
+ }
+ return 1 ;
+}
diff --git a/src/server/shibari_tdb_find_authority.c b/src/server/shibari_tdb_find_authority.c
new file mode 100644
index 0000000..5550f52
--- /dev/null
+++ b/src/server/shibari_tdb_find_authority.c
@@ -0,0 +1,46 @@
+/* ISC license. */
+
+#include <stdint.h>
+
+#include <skalibs/cdb.h>
+
+#include <shibari/constants.h>
+#include <shibari/tdb.h>
+
+static int find_ns_and_soa (cdb const *tdb, char const *s, uint16_t len, char const *loc, tain const *stamp)
+{
+ cdb_find_state state = CDB_FIND_STATE_ZERO ;
+ unsigned int flags = 0 ;
+ for (;;)
+ {
+ shibari_tdb_entry entry ;
+ cdb_data data ;
+ int r = cdb_findnext(tdb, &data, s, len, &state) ;
+ if (r == -1) return -1 ;
+ if (!r) break ;
+ r = shibari_tdb_entry_parse(&entry, data.s, data.len, SHIBARI_T_ANY, 0, loc, stamp) ;
+ if (r == -1) return -1 ;
+ if (!r) continue ;
+ if (entry.type == SHIBARI_T_SOA) flags |= 1 ;
+ else if (entry.type == SHIBARI_T_NS) flags |= 2 ;
+ }
+ return flags ;
+}
+
+int shibari_tdb_find_authority (cdb const *tdb, char const *s, uint16_t len, char const *loc, tain const *stamp, int *npl)
+{
+ uint16_t pos = 0 ;
+ uint16_t zplen = 0 ;
+ int nplen = -1 ;
+ while (pos < len)
+ {
+ int flags = find_ns_and_soa(tdb, s + pos, len - pos, loc, stamp) ;
+ if (flags == -1) return -1 ;
+ if (flags & 2) nplen = pos ;
+ if (flags & 1) { zplen = pos ; break ; }
+ pos += 1 + (uint8_t)s[pos] ;
+ }
+ if (pos >= len) return -2 ; /* out of bailiwick */
+ *npl = nplen ;
+ return zplen ;
+}
diff --git a/src/server/shibari_tdb_read_entry.c b/src/server/shibari_tdb_read_entry.c
new file mode 100644
index 0000000..b2d877c
--- /dev/null
+++ b/src/server/shibari_tdb_read_entry.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <skalibs/cdb.h>
+
+#include <shibari/tdb.h>
+
+int shibari_tdb_read_entry (cdb const *tdb, cdb_find_state *state, shibari_tdb_entry *out, char const *s, uint16_t len, uint16_t qtype, unsigned int wild, char const *loc, tain const *stamp, uint32_t *flags)
+{
+ cdb_data data ;
+ int r = 0 ;
+ while (!r)
+ {
+ r = cdb_findnext(tdb, &data, s, len, state) ;
+ if (r <= 0) return r ;
+ if (flags) *flags |= 1 ;
+ r = shibari_tdb_entry_parse(out, data.s, data.len, qtype, wild, loc, stamp) ;
+ if (r == -1) return -1 ;
+ }
+ out->key.s = s ;
+ out->key.len = len ;
+ return 1 ;
+}