#ifndef BOKF_WEB_H #define BOKF_WEB_H #include #include #include "util.h" /* Pure parts of bokfweb, the login gate in front of the browser terminal: HTTP request parsing, form and cookie decoding, HTML escaping, the session store and the per-address login limiter. Unit-tested in tests/test_web.c; bokfweb.c adds sockets and the bokfd calls. */ #define WEB_MAX_REQUEST 8192 struct web_req { char method[8]; char path[256]; /* without the query */ char query[256]; char cookie[512]; char forwarded_for[64]; char forwarded_uri[512]; const char *body; /* points into the parsed buffer */ size_t body_len; }; /* Parses one HTTP/1.x request in buf[0..n). Returns 0 when complete (headers and Content-Length bytes of body), 1 when more bytes are needed, -1 when malformed or too large. */ int web_parse_request(const char *buf, size_t n, struct web_req *r); /* Value of key in an application/x-www-form-urlencoded string (also a query string), decoded ('+' and %XX). 0 when found, -1 otherwise; a value that does not fit or decodes to a NUL byte counts as not found. */ int web_form_get(const char *form, size_t len, const char *key, char *out, size_t cap); /* Value of cookie `name` in a Cookie header. 0 when found. */ int web_cookie_get(const char *header, const char *name, char *out, size_t cap); /* Appends s to b with & < > " ' escaped. */ void web_html_escape(struct buf *b, const char *s); /* Whether s is a non-empty token of [A-Za-z0-9_-] only (cookie values and handles are generated that way; anything else is rejected unread). */ int web_token_ok(const char *s); /* --- sessions: cookie token -> bokfd session, plus the terminal handle */ #define WEB_MAX_SESSIONS 64 #define WEB_SESSION_TTL (12 * 3600) /* absolute; bokfd's idle TTL applies too */ struct web_session { char token[64]; /* the cookie value */ char handle[64]; /* goes into the terminal URL (?arg=) */ char bokf[128]; /* bokfd session id */ char user[64]; int64_t created; }; struct web_store { struct web_session s[WEB_MAX_SESSIONS]; }; /* Adds a session with fresh random token and handle; the oldest session is replaced when the store is full. Returns it, or NULL when the random source fails. */ struct web_session *web_store_add(struct web_store *st, const char *bokf, const char *user, int64_t now); /* The live session with this cookie token / terminal handle, or NULL. */ struct web_session *web_store_by_token(struct web_store *st, const char *token, int64_t now); struct web_session *web_store_by_handle(struct web_store *st, const char *handle, int64_t now); void web_store_del(struct web_session *s); /* --- failed-login limiter per client address */ #define WEB_RL_SLOTS 256 #define WEB_RL_MAX_FAILS 5 #define WEB_RL_WINDOW 900 struct web_rl_entry { char addr[64]; int fails; int64_t window_end; }; struct web_rl { struct web_rl_entry e[WEB_RL_SLOTS]; }; /* Seconds until addr may try again, 0 when it may try now. */ int64_t web_rl_blocked(const struct web_rl *rl, const char *addr, int64_t now); void web_rl_fail(struct web_rl *rl, const char *addr, int64_t now); void web_rl_ok(struct web_rl *rl, const char *addr); #endif