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
|