diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2023-08-05 11:51:25 +0000 |
---|---|---|
committer | Laurent Bercot <ska@appnovation.com> | 2023-08-05 11:51:25 +0000 |
commit | 17c382d1c9d7236c101418060758d2296cc5e17e (patch) | |
tree | fd00e58df0d9d3c70ddd1accfec9e819249c672a /src/libtipidee | |
download | tipidee-17c382d1c9d7236c101418060758d2296cc5e17e.tar.xz |
Initial commit
Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat (limited to 'src/libtipidee')
24 files changed, 964 insertions, 0 deletions
diff --git a/src/libtipidee/deps-lib/tipidee b/src/libtipidee/deps-lib/tipidee new file mode 100644 index 0000000..d218af1 --- /dev/null +++ b/src/libtipidee/deps-lib/tipidee @@ -0,0 +1,24 @@ +tipidee_chunked_read.o +tipidee_conf_free.o +tipidee_conf_get.o +tipidee_conf_get_argv.o +tipidee_conf_get_content_type.o +tipidee_conf_get_redirection.o +tipidee_conf_get_string.o +tipidee_conf_get_uint32.o +tipidee_conf_init.o +tipidee_headers_get_content_length.o +tipidee_headers_init.o +tipidee_headers_parse.o +tipidee_headers_search.o +tipidee_method_conv_table.o +tipidee_method_tonum.o +tipidee_method_tostr.o +tipidee_response_error.o +tipidee_response_header_builtin.o +tipidee_response_header_common_put.o +tipidee_response_header_date_fmt.o +tipidee_response_status.o +tipidee_rql_read.o +tipidee_uri_parse.o +-lskarnet diff --git a/src/libtipidee/tipidee_chunked_read.c b/src/libtipidee/tipidee_chunked_read.c new file mode 100644 index 0000000..66d5d80 --- /dev/null +++ b/src/libtipidee/tipidee_chunked_read.c @@ -0,0 +1,41 @@ +/* ISC license. */ + +#include <string.h> +#include <errno.h> + +#include <skalibs/types.h> +#include <skalibs/stralloc.h> +#include <skalibs/unix-timed.h> + +#include <tipidee/body.h> + +#include <skalibs/posixishard.h> + +int tipidee_chunked_read (buffer *b, stralloc *sa, size_t maxlen, tain const *deadline, tain *stamp) +{ + char line[512] ; + for (;;) + { + size_t chunklen, pos, w = 0 ; + ssize_t r = timed_getlnmax(b, line, 512, &w, '\n', deadline, stamp) ; + if (r < 0) return 0 ; + if (!r) return (errno = EPIPE, 0) ; + pos = size_scan(line, &chunklen) ; + if (!pos) return (errno = EPROTO, 0) ; + if (!memchr("\r\n; \t", line[pos], 5)) return (errno = EPROTO, 0) ; + if (!chunklen) break ; + if (sa->len + chunklen > maxlen) return (errno = EMSGSIZE, 0) ; + if (!stralloc_readyplus(sa, chunklen)) return 0 ; + if (buffer_timed_get(b, sa->s + sa->len, chunklen, deadline, stamp) < chunklen) return 0 ; + sa->len += chunklen ; + } + for (;;) + { + size_t w = 0 ; + ssize_t r = timed_getlnmax(b, line, 512, &w, '\n', deadline, stamp) ; + if (r < 0) return 0 ; + if (!r) return (errno = EPIPE, 0) ; + if (w == 1 || (w == 2 && line[0] == '\r')) break ; + } + return 1 ; +} diff --git a/src/libtipidee/tipidee_conf_free.c b/src/libtipidee/tipidee_conf_free.c new file mode 100644 index 0000000..e708d89 --- /dev/null +++ b/src/libtipidee/tipidee_conf_free.c @@ -0,0 +1,10 @@ +/* ISC license. */ + +#include <skalibs/cdb.h> + +#include <tipidee/conf.h> + +void tipidee_conf_free (tipidee_conf *conf) +{ + cdb_free(&conf->c) ; +} diff --git a/src/libtipidee/tipidee_conf_get.c b/src/libtipidee/tipidee_conf_get.c new file mode 100644 index 0000000..866ec99 --- /dev/null +++ b/src/libtipidee/tipidee_conf_get.c @@ -0,0 +1,22 @@ +/* ISC license. */ + +#include <errno.h> +#include <string.h> + +#include <skalibs/cdb.h> +#include <skalibs/lolstdio.h> + +#include <tipidee/conf.h> + +int tipidee_conf_get (tipidee_conf const *conf, char const *key, cdb_data *data) +{ + size_t keylen = strlen(key) ; + if (keylen > TIPIDEE_CONF_KEY_MAXLEN) return (errno = EINVAL, 0) ; + LOLDEBUG("tipidee_conf_get: looking up %s", key) ; + switch (cdb_find(&conf->c, data, key, keylen)) + { + case -1 : return (errno = EILSEQ, 0) ; + case 0 : return (errno = ENOENT, 0) ; + default : return 1 ; + } +} diff --git a/src/libtipidee/tipidee_conf_get_argv.c b/src/libtipidee/tipidee_conf_get_argv.c new file mode 100644 index 0000000..0dd016e --- /dev/null +++ b/src/libtipidee/tipidee_conf_get_argv.c @@ -0,0 +1,32 @@ +/* ISC license. */ + +#include <errno.h> +#include <string.h> + +#include <skalibs/cdb.h> + +#include <tipidee/conf.h> + +#include <skalibs/posixishard.h> + +unsigned int tipidee_conf_get_argv (tipidee_conf const *conf, char const *key, char const **argv, unsigned int max, size_t *maxlen) +{ + cdb_data data ; + size_t curlen = 0 ; + unsigned int n = 0, pos = 0 ; + if (!tipidee_conf_get(conf, key, &data)) return 0 ; + if (data.s[data.len-1]) return (errno = EPROTO, 0) ; + while (pos < data.len) + { + size_t len ; + if (n >= max) return (errno = E2BIG, 0) ; + argv[n++] = data.s + pos ; + len = strlen(data.s + pos) ; + if (len > curlen) curlen = len ; + pos += len + 1 ; + } + if (n >= max) return (errno = E2BIG, 0) ; + argv[n++] = 0 ; + if (maxlen) *maxlen = curlen ; + return n ; +} diff --git a/src/libtipidee/tipidee_conf_get_content_type.c b/src/libtipidee/tipidee_conf_get_content_type.c new file mode 100644 index 0000000..7ee8866 --- /dev/null +++ b/src/libtipidee/tipidee_conf_get_content_type.c @@ -0,0 +1,22 @@ +/* ISC license. */ + +#include <errno.h> +#include <string.h> + +#include <tipidee/conf.h> + +char const *tipidee_conf_get_content_type (tipidee_conf const *conf, char const *res) +{ + char const *ext = strrchr(res, '.') ; + if (ext && !strchr(ext, '/')) + { + char const *value = 0 ; + size_t extlen = strlen(ext+1) ; + char key[3 + extlen] ; + key[0] = 'T' ; key[1] = ':' ; + memcpy(key + 2, ext + 1, extlen + 1) ; + value = tipidee_conf_get_string(conf, key) ; + if (value || errno != ENOENT) return value ; + } + return "application/octet-stream" ; +} diff --git a/src/libtipidee/tipidee_conf_get_redirection.c b/src/libtipidee/tipidee_conf_get_redirection.c new file mode 100644 index 0000000..62ada34 --- /dev/null +++ b/src/libtipidee/tipidee_conf_get_redirection.c @@ -0,0 +1,35 @@ +/* ISC license. */ + +#include <errno.h> +#include <string.h> + +#include <tipidee/conf.h> + +#include <skalibs/posixishard.h> + +int tipidee_conf_get_redirection (tipidee_conf const *conf, char const *res, size_t docrootlen, tipidee_redirection *r) +{ + size_t reslen = strlen(res) ; + size_t l = 2 + reslen ; + char const *v = 0 ; + char key[3 + reslen] ; + key[0] = 'R' ; key[1] = ':' ; + memcpy(key + 2, res, reslen) ; + key[2 + reslen] = '/' ; + errno = ENOENT ; + while (!v) + { + if (errno != ENOENT) return -1 ; + while (l > 2 + docrootlen && key[l] != '/') l-- ; + if (l <= 2 + docrootlen) break ; + key[l--] = 0 ; + key[0] = 'r' ; + v = tipidee_conf_get_string(conf, key) ; + } + if (!v) return 0 ; + if (v[0] < '@' || v[0] > 'C') return (errno = EPROTO, -1) ; + r->type = v[0] & ~'@' ; + r->location = v+1 ; + r->sub = res + l - 2 ; + return 1 ; +} diff --git a/src/libtipidee/tipidee_conf_get_string.c b/src/libtipidee/tipidee_conf_get_string.c new file mode 100644 index 0000000..0ea93cd --- /dev/null +++ b/src/libtipidee/tipidee_conf_get_string.c @@ -0,0 +1,17 @@ +/* ISC license. */ + +#include <errno.h> + +#include <skalibs/cdb.h> + +#include <tipidee/conf.h> + +#include <skalibs/posixishard.h> + +char const *tipidee_conf_get_string (tipidee_conf const *conf, char const *key) +{ + cdb_data data ; + if (!tipidee_conf_get(conf, key, &data)) return 0 ; + if (data.s[data.len-1]) { errno = EPROTO ; return 0 ; } + return data.s ; +} diff --git a/src/libtipidee/tipidee_conf_get_uint32.c b/src/libtipidee/tipidee_conf_get_uint32.c new file mode 100644 index 0000000..ad8b21b --- /dev/null +++ b/src/libtipidee/tipidee_conf_get_uint32.c @@ -0,0 +1,19 @@ +/* ISC license. */ + +#include <errno.h> + +#include <skalibs/uint32.h> +#include <skalibs/cdb.h> + +#include <tipidee/conf.h> + +#include <skalibs/posixishard.h> + +int tipidee_conf_get_uint32 (tipidee_conf const *conf, char const *key, uint32_t *value) +{ + cdb_data data ; + if (!tipidee_conf_get(conf, key, &data)) return 0 ; + if (data.len != 4) return (errno = EPROTO, 0) ; + uint32_unpack_big(data.s, value) ; + return 1 ; +} diff --git a/src/libtipidee/tipidee_conf_init.c b/src/libtipidee/tipidee_conf_init.c new file mode 100644 index 0000000..3d2e119 --- /dev/null +++ b/src/libtipidee/tipidee_conf_init.c @@ -0,0 +1,10 @@ +/* ISC license. */ + +#include <skalibs/cdb.h> + +#include <tipidee/conf.h> + +int tipidee_conf_init (tipidee_conf *conf, char const *file) +{ + return cdb_init(&conf->c, file) ; +} diff --git a/src/libtipidee/tipidee_headers_get_content_length.c b/src/libtipidee/tipidee_headers_get_content_length.c new file mode 100644 index 0000000..0b29ece --- /dev/null +++ b/src/libtipidee/tipidee_headers_get_content_length.c @@ -0,0 +1,17 @@ +/* ISC license. */ + +#include <stddef.h> +#include <limits.h> + +#include <skalibs/types.h> + +#include <tipidee/headers.h> + +ssize_t tipidee_headers_get_content_length (tipidee_headers const *hdr) +{ + size_t n ; + char const *x = tipidee_headers_search(hdr, "Content-Length") ; + if (!x) return 0 ; + if (!size0_scan(x, &n) || n > SSIZE_MAX) return -1 ; + return n ; +} diff --git a/src/libtipidee/tipidee_headers_init.c b/src/libtipidee/tipidee_headers_init.c new file mode 100644 index 0000000..6c2d336 --- /dev/null +++ b/src/libtipidee/tipidee_headers_init.c @@ -0,0 +1,29 @@ +/* ISC license. */ + +#include <stdint.h> +#include <strings.h> + +#include <skalibs/avltreen.h> + +#include <tipidee/headers.h> + +static void *tipidee_headers_dtok (uint32_t d, void *data) +{ + tipidee_headers *hdr = data ; + return hdr->buf + hdr->list[d].left ; +} + +static int tipidee_headers_cmp (void const *a, void const *b, void *data) +{ + (void)data ; + return strcasecmp(a, b) ; +} + +void tipidee_headers_init (tipidee_headers *hdr, char *buf, size_t max) +{ + hdr->buf = buf ; + hdr->max = max ; + hdr->len = 0 ; + hdr->n = 0 ; + avltreen_init(&hdr->map, hdr->map_storage, hdr->map_freelist, TIPIDEE_HEADERS_MAX, &tipidee_headers_dtok, &tipidee_headers_cmp, hdr) ; +} diff --git a/src/libtipidee/tipidee_headers_parse.c b/src/libtipidee/tipidee_headers_parse.c new file mode 100644 index 0000000..57108bb --- /dev/null +++ b/src/libtipidee/tipidee_headers_parse.c @@ -0,0 +1,210 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> +#include <strings.h> +#include <errno.h> + +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/error.h> +#include <skalibs/avltreen.h> +#include <skalibs/unix-timed.h> +// #include <skalibs/lolstdio.h> + +#include <tipidee/headers.h> + +/* + +Reads header lines, separates into \0-terminated keys+values. +key is at hdr->buf + hdr->list[i].left +value is at hdr->buf + hdr->list[i].right +Compresses linear whitespace. +Does not unquote strings/comments in values. + + +st\ev 0 1 2 3 4 5 6 7 + CTL CR LF LWS : special normal 8bit + +00 kp +START X END? END X X X K X + +01 +END? X X END X X X X X + +02 zv p +K X X X X V1 X K X + +03 p p p p +V1 X V1?? V1? V1 V V V V + +04 +V1?? X X V1? X X X X X + +05 zn zn znkp +V1? X END? END V1 X X K X + +06 s s s p p p p +V X V2?? V2? V2 V V V V + +07 p p p p +V2 X V2?? V2? V2 V V V V + +08 +V2?? X X V2? X X X X X + +09 mzn mzn mznkp +V2? X END? END V2 X X K X + +END = 0a, X = 0b + +0x4000 s: write space +0x2000 m: go back one char +0x1000 z: write \0 +0x0800 n: cut key/value pair, prepare next +0x0400 k: start of key +0x0200 v: start of value +0x0100 p: write current char + +states: 4 bits, actions: 7 bits + +*/ + + +struct tainp_s +{ + tain const *deadline ; + tain *stamp ; +} ; + +typedef int get1_func (buffer *, char *, struct tainp_s *) ; +typedef get1_func *get1_func_ref ; + +static int get1_timed (buffer *b, char *c, struct tainp_s *d) +{ + return buffer_timed_get(b, c, 1, d->deadline, d->stamp) ; +} + +static int get1_notimed (buffer *b, char *c, struct tainp_s *data) +{ + (void)data ; + return buffer_get(b, c, 1) == 1 ; +} + +static uint8_t cclass (char c) +{ + static uint8_t const ctable[128] = "00000000032001000000000000000000365666665566566566666666664565655666666666666666666666666665556666666666666666666666666666656560" ; + return c & 0x80 ? 7 : ctable[(uint8_t)c] - '0' ; +} + +static int needs_processing (char const *s) +{ + if (!strcasecmp(s, "Set-Cookie")) return 0 ; + if (str_start(s, "X-")) return 0 ; + return 1 ; +} + +static int tipidee_headers_parse_with (buffer *b, tipidee_headers *hdr, get1_func_ref next, struct tainp_s *data, disize *header, uint32_t *state) +{ + static uint16_t const table[10][8] = + { + { 0x000b, 0x0001, 0x000a, 0x000b, 0x000b, 0x000b, 0x0502, 0x000b }, + { 0x000b, 0x000b, 0x000a, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b }, + { 0x000b, 0x000b, 0x000b, 0x000b, 0x1203, 0x000b, 0x0102, 0x000b }, + { 0x000b, 0x0004, 0x0005, 0x0003, 0x0106, 0x0106, 0x0106, 0x0106 }, + { 0x000b, 0x000b, 0x0005, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b }, + { 0x000b, 0x1801, 0x180a, 0x0003, 0x000b, 0x000b, 0x1d02, 0x000b }, + { 0x000b, 0x4008, 0x4009, 0x4007, 0x0106, 0x0106, 0x0106, 0x0106 }, + { 0x000b, 0x0008, 0x0009, 0x0007, 0x0106, 0x0106, 0x0106, 0x0106 }, + { 0x000b, 0x000b, 0x0009, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b }, + { 0x000b, 0x3801, 0x380a, 0x0007, 0x000b, 0x000b, 0x3d02, 0x000b }, + } ; + while (*state < 0x0a) + { + uint16_t c ; + char cur ; + if (!(*next)(b, &cur, data)) + return errno == ETIMEDOUT ? 408 : error_isagain(errno) ? -2 : -1 ; + c = table[*state][cclass(cur)] ; +/* + { + + char s[2] = { cur, 0 } ; + LOLDEBUG("tipidee_headers_parse_with: state %hhu, event %s, newstate %hhu, actions %s%s%s%s%s%s%s", + *state, + cur == '\r' ? "\\r" : cur == '\n' ? "\\n" : s, + c & 0x0f, + c & 0x4000 ? "s" : "", + c & 0x2000 ? "m" : "", + c & 0x1000 ? "z" : "", + c & 0x0800 ? "n" : "", + c & 0x0400 ? "k" : "", + c & 0x0200 ? "v" : "", + c & 0x0100 ? "p" : "" + ) ; + } +*/ + *state = c & 0x0f ; + if (c & 0x4000) { if (hdr->len >= hdr->max) return 413 ; hdr->buf[hdr->len++] = ' ' ; } + if (c & 0x2000) hdr->len-- ; + if (c & 0x1000) { if (hdr->len >= hdr->max) return 413 ; hdr->buf[hdr->len++] = 0 ; } + if (c & 0x0800) + { + uint32_t prev ; + if (hdr->n >= TIPIDEE_HEADERS_MAX) return 413 ; + hdr->list[hdr->n] = *header ; + if (needs_processing(hdr->buf + header->left)) + { +// LOLDEBUG("tipidee_headers_parse_with: n: adding header %u - key %zu (%s), value %zu (%s)", hdr->n, header->left, hdr->buf + header->left, header->right, hdr->buf + header->right) ; + if (avltreen_search(&hdr->map, hdr->buf + header->left, &prev)) + { + size_t start = hdr->list[prev+1].left ; + if (prev+1 == hdr->n) + { + hdr->buf[start - 1] = ',' ; + hdr->buf[start] = ' ' ; + memcpy(hdr->buf + start + 1, hdr->buf + header->right, hdr->len - header->right) ; + } + else + { + size_t len = header->left - start ; + size_t offset = hdr->len - header->right + 1 ; + char tmp[len] ; + memcpy(tmp, hdr->buf + start, len) ; + hdr->buf[start - 1] = ',' ; + hdr->buf[start] = ' ' ; + memcpy(hdr->buf + start + 1, hdr->buf + header->right, hdr->len - header->right) ; + memcpy(hdr->buf + start + offset, tmp, len) ; + for (uint32_t i = prev + 1 ; i < hdr->n ; i++) + { + hdr->list[i].left += offset ; + hdr->list[i].right += offset ; + } + } + hdr->len -= header->right - header->left - 1 ; + hdr->n-- ; + } + else if (!avltreen_insert(&hdr->map, hdr->n)) return 500 ; + } + hdr->n++ ; + } + if (c & 0x0400) header->left = hdr->len ; + if (c & 0x0200) header->right = hdr->len ; + if (c & 0x0100) { if (hdr->len >= hdr->max) return 413 ; hdr->buf[hdr->len++] = cur ; } + } + if (*state > 0x0a) return 400 ; + return 0 ; +} + +int tipidee_headers_timed_parse (buffer *b, tipidee_headers *hdr, tain const *deadline, tain *stamp) +{ + struct tainp_s d = { .deadline = deadline, .stamp = stamp } ; + disize header = DISIZE_ZERO ; + uint32_t state = 0 ; + return tipidee_headers_parse_with(b, hdr, &get1_timed, &d, &header, &state) ; +} + +int tipidee_headers_parse_nb (buffer *b, tipidee_headers *hdr, disize *header, uint32_t *state) +{ + return tipidee_headers_parse_with(b, hdr, &get1_notimed, 0, header, state) ; +} diff --git a/src/libtipidee/tipidee_headers_search.c b/src/libtipidee/tipidee_headers_search.c new file mode 100644 index 0000000..553ef65 --- /dev/null +++ b/src/libtipidee/tipidee_headers_search.c @@ -0,0 +1,13 @@ +/* ISC license. */ + +#include <stdint.h> + +#include <skalibs/avltreen.h> + +#include <tipidee/headers.h> + +char const *tipidee_headers_search (tipidee_headers const *hdr, char const *key) +{ + uint32_t i ; + return avltreen_search(&hdr->map, key, &i) ? hdr->buf + hdr->list[i].right : 0 ; +} diff --git a/src/libtipidee/tipidee_method_conv_table.c b/src/libtipidee/tipidee_method_conv_table.c new file mode 100644 index 0000000..892e5b5 --- /dev/null +++ b/src/libtipidee/tipidee_method_conv_table.c @@ -0,0 +1,19 @@ +/* ISC license. */ + +#include <tipidee/method.h> + +static tipidee_method_conv const table[] = +{ + { .num = TIPIDEE_METHOD_GET, .str = "GET" }, + { .num = TIPIDEE_METHOD_HEAD, .str = "HEAD" }, + { .num = TIPIDEE_METHOD_OPTIONS, .str = "OPTIONS" }, + { .num = TIPIDEE_METHOD_POST, .str = "POST" }, + { .num = TIPIDEE_METHOD_PUT, .str = "PUT" }, + { .num = TIPIDEE_METHOD_DELETE, .str = "DELETE" }, + { .num = TIPIDEE_METHOD_TRACE, .str = "TRACE" }, + { .num = TIPIDEE_METHOD_CONNECT, .str = "CONNECT" }, + { .num = TIPIDEE_METHOD_PRI, .str = "PRI" }, + { .num = TIPIDEE_METHOD_UNKNOWN, .str = 0 } +} ; + +tipidee_method_conv const *tipidee_method_conv_table = table ; diff --git a/src/libtipidee/tipidee_method_tonum.c b/src/libtipidee/tipidee_method_tonum.c new file mode 100644 index 0000000..7c88e45 --- /dev/null +++ b/src/libtipidee/tipidee_method_tonum.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <string.h> + +#include <tipidee/method.h> + +tipidee_method tipidee_method_tonum (char const *s) +{ + tipidee_method_conv const *p = tipidee_method_conv_table ; + for (; p->str ; p++) if (!strcmp(s, p->str)) break ; + return p->num ; +} diff --git a/src/libtipidee/tipidee_method_tostr.c b/src/libtipidee/tipidee_method_tostr.c new file mode 100644 index 0000000..8a3831d --- /dev/null +++ b/src/libtipidee/tipidee_method_tostr.c @@ -0,0 +1,8 @@ +/* ISC license. */ + +#include <tipidee/method.h> + +char const *tipidee_method_tostr (tipidee_method m) +{ + return m < TIPIDEE_METHOD_UNKNOWN ? tipidee_method_conv_table[m].str : 0 ; +} diff --git a/src/libtipidee/tipidee_response_error.c b/src/libtipidee/tipidee_response_error.c new file mode 100644 index 0000000..e2687a4 --- /dev/null +++ b/src/libtipidee/tipidee_response_error.c @@ -0,0 +1,41 @@ +/* ISC license. */ + +#include <stddef.h> + +#include <skalibs/types.h> +#include <skalibs/buffer.h> + +#include <tipidee/method.h> +#include <tipidee/rql.h> +#include <tipidee/response.h> + +size_t tipidee_response_error (buffer *b, tipidee_rql const *rql, char const *rsl, char const *text, uint32_t options) +{ + size_t n = 0 ; + static char const txt1[] = "<html>\n<head><title>" ; + static char const txt2[] = "</title></head>\n<body>\n<h1> " ; + static char const txt3[] = " </h1>\n<p>\n" ; + static char const txt4[] = "\n</p>\n</body>\n</html>\n" ; + n += tipidee_response_status_line(b, rql, rsl) ; + n += tipidee_response_header_common_put_g(buffer_1, options) ; + if (!(options & 2)) + { + char fmt[SIZE_FMT] ; + n += buffer_putsnoflush(buffer_1, "Content-Type: text/html; charset=UTF-8\r\n") ; + n += buffer_putsnoflush(buffer_1, "Content-Length: ") ; + n += buffer_putnoflush(buffer_1, fmt, size_fmt(fmt, sizeof(txt1) + sizeof(txt2) + sizeof(txt3) + sizeof(txt4) - 4 + 2 * strlen(rsl) + strlen(text))) ; + n += buffer_putnoflush(buffer_1, "\r\n", 2) ; + } + n += buffer_putnoflush(buffer_1, "\r\n", 2) ; + if (rql->m != TIPIDEE_METHOD_HEAD) + { + n += buffer_putsnoflush(buffer_1, txt1) ; + n += buffer_putsnoflush(buffer_1, rsl) ; + n += buffer_putsnoflush(buffer_1, txt2) ; + n += buffer_putsnoflush(buffer_1, rsl) ; + n += buffer_putsnoflush(buffer_1, txt3) ; + n += buffer_putsnoflush(buffer_1, text) ; + n += buffer_putsnoflush(buffer_1, txt4) ; + } + return n ; +} diff --git a/src/libtipidee/tipidee_response_header_builtin.c b/src/libtipidee/tipidee_response_header_builtin.c new file mode 100644 index 0000000..0125cb8 --- /dev/null +++ b/src/libtipidee/tipidee_response_header_builtin.c @@ -0,0 +1,40 @@ +/* ISC license. */ + +#include <string.h> +#include <stdlib.h> + +#include <tipidee/config.h> +#include <tipidee/response.h> + +static tipidee_response_header_builtin const tipidee_response_header_builtin_table_[] = +{ + { .key = "Accept-Ranges", .value = "none" }, + { .key = "Cache-Control", .value = "private" }, + { .key = "Content-Security-Policy", .value = "default-src 'self'; style-src 'self' 'unsafe-inline';" }, + { .key = "Referrer-Policy", .value = "no-referrer-when-downgrade" }, + { .key = "Server", .value = "tipidee/" TIPIDEE_VERSION }, + { .key = "Vary", .value = "Accept-Encoding" }, + { .key = "X-Content-Type-Options", .value = "nosniff" }, + { .key = "X-Frame-Options", .value = "DENY" }, + { .key = "X-XSS-Protection", .value = "1; mode=block" }, + { .key = 0, .value = 0 }, +} ; + +tipidee_response_header_builtin const *tipidee_response_header_builtin_table = tipidee_response_header_builtin_table_ ; + +static int tipidee_response_header_builtin_cmp (void const *a, void const *b) +{ + return strcmp((char const *)a, ((tipidee_response_header_builtin const *)b)->key) ; +} + +char const *tipidee_response_header_builtin_search (char const *key) +{ + tipidee_response_header_builtin const *p = bsearch( + key, + tipidee_response_header_builtin_table_, + sizeof(tipidee_response_header_builtin_table_) / sizeof(tipidee_response_header_builtin) - 1, + sizeof(tipidee_response_header_builtin), + &tipidee_response_header_builtin_cmp) ; + return p ? p->value : 0 ; +} + diff --git a/src/libtipidee/tipidee_response_header_common_put.c b/src/libtipidee/tipidee_response_header_common_put.c new file mode 100644 index 0000000..8352ba9 --- /dev/null +++ b/src/libtipidee/tipidee_response_header_common_put.c @@ -0,0 +1,23 @@ +/* ISC license. */ + +#include <stdint.h> + +#include <skalibs/buffer.h> + +#include <tipidee/config.h> +#include <tipidee/response.h> + +size_t tipidee_response_header_common_put (buffer *b, uint32_t options, tain const *stamp) +{ + char fmt[128] ; + size_t m = buffer_putnoflush(b, fmt, tipidee_response_header_date_fmt(fmt, 128, stamp)) ; + for (tipidee_response_header_builtin const *p = tipidee_response_header_builtin_table ; p->key ; p++) + { + m += buffer_putsnoflush(b, p->key) ; + m += buffer_putnoflush(b, ": ", 2) ; + m += buffer_putsnoflush(b, p->value) ; + m += buffer_putnoflush(b, "\r\n", 2) ; + } + if (options & 1) m += buffer_putsnoflush(b, "Connection: close\r\n") ; + return m ; +} diff --git a/src/libtipidee/tipidee_response_header_date_fmt.c b/src/libtipidee/tipidee_response_header_date_fmt.c new file mode 100644 index 0000000..df19673 --- /dev/null +++ b/src/libtipidee/tipidee_response_header_date_fmt.c @@ -0,0 +1,24 @@ +/* ISC license. */ + +#include <string.h> +#include <time.h> + +#include <skalibs/tai.h> +#include <skalibs/djbtime.h> + +#include <tipidee/response.h> + +size_t tipidee_response_header_date_fmt (char *s, size_t max, tain const *stamp) +{ + size_t m = 0, l ; + struct tm tm ; + if (m + 6 > max) return 0 ; + if (!localtm_from_tai(&tm, tain_secp(stamp), 0)) return 0 ; + memcpy(s, "Date: ", 6) ; m += 6 ; + l = strftime(s + m, max - m, "%a, %d %b %Y %T GMT", &tm) ; + if (!l) return 0 ; + m += l ; + if (m + 2 > max) return 0 ; + s[m++] = '\r' ; s[m++] = '\n' ; + return m ; +} diff --git a/src/libtipidee/tipidee_response_status.c b/src/libtipidee/tipidee_response_status.c new file mode 100644 index 0000000..aedec39 --- /dev/null +++ b/src/libtipidee/tipidee_response_status.c @@ -0,0 +1,27 @@ +/* ISC license. */ + +#include <skalibs/types.h> +#include <skalibs/buffer.h> + +#include <tipidee/response.h> + +size_t tipidee_response_status (buffer *b, tipidee_rql const *rql, unsigned int status, char const *line) +{ + size_t n = 0 ; + char fmt[UINT_FMT] ; + n += buffer_putnoflush(b, "HTTP/", 5) ; + n += buffer_putnoflush(b, fmt, uint_fmt(fmt, rql->http_major ? rql->http_major : 1)) ; + n += buffer_putnoflush(b, ".", 1) ; + n += buffer_putnoflush(b, fmt, uint_fmt(fmt, rql->http_major ? rql->http_minor : 1)) ; + n += buffer_putnoflush(b, " ", 1) ; + if (status) + { + char fmt[UINT_FMT] ; + size_t m = uint_fmt(fmt, status) ; + n += buffer_putnoflush(b, fmt, m) ; + n += buffer_putnoflush(b, " ", 1) ; + } + n += buffer_putsnoflush(b, line) ; + n += buffer_putnoflush(b, "\r\n", 2) ; + return n ; +} diff --git a/src/libtipidee/tipidee_rql_read.c b/src/libtipidee/tipidee_rql_read.c new file mode 100644 index 0000000..f3508cf --- /dev/null +++ b/src/libtipidee/tipidee_rql_read.c @@ -0,0 +1,85 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> +#include <strings.h> + +#include <skalibs/types.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/unix-timed.h> +// #include <skalibs/lolstdio.h> + +#include <tipidee/method.h> +#include <tipidee/uri.h> +#include <tipidee/rql.h> + +static inline uint8_t tokenize_cclass (char c) +{ + switch (c) + { + case '\0' : return 0 ; + case ' ' : + case '\t' : return 1 ; + default : return 2 ; + } +} + +static inline int rql_tokenize (char *s, size_t *tab) +{ + uint8_t const table[2][3] = + { + { 0x02, 0x00, 0x11 }, + { 0x02, 0x20, 0x01 } + } ; + size_t i = 0 ; + unsigned int tokens = 0 ; + uint8_t state = 0 ; + for (; state < 2 ; i++) + { + uint8_t c = table[state][tokenize_cclass(s[i])] ; + state = c & 3 ; + if (c & 0x10) + { + if (tokens >= 3) goto err ; + tab[tokens++] = i ; + } + if (c & 0x20) s[i] = 0 ; + } + return 1 ; + err: + return 0 ; +} + +static inline int get_version (char const *in, tipidee_rql *rql) +{ + size_t l ; + if (strncmp(in, "HTTP/", 5)) return 0 ; + in += 5 ; + l = uint_scan(in, &rql->http_major) ; + if (!l) return 0 ; + in += l ; + if (*in++ != '.') return 0 ; + return !!uint0_scan(in, &rql->http_minor) ; +} + +int tipidee_rql_read (buffer *b, char *buf, size_t max, size_t *w, tipidee_rql *rql, tain const *deadline, tain *stamp) +{ + size_t pos[3] = { 0 } ; + if (timed_getlnmax(b, buf, max, &pos[0], '\n', deadline, stamp) <= 0) return -1 ; + buf[--pos[0]] = 0 ; + if (buf[pos[0] - 1] == '\r') buf[--pos[0]] = 0 ; +// LOLDEBUG("tipidee_rql_read: timed_getlnmax: len is %zu, line is %s", pos[0], buf) ; + if (!rql_tokenize(buf, pos)) return 400 ; +// LOLDEBUG("tipidee_rql_read: method: %s, version: %s, uri to parse: %s", buf + pos[0], buf + pos[2], buf + pos[1]) ; + rql->m = tipidee_method_tonum(buf + pos[0]) ; + if (rql->m == TIPIDEE_METHOD_UNKNOWN) return 400 ; + if (!get_version(buf + pos[2], rql)) return 400 ; + if (rql->m != TIPIDEE_METHOD_OPTIONS || strcmp(buf + pos[1], "*")) + { + size_t l = tipidee_uri_parse(buf, max, buf + pos[1], &rql->uri) ; + if (!l) return 400 ; + *w = l ; + } + return 0 ; +} diff --git a/src/libtipidee/tipidee_uri_parse.c b/src/libtipidee/tipidee_uri_parse.c new file mode 100644 index 0000000..10b0f91 --- /dev/null +++ b/src/libtipidee/tipidee_uri_parse.c @@ -0,0 +1,184 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> + +#include <skalibs/uint16.h> +#include <skalibs/bytestr.h> +#include <skalibs/fmtscan.h> +#include <skalibs/lolstdio.h> + +#include <tipidee/uri.h> + + +/* + + Decodes an URI. + Accepts absolute (http and https) and local, decodes %-encoding up to ? query. + +st\ev 0 1 2 3 4 5 6 7 8 9 a b c d e + \0 invalid % ? / : @ h t p s 0-9 a-f other delim + +00 Pp +START X X X X PATH? X X H X X X X X X X + +01 +H X X X X X X X X HT X X X X X X + +02 +HT X X X X X X X X HTT X X X X X X + +03 +HTT X X X X X X X X X HTTP X X X X X + +04 +HTTP X X X X X AUTH X X X X HTTPS X X X X + +05 s +HTTPS X X X X X AUTH X X X X X X X X X + +06 +AUTH X X X X AUTH/ X X X X X X X X X X + +07 +AUTH/ X X X X HOST X X X X X X X X X X + +08 H Hp Hp Hp Hp Hp Hp Hp Hp +HOST X X HQ H1 X X X H1 H1 H1 H1 H1 H1 H1 X + +09 p a a +HQ X X H1 X X X X X X X X HQ1 HQ1 X X + +0a ab ab +HQ1 X X X X X X X X X X X H1 H1 X X + +0b p zPp zm p p p p p p p +H1 END X HQ H1 PATH PORT X H1 H1 H1 H1 H1 H1 H1 X + +0c Pp p +PORT X X X X PATH X X X X X X PORT1 X X X + +0d zc zcPp p +PORT1 END X X X PATH X X X X X X PORT1 X X X + +0e p zQ p p p p p p p p p p +PATH END X Q QUERY PATH PATH PATH PATH PATH PATH PATH PATH PATH PATH X + +0f p a a +Q X X PATH X X X X X X X X Q1 Q1 X X + +10 ab ab +Q1 X X X X X X X X X X X PATH PATH X X + +11 p p p p p p p p p p p p p p +QUERY END X QUERY QUERY QUERY QUERY QUERY QUERY QUERY QUERY QUERY QUERY QUERY QUERY QUERY + +12 p zQ p p p p p p p p p +PATH? END X Q QUERY X PATH PATH PATH PATH PATH PATH PATH PATH PATH X + +st\ev 0 1 2 3 4 5 6 7 8 9 a b c d e + \0 invalid % ? / : @ h t p s 0-9 a-f other delim + +END = 13, X = 14 + +0x8000 s ssl +0x4000 H start host +0x2000 z print \0 +0x1000 m mark +0x0800 c scan port from mark, reset to mark +0x0400 P start path +0x0200 p print cur +0x0100 Q start query +0x0080 a push num +0x0040 b decode num, print num, reinit + +*/ + +static inline uint8_t uridecode_cclass (char c) +{ + static uint8_t const table[128] = "01111111111111111111111111111111161162>>>>>=>==4;;;;;;;;;;5>>=>36<<<<<<====================>1>==1<<<<<<=7=======9==:8======111=1" ; + return c < 0 ? 1 : table[(uint8_t)c] - '0' ; +} + +size_t tipidee_uri_parse (char *out, size_t max, char const *in, tipidee_uri *uri) +{ + static uint16_t const table[19][15] = + { + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0612, 0x0014, 0x0014, 0x0001, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0002, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0003, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0004, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0006, 0x0014, 0x0014, 0x0014, 0x0014, 0x0005, 0x0014, 0x0014, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x8006, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0007, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0008, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x4009, 0x420b, 0x0014, 0x0014, 0x0014, 0x420b, 0x420b, 0x420b, 0x420b, 0x420b, 0x420b, 0x420b, 0x0014 }, + { 0x0014, 0x0014, 0x020b, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x008a, 0x008a, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x00cb, 0x00cb, 0x0014, 0x0014 }, + { 0x0213, 0x0014, 0x0009, 0x000b, 0x260e, 0x300c, 0x0014, 0x020b, 0x020b, 0x020b, 0x020b, 0x020b, 0x020b, 0x020b, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x060e, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x020d, 0x0014, 0x0014, 0x0014 }, + { 0x2813, 0x0014, 0x0014, 0x0014, 0x2e0e, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x020d, 0x0014, 0x0014, 0x0014 }, + { 0x0213, 0x0014, 0x000f, 0x2111, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x0014 }, + { 0x0014, 0x0014, 0x020e, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0090, 0x0090, 0x0014, 0x0014 }, + { 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x00ce, 0x00ce, 0x0014, 0x0014 }, + { 0x0213, 0x0014, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211, 0x0211 }, + { 0x0213, 0x0014, 0x000f, 0x2111, 0x0014, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x020e, 0x0014 } + } ; + size_t w = 0, lastslash = 0, mark = 0 ; + char const *host = 0 ; + char const *path = 0 ; + char const *query = 0 ; + uint16_t port = 0 ; + uint16_t state = 0 ; + unsigned char decoded = 0 ; + uint8_t ssl = 0 ; + for (; state < 0x13 ; in++) + { + uint16_t c = table[state][uridecode_cclass(*in)] ; +/* + LOLDEBUG("tipidee_uri_parse: state %hu, event %c, newstate %hu, actions %s%s%s%s%s%s%s%s%s%s", state, *in, c & 0x1f, + c & 0x8000 ? "s" : "", + c & 0x4000 ? "H" : "", + c & 0x2000 ? "z" : "", + c & 0x1000 ? "m" : "", + c & 0x0800 ? "c" : "", + c & 0x0400 ? "P" : "", + c & 0x0200 ? "p" : "", + c & 0x0100 ? "Q" : "", + c & 0x0080 ? "a" : "", + c & 0x0040 ? "b" : "" + ) ; +*/ + state = c & 0x1f ; + if (c & 0x8000) ssl = 1 ; + if (c & 0x4000) host = out + w ; + if (c & 0x2000) { if (w >= max) return 0 ; out[w++] = 0 ; } + if (c & 0x1000) mark = w ; + if (c & 0x0800) { if (!uint160_scan(out + mark, &port)) return 0 ; w = mark ; } + if (c & 0x0400) path = out + w ; + if (c & 0x0200) { if (w >= max) return 0 ; out[w++] = *in ; } + if (c & 0x0100) query = out + w ; + if (c & 0x0080) decoded = (decoded << 4) | fmtscan_num(*in, 16) ; + if (c & 0x0040) + { + if (w >= max) return 0 ; + if (decoded == '/') lastslash = w ; + out[w++] = decoded ; + decoded = 0 ; + } + } + if (state > 0x13) return 0 ; + if (path) + { + size_t len = strlen(path) ; + if (len >= 3 && !memcmp(path + len - 3, "/..", 3)) return 0 ; + if (strstr(path, "/../")) return 0 ; + } + uri->host = host ; + uri->port = port ; + uri->path = path ? path : "/" ; + uri->query = query ; + uri->lastslash = path ? lastslash - (path - out) : 0 ; + uri->https = ssl ; + return w ; +} |