summaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2023-12-10 11:48:01 +0000
committerLaurent Bercot <ska@appnovation.com>2023-12-10 11:48:01 +0000
commitb8d0f83e6cea9640a7ee4402c163ad812237355d (patch)
tree57a64ac8aa0e98c40db8c36e96e7379490e44dbf /src/server
downloadshibari-b8d0f83e6cea9640a7ee4402c163ad812237355d.tar.xz
Initial commit
Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat (limited to 'src/server')
-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
16 files changed, 870 insertions, 0 deletions
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 ;
+}