summaryrefslogtreecommitdiff
path: root/clients/web.h
blob: a65249809d7af91be2cb3fb7392ca62fe7cd1f32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#ifndef BOKF_WEB_H
#define BOKF_WEB_H

#include <stddef.h>
#include <stdint.h>

#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