diff options
Diffstat (limited to 'src/config/lexparse.c')
-rw-r--r-- | src/config/lexparse.c | 230 |
1 files changed, 230 insertions, 0 deletions
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) ; +} |