diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rwxr-xr-x | configure | 27 | ||||
-rw-r--r-- | package/deps.mak | 10 | ||||
-rw-r--r-- | package/info | 2 | ||||
-rw-r--r-- | package/modes | 1 | ||||
-rw-r--r-- | package/targets.mak | 3 | ||||
-rw-r--r-- | src/cache/shibari-cache.c | 282 | ||||
-rw-r--r-- | src/config/PARSING-config.txt | 26 | ||||
-rw-r--r-- | src/config/conftree.c | 51 | ||||
-rw-r--r-- | src/config/defaults.c | 70 | ||||
-rw-r--r-- | src/config/deps-exe/shibari-cache-config | 8 | ||||
-rw-r--r-- | src/config/lexparse.c | 230 | ||||
-rw-r--r-- | src/config/node.c | 34 | ||||
-rw-r--r-- | src/config/repo.c | 46 | ||||
-rw-r--r-- | src/config/shibari-cache-config-internal.h | 91 | ||||
-rw-r--r-- | src/config/shibari-cache-config.c | 101 | ||||
-rw-r--r-- | src/config/util.c | 15 |
17 files changed, 993 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore index 0552150..ee5b903 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /libshibari-server.so.xyzzy /shibari-server-tcp /shibari-server-udp +/shibari-cache-config diff --git a/configure b/configure index efee0c9..bbc68e7 100755 --- a/configure +++ b/configure @@ -23,6 +23,7 @@ Fine tuning of the installation directories: --libexecdir=DIR package-scoped executables [EPREFIX/libexec] --libdir=DIR static library files [PREFIX/lib/$package] --includedir=DIR C header files [PREFIX/include] + --sysconfdir=DIR global configuration files [PREFIX/etc] If no --prefix option is given, by default libdir (but not dynlibdir) will be /usr/lib/$package, and includedir will be /usr/include. @@ -133,13 +134,14 @@ LDFLAGS_POST="$LDFLAGS" LDFLAGS= LDFLAGS_NOSHARED= LDFLAGS_SHARED=-shared -prefix=/usr +prefix= exec_prefix='$prefix' dynlibdir='$prefix/lib' libexecdir='$exec_prefix/libexec' bindir='$exec_prefix/bin' libdir='$prefix/lib/$package' includedir='$prefix/include' +sysconfdir='$prefix/etc' sysdeps='$prefix/lib/skalibs/sysdeps' manualsysdeps=false shared=false @@ -169,6 +171,7 @@ for arg ; do --bindir=*) bindir=${arg#*=} ;; --libdir=*) libdir=${arg#*=} ;; --includedir=*) includedir=${arg#*=} ;; + --sysconfdir=*) sysconfdir=${arg#*=} ;; --with-sysdeps=*) sysdeps=${arg#*=} manualsysdeps=true ;; --with-include=*) var=${arg#*=} ; stripdir var ; addincpath="$addincpath -I$var" ;; --with-lib=*) var=${arg#*=} ; stripdir var ; addlibspath="$addlibspath -L$var" ; vpaths="$vpaths $var" ;; @@ -197,9 +200,25 @@ for arg ; do esac done +# Add /usr in the default default case +if test -z "$prefix" ; then + if test "$libdir" = '$prefix/lib/$package' ; then + libdir=/usr/lib/$package + fi + if test "$dynlibdir" = '$prefix/lib' ; then + dynlibdir=/usr/lib + fi + if test "$includedir" = '$prefix/include' ; then + includedir=/usr/include + fi + if test "$sysdeps" = '$prefix/lib/skalibs/sysdeps' ; then + sysdeps=/usr/lib/skalibs/sysdeps + fi +fi + # Expand installation directories stripdir prefix -for i in exec_prefix dynlibdir libexecdir bindir libdir includedir sysdeps sproot ; do +for i in exec_prefix dynlibdir libexecdir bindir libdir includedir sysconfdir sysdeps sproot ; do eval tmp=\${$i} eval $i=$tmp stripdir $i @@ -230,7 +249,6 @@ if $slashpackage ; then else sysdeps=${DESTDIR}${sproot}/package/prog/skalibs/sysdeps fi - prefix= extbinprefix=${exthome}/command dynlibdir=${home}/library.so bindir=${home}/command @@ -378,6 +396,7 @@ libexecdir := $libexecdir bindir := $bindir libdir := $libdir includedir := $includedir +sysconfdir := $sysconfdir sysdeps := $sysdeps slashpackage := $slashpackage sproot := $sproot @@ -453,6 +472,8 @@ else echo "#define ${package_macro_name}_EXTBINPREFIX \"\"" fi echo "#define ${package_macro_name}_LIBEXECPREFIX \"$libexecdir/\"" +echo "#define ${package_macro_name}_SYSCONFPREFIX \"$sysconfdir/\"" + echo echo "#endif" exec 1>&3 3>&- diff --git a/package/deps.mak b/package/deps.mak index ffc2c1a..78ab070 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -16,6 +16,7 @@ src/cache/dcache_init.o src/cache/dcache_init.lo: src/cache/dcache_init.c src/in src/cache/dcache_load.o src/cache/dcache_load.lo: src/cache/dcache_load.c src/include/shibari/dcache.h src/cache/dcache_save.o src/cache/dcache_save.lo: src/cache/dcache_save.c src/include/shibari/dcache.h src/cache/dcache_search.o src/cache/dcache_search.lo: src/cache/dcache_search.c src/cache/dcache-internal.h src/include/shibari/dcache.h +src/cache/shibari-cache.o src/cache/shibari-cache.lo: src/cache/shibari-cache.c src/include/shibari/cache.h src/include/shibari/common.h src/common/shibari_log_answer.o src/common/shibari_log_answer.lo: src/common/shibari_log_answer.c src/include/shibari/log.h src/include/shibari/util.h src/common/shibari_log_exit.o src/common/shibari_log_exit.lo: src/common/shibari_log_exit.c src/include/shibari/log.h src/common/shibari_log_query.o src/common/shibari_log_query.lo: src/common/shibari_log_query.c src/include/shibari/log.h src/include/shibari/util.h @@ -26,6 +27,13 @@ src/common/shibari_util_get_prefixlen.o src/common/shibari_util_get_prefixlen.lo src/common/shibari_util_qtype_num.o src/common/shibari_util_qtype_num.lo: src/common/shibari_util_qtype_num.c src/include/shibari/util.h src/common/shibari_util_qtype_str.o src/common/shibari_util_qtype_str.lo: src/common/shibari_util_qtype_str.c src/include/shibari/util.h src/common/shibari_util_rcode_str.o src/common/shibari_util_rcode_str.lo: src/common/shibari_util_rcode_str.c src/include/shibari/util.h +src/config/conftree.o src/config/conftree.lo: src/config/conftree.c src/config/shibari-cache-config-internal.h +src/config/defaults.o src/config/defaults.lo: src/config/defaults.c src/config/shibari-cache-config-internal.h +src/config/lexparse.o src/config/lexparse.lo: src/config/lexparse.c src/config/shibari-cache-config-internal.h src/include/shibari/config.h +src/config/node.o src/config/node.lo: src/config/node.c src/config/shibari-cache-config-internal.h +src/config/repo.o src/config/repo.lo: src/config/repo.c src/config/shibari-cache-config-internal.h +src/config/shibari-cache-config.o src/config/shibari-cache-config.lo: src/config/shibari-cache-config.c src/config/shibari-cache-config-internal.h src/include/shibari/config.h +src/config/util.o src/config/util.lo: src/config/util.c src/config/shibari-cache-config-internal.h src/server/shibari-server-tcp.o src/server/shibari-server-tcp.lo: src/server/shibari-server-tcp.c src/include/shibari/common.h src/include/shibari/server.h src/server/shibari-server-udp.o src/server/shibari-server-udp.lo: src/server/shibari-server-udp.c src/include/shibari/common.h src/include/shibari/server.h src/server/shibari_packet_add_glue.o src/server/shibari_packet_add_glue.lo: src/server/shibari_packet_add_glue.c src/include/shibari/constants.h src/include/shibari/packet.h src/include/shibari/tdb.h src/include/shibari/util.h @@ -55,6 +63,8 @@ libshibari-common.a.xyzzy: src/common/shibari_log_answer.lo src/common/shibari_l endif libshibari-common.so.xyzzy: EXTRA_LIBS := -ls6dns -lskarnet libshibari-common.so.xyzzy: src/common/shibari_log_answer.lo src/common/shibari_log_exit.lo src/common/shibari_log_query.lo src/common/shibari_log_queryplus.lo src/common/shibari_log_start.lo src/common/shibari_util_qtype_num.lo src/common/shibari_util_qtype_str.lo src/common/shibari_util_rcode_str.lo src/common/shibari_util_canon_domain.lo src/common/shibari_util_get_prefixlen.lo +shibari-cache-config: EXTRA_LIBS := -ls6dns -lskarnet +shibari-cache-config: src/config/shibari-cache-config.o src/config/util.o src/config/node.o src/config/repo.o src/config/conftree.o src/config/defaults.o src/config/lexparse.o ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),) libshibari-server.a.xyzzy: src/server/shibari_packet_init.o src/server/shibari_packet_begin.o src/server/shibari_packet_end.o src/server/shibari_packet_add_rr.o src/server/shibari_tdb_entry_parse.o src/server/shibari_tdb_extract_domain.o src/server/shibari_tdb_find_authority.o src/server/shibari_tdb_read_entry.o src/server/shibari_packet_add_glue.o src/server/shibari_packet_assert_authority.o src/server/shibari_packet_tdb_answer_query.o src/server/shibari_packet_tdb_axfr.o else diff --git a/package/info b/package/info index fc40b23..391cef7 100644 --- a/package/info +++ b/package/info @@ -1,4 +1,4 @@ package=shibari -version=0.0.1.1 +version=0.0.2.0 category=web package_macro_name=SHIBARI diff --git a/package/modes b/package/modes index 94bdf3e..238d332 100644 --- a/package/modes +++ b/package/modes @@ -1,2 +1,3 @@ shibari-server-tcp 0755 shibari-server-udp 0755 +shibari-cache-config 0755 diff --git a/package/targets.mak b/package/targets.mak index 0f2d479..a5a43cc 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -1,6 +1,7 @@ BIN_TARGETS := \ shibari-server-tcp \ -shibari-server-udp +shibari-server-udp \ +shibari-cache-config LIBEXEC_TARGETS := diff --git a/src/cache/shibari-cache.c b/src/cache/shibari-cache.c new file mode 100644 index 0000000..1d88556 --- /dev/null +++ b/src/cache/shibari-cache.c @@ -0,0 +1,282 @@ +/* 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/ip46.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/allreadwrite.h> +#include <skalibs/tai.h> +#include <skalibs/socket.h> +#include <skalibs/ip46.h> +#include <skalibs/cdb.h> +#include <skalibs/sig.h> +#include <skalibs/iopause.h> +#include <skalibs/selfpipe.h> + +#include <s6/accessrules.h> + +#include <shibari/common.h> +#include <shibari/cache.h> + +#define USAGE "shibari-cache [ -U ] [ -v verbosity ] [ -d notif ] [ -D cachedumpfile ] [ -w wtimeout ] [ -i rulesdir | -x rulesfile ] ip[_port]..." +#define dieusage() strerr_dieusage(100, USAGE) + + +static char const *dumpfile = 0 ; + + +typedef struct shibari_ip4_s shibari_ip4 ; +struct shibari_ip4_s +{ + int fd ; + char ip[4] ; +} ; + +typedef struct shibari_ip6_s shibari_ip6 ; +struct shibari_ip6_s +{ + int fd ; + char ip[16] ; +} ; + +static inline void argv_pass1 (char const *const *argv, unsigned int *n4, unsigned int *n6) +{ + char ip[16] ; + for (; *argv ; argv++) + if (ip6_scan(argv, ip)) +#ifdef SKALIBS_IPV6_ENABLED + n6++ ; +#else + strerr_dief1x(100, "IPv6 listening addresses unsupported on this system") ; +#endif + else n4++ ; +} + +static inline void argv_pass2 (char const *const *argv, shibari_ip4 *ip4, shibari_ip6 *ip6, uint16_t *ports) +{ + unsigned int i4 = 0, i6 = 0 ; + char ip[16] ; + size_t len ; + for (; *argv ; argv++) + { + len = ip6_scan(argv, ip) ; + if (len) + { + if (argv[0][len] == '_') + { + uint16_t port ; + if (!uint160_scan(*argv + len + 1)) + strerr_dief + } + } + } +} + +static inline void reload_cdbs (void) +{ + cdb newtdb = CDB_ZERO ; + 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 ; +} + +static inline void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read selfpipe") ; + case 0 : return ; + case SIGTERM : cont = 0 ; break ; + case SIGHUP : reload_cdbs() ; break ; + case SIGALRM : dump_cache() ; break ; + default : break ; + } +} + +int main (int argc, char const *const *argv) +{ + unsigned int notif = 0 ; + unsigned int n4 = 0, n6 = 0 ; + uid_t uid = 0 ; + gid_t gid = 0 ; + PROG = "shibari-cache" ; + { + int flagdrop = 0 ; + subgetopt l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "Uv:d:D:w:i:x:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'U' : flagdrop = 1 ; break ; + case 'v' : if (!uint320_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'd' : if (!uint0_scan(l.arg, ¬if)) dieusage() ; break ; + case 'D' : dumpfile = l.arg ; break ; + case 'w' : if (!uint0_scan(l.arg, &wtimeout)) dieusage() ; break ; + case 'i' : rulesfile = l.arg ; rulestype = 1 ; break ; + case 'x' : rulesfile = l.arg ; rulestype = 2 ; break ; + default : strerr_dieusage(10, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + if (!argc) default_iplist ; + + if (!ip46_scan(argv[0], &localip)) dieusage() ; + if (flagdrop) + { + char const *x = getenv("UID") ; + if (!x) strerr_dienotset(100, "UID") ; + if (!uid0_scan(x, &uid)) strerr_dieinvalid(100, "UID") ; + x = getenv("GID") ; + if (!x) strerr_dienotset(100, "GID") ; + if (!uid0_scan(x, &gid)) strerr_dieinvalid(100, "GID") ; + } + if (wtimeout) tain_from_millisecs(&wtto, wtimeout) ; + } + + 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) ; + x[0].fd = selfpipe_init() ; + if (x[0].fd == -1) strerr_diefu1sys(111, "create selfpipe") ; + if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGHUP) ; + sigaddset(&set, SIGTERM) ; + if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ; + } + + 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) ; + + x[1].fd = socket_udp46_nb(ip46_is6(&localip)) ; + if (x[1].fd == -1) strerr_diefu1sys(111, "create socket") ; + if (socket_bind46_reuse(x[1].fd, &localip, localport) == -1) strerr_diefu1sys(111, "bind socket") ; + + if (gid && setgid(gid) == -1) strerr_diefu1sys(111, "setgid") ; + if (uid && setuid(uid) == -1) strerr_diefu1sys(111, "setuid") ; + if (!tain_now_set_stopwatch_g()) strerr_diefu1sys(111, "initialize clock") ; + + shibari_log_start(verbosity, &localip, localport) ; + if (notif) + { + write(notif, "\n", 1) ; + close(notif) ; + } + + while (cont) + { + tain wstamp = TAIN_INFINITE ; + 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 ; + + if (iopause_g(x, 2, &wstamp) == -1) strerr_diefu1sys(111, "iopause") ; + if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with selfpipe") ; + if (x[0].revents & IOPAUSE_READ) { handle_signals() ; continue ; } + + r = sanitize_read(socket_recv46(x[1].fd, buf, 512, &remoteip, &remoteport, ip46_is6(&localip))) ; + if (!r) continue ; + if (r == -1) strerr_diefu1sys(111, "recv from socket") ; + 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) ; + tain_add_g(&wstamp, &wtto) ; + if (socket_sendnb46_g(x[1].fd, buf, pkt.pos, &remoteip, remoteport, &wstamp) < pkt.pos && verbosity) + strerr_warnwu1sys("send answer") ; + } + + shibari_log_exit(verbosity, 0) ; + return 0 ; +} diff --git a/src/config/PARSING-config.txt b/src/config/PARSING-config.txt new file mode 100644 index 0000000..072a1fa --- /dev/null +++ b/src/config/PARSING-config.txt @@ -0,0 +1,26 @@ + +class | 0 1 2 3 4 +st\ev | \0 space # \n other + +START | P np +00 | END SPACE COMMENT START WORD + +COMMENT | P +01 | END COMMENT COMMENT START COMMENT + +SPACE | P P np +02 | END SPACE COMMENT START WORD + +WORD | 0P 0 p 0P p +03 | END SPACE WORD START WORD + +END: 04 +X: 05 + +states: 3 bits +actions: 4 bits + +0x10 n new word +0x20 p push cur +0x40 0 push \0 +0x80 P process line diff --git a/src/config/conftree.c b/src/config/conftree.c new file mode 100644 index 0000000..573646a --- /dev/null +++ b/src/config/conftree.c @@ -0,0 +1,51 @@ +/* ISC license. */ + +#include <skalibs/genalloc.h> +#include <skalibs/avltree.h> +#include <skalibs/cdbmake.h> + +#include "shibari-cache-config-internal.h" + +static repo conftree = \ +{ \ + .ga = GENALLOC_ZERO, \ + .tree = AVLTREE_INIT(8, 3, 8, &node_dtok, &node_cmp, &conftree.ga), \ + .storage = &g.storage \ +} ; + +void confnode_start (node *node, char const *key, size_t filepos, uint32_t line) +{ + return node_start(&g.storage, node, key, filepos, line) ; +} + +void confnode_add (node *node, char const *s, size_t len) +{ + return node_add(&g.storage, node, s, len) ; +} + +node const *conftree_search (char const *key) +{ + return repo_search(&conftree, key) ; +} + +void conftree_add (node const *node) +{ + return repo_add(&conftree, node) ; +} + +void conftree_update (node const *node) +{ + return repo_update(&conftree, node) ; +} + +static int confnode_write (uint32_t d, unsigned int h, void *data) +{ + node *nod = genalloc_s(node, &conftree.ga) + d ; + (void)h ; + return cdbmake_add((cdbmaker *)data, conftree.storage->s + nod->key, nod->keylen, conftree.storage->s + nod->data, nod->datalen) ; +} + +int conftree_write (cdbmaker *cm) +{ + return avltree_iter(&conftree.tree, &confnode_write, cm) ; +} diff --git a/src/config/defaults.c b/src/config/defaults.c new file mode 100644 index 0000000..14fce76 --- /dev/null +++ b/src/config/defaults.c @@ -0,0 +1,70 @@ +/* ISC license. */ + +#include <stddef.h> + +#include "shibari-cache-config-internal.h" + +struct defaults_s +{ + char const *key ; + char const *value ; + size_t vlen ; +} ; + +#define REC(k, v, n) { .key = (k), .value = (v), .vlen = (n) } +#define RECS(k, v) REC(k, v, sizeof(v)) +#define RECU32(k, u) { .key = (k), .value = (char const [4]){ (u) >> 24 & 0xffu, (u) >> 16 & 0xffu, (u) >> 8 & 0xffu, (u) & 0xffu }, .vlen = 4 } + +static struct defaults_s const defaults[] = +{ + RECU32("G:logv", 1), + REC("G:listen4", "\0\0\0\0\0\35", 6), + REC("G:listen6", "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\35", 18), + + REC("R4:", + "\0\306\51\0\4" + "\0\252\367\252\2" + "\0\300\41\4\14" + "\0\307\7\133\15" + "\0\300\313\346\12" + "\0\300\5\5\361" + "\0\300\160\44\4" + "\0\306\141\276\65" + "\0\300\44\224\21" + "\0\300\72\200\36" + "\0\301\0\16\201" + "\0\307\7\123\52" + "\0\312\14\33\41" + , 65), + + REC("R6:", + "\0\40\1\5\3\272\76\0\0\0\0\0\0\0\2\0\60" + "\0\50\1\1\270\0\20\0\0\0\0\0\0\0\0\0\13" + "\0\40\1\5\0\0\2\0\0\0\0\0\0\0\0\0\14" + "\0\40\1\5\0\0\55\0\0\0\0\0\0\0\0\0\15" + "\0\40\1\5\0\0\250\0\0\0\0\0\0\0\0\0\16" + "\0\40\1\5\0\0\57\0\0\0\0\0\0\0\0\0\17" + "\0\40\1\5\0\0\22\0\0\0\0\0\0\0\0\15\15" + "\0\40\1\5\0\0\1\0\0\0\0\0\0\0\0\0\123" + "\0\40\1\7\376\0\0\0\0\0\0\0\0\0\0\0\123" + "\0\40\1\5\3\14\47\0\0\0\0\0\0\0\2\0\60" + "\0\40\1\7\375\0\0\0\0\0\0\0\0\0\0\0\1" + "\0\40\1\5\0\0\237\0\0\0\0\0\0\0\0\0\102" + "\0\40\1\15\303\0\0\0\0\0\0\0\0\0\0\0\65" + , 221), + REC(0, 0, 0) +} ; + +void conf_defaults (void) +{ + for (struct defaults_s const *p = defaults ; p->key ; p++) + { + if (!conftree_search(p->key)) + { + node node ; + confnode_start(&node, p->key, 0, 0) ; + confnode_add(&node, p->value, p->vlen) ; + conftree_add(&node) ; + } + } +} diff --git a/src/config/deps-exe/shibari-cache-config b/src/config/deps-exe/shibari-cache-config new file mode 100644 index 0000000..1685f25 --- /dev/null +++ b/src/config/deps-exe/shibari-cache-config @@ -0,0 +1,8 @@ +util.o +node.o +repo.o +conftree.o +defaults.o +lexparse.o +-ls6dns +-lskarnet diff --git a/src/config/lexparse.c b/src/config/lexparse.c new file mode 100644 index 0000000..9987fbd --- /dev/null +++ b/src/config/lexparse.c @@ -0,0 +1,230 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <skalibs/uint32.h> +#include <skalibs/bitarray.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/skamisc.h> + +#include <s6-dns/s6dns-domain.h> + +#include <shibari/config.h> +#include "shibari-cache-config-internal.h" + +#define dietoobig() strerr_diefu1sys(100, "read configuration") + +typedef struct mdt_s mdt, *mdt_ref ; +struct mdt_s +{ + size_t filepos ; + uint32_t line ; + char linefmt[UINT32_FMT] ; +} ; +#define MDT_ZERO { .filepos = 0, .line = 0, .linefmt = "0" } + +struct namevalue_s +{ + char const *name ; + uint32_t value ; +} ; + +enum directivevalue_e +{ + T_VERBOSITY, + T_LISTEN, + T_SERVER, + T_FORWARD, +} ; + +static void conftree_checkunique (char const *key, mdt const *md) +{ + node const *node = conftree_search(key) ; + if (node) + { + char fmt[UINT32_FMT] ; + fmt[uint32_fmt(fmt, node->line)] = 0 ; + strerr_diefn(1, 12, "duplicate ", "key ", key, " in file ", g.storage.s + md->filepos, " line ", md->linefmt, ", previously defined", " in file ", g.storage.s + node->filepos, " line ", fmt) ; + } +} + +static void add_unique (char const *key, char const *value, size_t valuelen, mdt const *md) +{ + node node ; + conftree_checkunique(key, md) ; + confnode_start(&node, key, md->filepos, md->line) ; + confnode_add(&node, value, valuelen) ; + conftree_add(&node) ; +} + +static int ip40_scan (char const *s, char *ip) +{ + size_t len = ip4_scan(s, ip) ; + return len ? !s[len] : 0 ; +} + +static int ip60_scan (char const *s, char *ip) +{ + size_t len = ip6_scan(s, ip) ; + return len ? !s[len] : 0 ; +} + +static inline void parse_verbosity (char const *s, size_t const *word, size_t n, mdt const *md) +{ + uint32_t v ; + char pack[4] ; + if (n != 1) + strerr_dief8x(1, "too ", n ? "many" : "few", " arguments to directive ", "verbosity", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + if (!uint320_scan(s + word[0], &v)) + strerr_dief7x(1, " argument to directive ", "verbosity", " must be an integer ", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + uint32_pack_big(pack, v) ; + add_unique("G:logv", pack, 4, md) ; +} + +static inline void parse_listen (char const *s, size_t const *word, size_t n, mdt const *md) +{ + if (!n) + strerr_dief6x(1, "too few arguments to directive ", "listen", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + { + size_t n4 = 0, n6 = 0 ; + char ip6[n << 4] ; + char ip4[n << 2] ; + for (size_t i = 0 ; i < n ; i++) + { + if (ip60_scan(s + word[i], ip6 + (n6 << 4))) n6++ ; + else if (ip40_scan(s + word[i], ip4 + (n4 << 2))) n4++ ; + else strerr_dief6x(1, "arguments to directive ", "listen", " must be IPs in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + } + add_unique("G:listen4", ip4, n4 << 2, md) ; + add_unique("G:listen6", ip6, n6 << 4, md) ; + } +} + +static inline void parse_server (char const *s, size_t const *word, size_t n, mdt const *md, int forward) +{ + char const *x = forward ? "forward" : "server" ; + s6dns_domain_t domain ; + if (n-- < 2) + strerr_dief8x(1, "too ", "few", " arguments to directive ", x, " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + if (!s6dns_domain_fromstring(&domain, s + word[0], strlen(s + word[0])) + || !s6dns_domain_noqualify(&domain)) + strerr_dief7x(1, "first argument to directive ", x, " must be a zone ", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + word++ ; + { + size_t n4 = 0, n6 = 0 ; + char ip6[n * 17] ; + char ip4[n * 5] ; + char key[3 + domain.len] ; + for (size_t i = 0 ; i < n ; i++) + { + if (ip60_scan(s + word[i], ip6 + (n6 * 17) + 1)) ip6[n6++ * 17] = !!forward ; + else if (ip40_scan(s + word[i], ip4 + (n4 * 5) + 1)) ip4[n4++ * 5] = !!forward ; + else strerr_dief6x(1, "subsequent arguments to directive ", x, " must be IPs in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + } + memcpy(key, "R4:", 3) ; + memcpy(key + 3, domain.s + 1, domain.len - 1) ; + key[2 + domain.len] = 0 ; + add_unique(key, ip4, n4 * 5, md) ; + key[1] = '6' ; + add_unique(key, ip6, n6 * 17, md) ; + } +} + +static inline void process_line (char const *s, size_t const *word, size_t n, mdt *md) +{ + static struct namevalue_s const directives[] = + { + { .name = "forward", .value = T_FORWARD }, + { .name = "listen", .value = T_LISTEN }, + { .name = "server", .value = T_SERVER }, + { .name = "verbosity", .value = T_VERBOSITY }, + } ; + struct namevalue_s const *directive ; + char const *word0 ; + if (!n--) return ; + word0 = s + *word++ ; + directive = BSEARCH(struct namevalue_s, word0, directives) ; + if (!directive) + strerr_dief6x(1, "unrecognized word ", word0, " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + switch (directive->value) + { + case T_VERBOSITY : + parse_verbosity(s, word, n, md) ; + break ; + case T_LISTEN : + parse_listen(s, word, n, md) ; + break ; + case T_SERVER : + parse_server(s, word, n, md, 0) ; + break ; + case T_FORWARD : + parse_server(s, word, n, md, 1) ; + break ; + } +} + +static inline uint8_t cclass (char c) +{ + switch (c) + { + case 0 : return 0 ; + case ' ' : + case '\t' : + case '\f' : + case '\r' : return 1 ; + case '#' : return 2 ; + case '\n' : return 3 ; + default : return 4 ; + } +} + +static inline char next (buffer *b, mdt const *md) +{ + char c ; + ssize_t r = buffer_get(b, &c, 1) ; + if (r == -1) strerr_diefu1sys(111, "read from preprocessor") ; + if (!r) return 0 ; + if (!c) strerr_dief5x(1, "null character", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + return c ; +} + +void conf_lexparse (buffer *b, char const *ifile) +{ + static uint8_t const table[4][5] = /* see PARSING-config.txt */ + { + { 0x04, 0x02, 0x01, 0x80, 0x33 }, + { 0x04, 0x01, 0x01, 0x80, 0x01 }, + { 0x84, 0x02, 0x01, 0x80, 0x33 }, + { 0xc4, 0x42, 0x23, 0xc0, 0x23 } + } ; + stralloc sa = STRALLOC_ZERO ; + genalloc words = GENALLOC_ZERO ; /* size_t */ + mdt md = MDT_ZERO ; + uint8_t state = 0 ; + if (!stralloc_catb(&g.storage, ifile, strlen(ifile) + 1)) dienomem() ; + while (state < 0x04) + { + char c = next(b, &md) ; + uint8_t what = table[state][cclass(c)] ; + state = what & 0x07 ; + if (what & 0x10) if (!genalloc_catb(size_t, &words, &sa.len, 1)) dienomem() ; + if (what & 0x20) if (!stralloc_catb(&sa, &c, 1)) dienomem() ; + if (what & 0x40) if (!stralloc_0(&sa)) dienomem() ; + if (what & 0x80) + { + process_line(sa.s, genalloc_s(size_t, &words), genalloc_len(size_t, &words), &md) ; + genalloc_setlen(size_t, &words, 0) ; + sa.len = 0 ; + md.line++ ; + md.linefmt[uint32_fmt(md.linefmt, md.line)] = 0 ; + } + } + genalloc_free(size_t, &words) ; + stralloc_free(&sa) ; +} diff --git a/src/config/node.c b/src/config/node.c new file mode 100644 index 0000000..7e6cd4b --- /dev/null +++ b/src/config/node.c @@ -0,0 +1,34 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> + +#include <skalibs/stralloc.h> +#include <skalibs/strerr.h> + +#include "shibari-cache-config-internal.h" + +#define diestorage() strerr_diefu2x(100, "add node to configuration tree", ": too much data") +#define diefilepos() strerr_diefu2x(100, "add node to configuration tree", ": file too large") + +void node_start (stralloc *storage, node *node, char const *key, size_t filepos, uint32_t line) +{ + size_t l = strlen(key) ; + size_t k = storage->len ; + if (!stralloc_catb(storage, key, l + 1)) dienomem() ; + if (storage->len >= UINT32_MAX) diestorage() ; + if (filepos > UINT32_MAX) diefilepos() ; + node->key = k ; + node->keylen = l ; + node->data = storage->len ; + node->datalen = 0 ; + node->filepos = filepos ; + node->line = line ; +} + +void node_add (stralloc *storage, node *node, char const *s, size_t len) +{ + if (!stralloc_catb(storage, s, len)) dienomem() ; + if (storage->len >= UINT32_MAX) diestorage() ; + node->datalen += len ; +} diff --git a/src/config/repo.c b/src/config/repo.c new file mode 100644 index 0000000..8ed8c51 --- /dev/null +++ b/src/config/repo.c @@ -0,0 +1,46 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> + +#include <skalibs/genalloc.h> +#include <skalibs/avltree.h> + +#include "shibari-cache-config-internal.h" + +void *node_dtok (uint32_t d, void *data) +{ + repo *r = data ; + return r->storage->s + genalloc_s(node, &r->ga)[d].key ; +} + +int node_cmp (void const *a, void const *b, void *data) +{ + (void)data ; + return strcmp((char const *)a, (char const *)b) ; +} + +node const *repo_search (repo const *r, char const *key) +{ + uint32_t i ; + return avltree_search(&r->tree, key, &i) ? genalloc_s(node const, &r->ga) + i : 0 ; +} + +void repo_add (repo *r, node const *nod) +{ + uint32_t i = genalloc_len(node, &r->ga) ; + if (!genalloc_append(node, &r->ga, nod)) dienomem() ; + if (!avltree_insert(&r->tree, i)) dienomem() ; +} + +void repo_update (repo *r, node const *nod) +{ + uint32_t i ; + if (avltree_search(&r->tree, r->storage->s + nod->key, &i)) + { + if (!avltree_delete(&r->tree, r->storage->s + nod->key)) dienomem() ; + genalloc_s(node, &r->ga)[i] = *nod ; + if (!avltree_insert(&r->tree, i)) dienomem() ; + } + else repo_add(r, nod) ; +} diff --git a/src/config/shibari-cache-config-internal.h b/src/config/shibari-cache-config-internal.h new file mode 100644 index 0000000..c7b0197 --- /dev/null +++ b/src/config/shibari-cache-config-internal.h @@ -0,0 +1,91 @@ +/* ISC license. */ + +#ifndef SHIBARI_CACHE_CONFIG_INTERNAL_H +#define SHIBARI_CACHE_CONFIG_INTERNAL_H + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> + +#include <skalibs/buffer.h> +#include <skalibs/strerr.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/cdbmake.h> +#include <skalibs/avltree.h> + +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") + +typedef struct node_s node, *node_ref ; +struct node_s +{ + uint32_t key ; + uint32_t keylen ; + uint32_t data ; + uint32_t datalen ; + uint32_t filepos ; + uint32_t line ; +} ; +#define NODE_ZERO { .key = 0, .keylen = 0, .data = 0, .datalen = 0 } + +typedef struct repo_s repo, *repo_ref ; +struct repo_s +{ + genalloc ga ; + avltree tree ; + stralloc *storage ; +} ; +#define REPO_ZERO { .ga = GENALLOC_ZERO, .tree = AVLTREE_ZERO, .storage = 0 } + +struct global_s +{ + stralloc storage ; +} ; +#define GLOBAL_ZERO { .storage = STRALLOC_ZERO } + +extern struct global_s g ; + + + /* util */ + +extern int keycmp (void const *, void const *) ; /* for any struct starting with a string key */ +#define BSEARCH(type, key, array) bsearch(key, (array), sizeof(array)/sizeof(type), sizeof(type), &keycmp) + + + /* node */ + +extern void node_start (stralloc *, node *, char const *, size_t, uint32_t) ; +extern void node_add (stralloc *, node *, char const *, size_t) ; + + + /* repo */ + +extern void *node_dtok (uint32_t, void *) ; +extern int node_cmp (void const *, void const *, void *) ; +extern node const *repo_search (repo const *, char const *) ; +extern void repo_add (repo *, node const *) ; +extern void repo_update (repo *, node const *) ; + + + /* conftree */ + +extern void confnode_start (node *, char const *, size_t, uint32_t) ; +extern void confnode_add (node *, char const *, size_t) ; + +extern node const *conftree_search (char const *) ; +extern void conftree_add (node const *) ; +extern void conftree_update (node const *) ; + +extern int conftree_write (cdbmaker *) ; + + + /* lexparse */ + +extern void conf_lexparse (buffer *, char const *) ; + + + /* defaults */ + +extern void conf_defaults (void) ; + +#endif diff --git a/src/config/shibari-cache-config.c b/src/config/shibari-cache-config.c new file mode 100644 index 0000000..0aef2ce --- /dev/null +++ b/src/config/shibari-cache-config.c @@ -0,0 +1,101 @@ +/* ISC license. */ + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> /* rename() */ +#include <errno.h> +#include <signal.h> + +#include <skalibs/posixplz.h> +#include <skalibs/types.h> +#include <skalibs/sgetopt.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr.h> +#include <skalibs/djbunix.h> + +#include <shibari/config.h> +#include "shibari-cache-config-internal.h" + +#define USAGE "shibari-cache-config [ -i textfile ] [ -o cdbfile ] [ -m mode ]" +#define dieusage() strerr_dieusage(100, USAGE) + +struct global_s g = GLOBAL_ZERO ; + +static inline void conf_output (char const *ofile, unsigned int omode) +{ + int fdw ; + cdbmaker cm = CDBMAKER_ZERO ; + size_t olen = strlen(ofile) ; + char otmp[olen + 8] ; + memcpy(otmp, ofile, olen) ; + memcpy(otmp + olen, ":XXXXXX", 8) ; + fdw = mkstemp(otmp) ; + if (fdw == -1) strerr_diefu3sys(111, "open ", otmp, " for writing") ; + if (!cdbmake_start(&cm, fdw)) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "cdmake_start ", otmp) ; + } + if (!conftree_write(&cm)) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "write config tree into ", otmp) ; + } + if (!cdbmake_finish(&cm)) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "cdbmake_finish ", otmp) ; + } + if (fsync(fdw) == -1) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "fsync ", otmp) ; + } + if (fchmod(fdw, omode & 0777) == -1) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "fchmod ", otmp) ; + } + if (rename(otmp, ofile) == -1) + { + unlink_void(otmp) ; + strerr_diefu4sys(111, "rename ", otmp, " to ", ofile) ; + } +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char const *ifile = SHIBARI_SYSCONFPREFIX "shibari-cache.conf" ; + char const *ofile = SHIBARI_SYSCONFPREFIX "shibari-cache.conf.cdb" ; + unsigned int omode = 0644 ; + + PROG = "shibari-cache-config" ; + { + subgetopt l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "i:o:m:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'i' : ifile = l.arg ; break ; + case 'o' : ofile = l.arg ; break ; + case 'm' : if (!uint0_oscan(l.arg, &omode)) dieusage() ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + { + int fdr = openc_readb(ifile) ; + char buf[4096] ; + buffer b = BUFFER_INIT(&buffer_read, fdr, buf, 4096) ; + if (fdr == -1) strerr_diefu2sys(111, "open ", ifile) ; + conf_lexparse(&b, ifile) ; + } + conf_defaults() ; + conf_output(ofile, omode) ; + return 0 ; +} diff --git a/src/config/util.c b/src/config/util.c new file mode 100644 index 0000000..f3da287 --- /dev/null +++ b/src/config/util.c @@ -0,0 +1,15 @@ +/* ISC license. */ + +#include <string.h> + +#include "shibari-cache-config-internal.h" + +struct starts_with_a_string_key_s +{ + char const *s ; +} ; + +int keycmp (void const *a, void const *b) +{ + return strcmp((char const *)a, ((struct starts_with_a_string_key_s const *)b)->s) ; +} |