diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2023-12-10 11:48:01 +0000 |
---|---|---|
committer | Laurent Bercot <ska@appnovation.com> | 2023-12-10 11:48:01 +0000 |
commit | b8d0f83e6cea9640a7ee4402c163ad812237355d (patch) | |
tree | 57a64ac8aa0e98c40db8c36e96e7379490e44dbf /src/server | |
download | shibari-b8d0f83e6cea9640a7ee4402c163ad812237355d.tar.gz shibari-b8d0f83e6cea9640a7ee4402c163ad812237355d.tar.xz shibari-b8d0f83e6cea9640a7ee4402c163ad812237355d.zip |
Initial commit
Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/deps-exe/shibari-server-tcp | 4 | ||||
-rw-r--r-- | src/server/deps-exe/shibari-server-udp | 6 | ||||
-rw-r--r-- | src/server/deps-lib/shibari-server | 13 | ||||
-rw-r--r-- | src/server/shibari-server-tcp.c | 235 | ||||
-rw-r--r-- | src/server/shibari-server-udp.c | 207 | ||||
-rw-r--r-- | src/server/shibari_packet_add_glue.c | 48 | ||||
-rw-r--r-- | src/server/shibari_packet_add_rr.c | 46 | ||||
-rw-r--r-- | src/server/shibari_packet_assert_authority.c | 18 | ||||
-rw-r--r-- | src/server/shibari_packet_begin.c | 32 | ||||
-rw-r--r-- | src/server/shibari_packet_end.c | 13 | ||||
-rw-r--r-- | src/server/shibari_packet_init.c | 14 | ||||
-rw-r--r-- | src/server/shibari_packet_tdb_answer_query.c | 93 | ||||
-rw-r--r-- | src/server/shibari_tdb_entry_parse.c | 56 | ||||
-rw-r--r-- | src/server/shibari_tdb_extract_domain.c | 17 | ||||
-rw-r--r-- | src/server/shibari_tdb_find_authority.c | 46 | ||||
-rw-r--r-- | src/server/shibari_tdb_read_entry.c | 22 |
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, ¬if)) 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, ¶ms, &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 ; +} |