1*4c131d56Stobhe /* $OpenBSD: httpd.c,v 1.74 2024/04/08 12:45:18 tobhe Exp $ */
2b7b6a941Sreyk
3b7b6a941Sreyk /*
4b7b6a941Sreyk * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
5b7b6a941Sreyk *
6b7b6a941Sreyk * Permission to use, copy, modify, and distribute this software for any
7b7b6a941Sreyk * purpose with or without fee is hereby granted, provided that the above
8b7b6a941Sreyk * copyright notice and this permission notice appear in all copies.
9b7b6a941Sreyk *
10b7b6a941Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11b7b6a941Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12b7b6a941Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13b7b6a941Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14b7b6a941Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15b7b6a941Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16b7b6a941Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17b7b6a941Sreyk */
18b7b6a941Sreyk
19b7b6a941Sreyk #include <sys/types.h>
20b7b6a941Sreyk #include <sys/queue.h>
21b7b6a941Sreyk #include <sys/socket.h>
22ef353f36Sreyk #include <sys/stat.h>
23b7b6a941Sreyk #include <sys/resource.h>
24b7b6a941Sreyk
25b7b6a941Sreyk #include <netinet/in.h>
26b7b6a941Sreyk #include <arpa/inet.h>
27b7b6a941Sreyk
28b7b6a941Sreyk #include <stdio.h>
29b7b6a941Sreyk #include <stdlib.h>
3086f952e4Sreyk #include <stdarg.h>
3186f952e4Sreyk #include <string.h>
3286f952e4Sreyk #include <signal.h>
33b7b6a941Sreyk #include <getopt.h>
3408141198Sreyk #include <netdb.h>
35b7b6a941Sreyk #include <fnmatch.h>
36b7b6a941Sreyk #include <err.h>
37b7b6a941Sreyk #include <errno.h>
38b7b6a941Sreyk #include <event.h>
390f12961aSreyk #include <syslog.h>
40b7b6a941Sreyk #include <unistd.h>
41b7b6a941Sreyk #include <ctype.h>
42b7b6a941Sreyk #include <pwd.h>
43b7b6a941Sreyk
44b7b6a941Sreyk #include "httpd.h"
45b7b6a941Sreyk
46b9fc9a72Sderaadt #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
47b9fc9a72Sderaadt
48b7b6a941Sreyk __dead void usage(void);
49b7b6a941Sreyk
50b7b6a941Sreyk int parent_configure(struct httpd *);
51b7b6a941Sreyk void parent_configure_done(struct httpd *);
524703e0faSreyk void parent_reload(struct httpd *, unsigned int, const char *);
53844c3615Sreyk void parent_reopen(struct httpd *);
54b7b6a941Sreyk void parent_sig_handler(int, short, void *);
55b7b6a941Sreyk void parent_shutdown(struct httpd *);
56b7b6a941Sreyk int parent_dispatch_server(int, struct privsep_proc *,
57b7b6a941Sreyk struct imsg *);
58844c3615Sreyk int parent_dispatch_logger(int, struct privsep_proc *,
59844c3615Sreyk struct imsg *);
60fe006a11Sclaudio void parent_tls_ticket_rekey_start(struct server *);
61fe006a11Sclaudio void parent_tls_ticket_rekey(int, short, void *);
62b7b6a941Sreyk
63b7b6a941Sreyk struct httpd *httpd_env;
64b7b6a941Sreyk
65b7b6a941Sreyk static struct privsep_proc procs[] = {
66844c3615Sreyk { "server", PROC_SERVER, parent_dispatch_server, server },
67844c3615Sreyk { "logger", PROC_LOGGER, parent_dispatch_logger, logger }
68b7b6a941Sreyk };
69b7b6a941Sreyk
70488a4384Sderaadt enum privsep_procid privsep_process;
71488a4384Sderaadt
72b7b6a941Sreyk void
parent_sig_handler(int sig,short event,void * arg)73b7b6a941Sreyk parent_sig_handler(int sig, short event, void *arg)
74b7b6a941Sreyk {
75b7b6a941Sreyk struct privsep *ps = arg;
76b7b6a941Sreyk
77b7b6a941Sreyk switch (sig) {
78b7b6a941Sreyk case SIGTERM:
79b7b6a941Sreyk case SIGINT:
80b7b6a941Sreyk parent_shutdown(ps->ps_env);
81b7b6a941Sreyk break;
82b7b6a941Sreyk case SIGHUP:
83b7b6a941Sreyk log_info("%s: reload requested with SIGHUP", __func__);
84b7b6a941Sreyk
85b7b6a941Sreyk /*
86b7b6a941Sreyk * This is safe because libevent uses async signal handlers
87b7b6a941Sreyk * that run in the event loop and not in signal context.
88b7b6a941Sreyk */
89b7b6a941Sreyk parent_reload(ps->ps_env, CONFIG_RELOAD, NULL);
90b7b6a941Sreyk break;
91b7b6a941Sreyk case SIGPIPE:
92b7b6a941Sreyk /* ignore */
93b7b6a941Sreyk break;
94844c3615Sreyk case SIGUSR1:
95844c3615Sreyk log_info("%s: reopen requested with SIGUSR1", __func__);
96844c3615Sreyk
97844c3615Sreyk parent_reopen(ps->ps_env);
98844c3615Sreyk break;
99b7b6a941Sreyk default:
100b7b6a941Sreyk fatalx("unexpected signal");
101b7b6a941Sreyk }
102b7b6a941Sreyk }
103b7b6a941Sreyk
104b7b6a941Sreyk __dead void
usage(void)105b7b6a941Sreyk usage(void)
106b7b6a941Sreyk {
107b7b6a941Sreyk extern char *__progname;
108b7b6a941Sreyk
109b7b6a941Sreyk fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
110b7b6a941Sreyk __progname);
111b7b6a941Sreyk exit(1);
112b7b6a941Sreyk }
113b7b6a941Sreyk
114b7b6a941Sreyk int
main(int argc,char * argv[])115b7b6a941Sreyk main(int argc, char *argv[])
116b7b6a941Sreyk {
117b7b6a941Sreyk int c;
118bde157a8Sjsg unsigned int proc;
119b7b6a941Sreyk int debug = 0, verbose = 0;
1204703e0faSreyk uint32_t opts = 0;
121b7b6a941Sreyk struct httpd *env;
122b7b6a941Sreyk struct privsep *ps;
123b7b6a941Sreyk const char *conffile = CONF_FILE;
124887f279cSrzalamena enum privsep_procid proc_id = PROC_PARENT;
125887f279cSrzalamena int proc_instance = 0;
126887f279cSrzalamena const char *errp, *title = NULL;
127887f279cSrzalamena int argc0 = argc;
128b7b6a941Sreyk
129887f279cSrzalamena while ((c = getopt(argc, argv, "dD:nf:I:P:v")) != -1) {
130b7b6a941Sreyk switch (c) {
131b7b6a941Sreyk case 'd':
132b7b6a941Sreyk debug = 2;
133b7b6a941Sreyk break;
134b7b6a941Sreyk case 'D':
135b7b6a941Sreyk if (cmdline_symset(optarg) < 0)
136b7b6a941Sreyk log_warnx("could not parse macro definition %s",
137b7b6a941Sreyk optarg);
138b7b6a941Sreyk break;
139b7b6a941Sreyk case 'n':
140b7b6a941Sreyk debug = 2;
141b7b6a941Sreyk opts |= HTTPD_OPT_NOACTION;
142b7b6a941Sreyk break;
143b7b6a941Sreyk case 'f':
144b7b6a941Sreyk conffile = optarg;
145b7b6a941Sreyk break;
146b7b6a941Sreyk case 'v':
147b7b6a941Sreyk verbose++;
148b7b6a941Sreyk opts |= HTTPD_OPT_VERBOSE;
149b7b6a941Sreyk break;
150887f279cSrzalamena case 'P':
151887f279cSrzalamena title = optarg;
152887f279cSrzalamena proc_id = proc_getid(procs, nitems(procs), title);
153887f279cSrzalamena if (proc_id == PROC_MAX)
154887f279cSrzalamena fatalx("invalid process name");
155887f279cSrzalamena break;
156887f279cSrzalamena case 'I':
157887f279cSrzalamena proc_instance = strtonum(optarg, 0,
158887f279cSrzalamena PROC_MAX_INSTANCES, &errp);
159887f279cSrzalamena if (errp)
160887f279cSrzalamena fatalx("invalid process instance");
161887f279cSrzalamena break;
162b7b6a941Sreyk default:
163b7b6a941Sreyk usage();
164b7b6a941Sreyk }
165b7b6a941Sreyk }
166b7b6a941Sreyk
1670f12961aSreyk /* log to stderr until daemonized */
1680f12961aSreyk log_init(debug ? debug : 1, LOG_DAEMON);
169b7b6a941Sreyk
170b7b6a941Sreyk argc -= optind;
171b7b6a941Sreyk if (argc > 0)
172b7b6a941Sreyk usage();
173b7b6a941Sreyk
174b7b6a941Sreyk if ((env = calloc(1, sizeof(*env))) == NULL ||
175b7b6a941Sreyk (ps = calloc(1, sizeof(*ps))) == NULL)
176b7b6a941Sreyk exit(1);
177b7b6a941Sreyk
178b7b6a941Sreyk httpd_env = env;
179b7b6a941Sreyk env->sc_ps = ps;
180b7b6a941Sreyk ps->ps_env = env;
181b7b6a941Sreyk TAILQ_INIT(&ps->ps_rcsocks);
182b7b6a941Sreyk env->sc_conffile = conffile;
183b7b6a941Sreyk env->sc_opts = opts;
184b7b6a941Sreyk
185b7b6a941Sreyk if (parse_config(env->sc_conffile, env) == -1)
186b7b6a941Sreyk exit(1);
187b7b6a941Sreyk
188b7b6a941Sreyk if (geteuid())
189b7b6a941Sreyk errx(1, "need root privileges");
190b7b6a941Sreyk
191b7b6a941Sreyk if ((ps->ps_pw = getpwnam(HTTPD_USER)) == NULL)
192b7b6a941Sreyk errx(1, "unknown user %s", HTTPD_USER);
193b7b6a941Sreyk
194b7b6a941Sreyk /* Configure the control socket */
1955f32a1dbSflorian ps->ps_csock.cs_name = NULL;
196b7b6a941Sreyk
1970f12961aSreyk log_init(debug, LOG_DAEMON);
198871fc12cSreyk log_setverbose(verbose);
199b7b6a941Sreyk
200b7b6a941Sreyk if (env->sc_opts & HTTPD_OPT_NOACTION)
201b7b6a941Sreyk ps->ps_noaction = 1;
202b7b6a941Sreyk
203b7b6a941Sreyk ps->ps_instances[PROC_SERVER] = env->sc_prefork_server;
204887f279cSrzalamena ps->ps_instance = proc_instance;
20571b294e4Sreyk if (title != NULL)
20671b294e4Sreyk ps->ps_title[proc_id] = title;
207b7b6a941Sreyk
208cf605f40Sreyk if (env->sc_chroot == NULL)
209cf605f40Sreyk env->sc_chroot = ps->ps_pw->pw_dir;
210bde157a8Sjsg for (proc = 0; proc < nitems(procs); proc++)
211bde157a8Sjsg procs[proc].p_chroot = env->sc_chroot;
212bde157a8Sjsg
213032d2b93Sbeck if (env->sc_logdir == NULL) {
214032d2b93Sbeck if (asprintf(&env->sc_logdir, "%s%s", env->sc_chroot,
215032d2b93Sbeck HTTPD_LOGROOT) == -1)
216032d2b93Sbeck errx(1, "malloc failed");
217032d2b93Sbeck }
218032d2b93Sbeck
21971b294e4Sreyk /* only the parent returns */
22038b34180Sbluhm proc_init(ps, procs, nitems(procs), debug, argc0, argv, proc_id);
221887f279cSrzalamena
2220f12961aSreyk log_procinit("parent");
223887f279cSrzalamena
224887f279cSrzalamena if (ps->ps_noaction == 0)
225887f279cSrzalamena log_info("startup");
226b7b6a941Sreyk
227b90d0acdSderaadt if (pledge("stdio rpath wpath cpath inet dns sendfd", NULL) == -1)
2281019da98Sflorian fatal("pledge");
2291019da98Sflorian
230b7b6a941Sreyk event_init();
231b7b6a941Sreyk
232b7b6a941Sreyk signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps);
233b7b6a941Sreyk signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps);
234b7b6a941Sreyk signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);
235b7b6a941Sreyk signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);
236844c3615Sreyk signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps);
237b7b6a941Sreyk
238b7b6a941Sreyk signal_add(&ps->ps_evsigint, NULL);
239b7b6a941Sreyk signal_add(&ps->ps_evsigterm, NULL);
240b7b6a941Sreyk signal_add(&ps->ps_evsighup, NULL);
241b7b6a941Sreyk signal_add(&ps->ps_evsigpipe, NULL);
242844c3615Sreyk signal_add(&ps->ps_evsigusr1, NULL);
243b7b6a941Sreyk
244887f279cSrzalamena proc_connect(ps);
245b7b6a941Sreyk
246b7b6a941Sreyk if (load_config(env->sc_conffile, env) == -1) {
247b7b6a941Sreyk proc_kill(env->sc_ps);
248b7b6a941Sreyk exit(1);
249b7b6a941Sreyk }
250b7b6a941Sreyk
251b7b6a941Sreyk if (env->sc_opts & HTTPD_OPT_NOACTION) {
252b7b6a941Sreyk fprintf(stderr, "configuration OK\n");
253b7b6a941Sreyk proc_kill(env->sc_ps);
254b7b6a941Sreyk exit(0);
255b7b6a941Sreyk }
256b7b6a941Sreyk
257fe006a11Sclaudio /* initialize the TLS session id to a random key for all procs */
258fe006a11Sclaudio arc4random_buf(env->sc_tls_sid, sizeof(env->sc_tls_sid));
259fe006a11Sclaudio
260b7b6a941Sreyk if (parent_configure(env) == -1)
261b7b6a941Sreyk fatalx("configuration failed");
262b7b6a941Sreyk
263b7b6a941Sreyk event_dispatch();
264b7b6a941Sreyk
265b7b6a941Sreyk parent_shutdown(env);
266b7b6a941Sreyk /* NOTREACHED */
267b7b6a941Sreyk
268b7b6a941Sreyk return (0);
269b7b6a941Sreyk }
270b7b6a941Sreyk
271b7b6a941Sreyk int
parent_configure(struct httpd * env)272b7b6a941Sreyk parent_configure(struct httpd *env)
273b7b6a941Sreyk {
274b7b6a941Sreyk int id;
275b7b6a941Sreyk struct ctl_flags cf;
276b7b6a941Sreyk int ret = -1;
277b7b6a941Sreyk struct server *srv;
2789b9ff8ecSreyk struct media_type *media;
279602531d9Sreyk struct auth *auth;
2809b9ff8ecSreyk
2819b9ff8ecSreyk RB_FOREACH(media, mediatypes, env->sc_mediatypes) {
2829b9ff8ecSreyk if (config_setmedia(env, media) == -1)
2839b9ff8ecSreyk fatal("send media");
2849b9ff8ecSreyk }
285b7b6a941Sreyk
286602531d9Sreyk TAILQ_FOREACH(auth, env->sc_auth, auth_entry) {
287602531d9Sreyk if (config_setauth(env, auth) == -1)
288602531d9Sreyk fatal("send auth");
289602531d9Sreyk }
290602531d9Sreyk
291bd1bab2fSreyk /* First send the servers... */
292b7b6a941Sreyk TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
293bd1bab2fSreyk if (srv->srv_conf.flags & SRVFLAG_LOCATION)
294bd1bab2fSreyk continue;
295fe006a11Sclaudio /* start the rekey of the tls ticket keys */
296fe006a11Sclaudio if (srv->srv_conf.flags & SRVFLAG_TLS &&
297fe006a11Sclaudio srv->srv_conf.tls_ticket_lifetime)
298fe006a11Sclaudio parent_tls_ticket_rekey_start(srv);
299b7b6a941Sreyk if (config_setserver(env, srv) == -1)
3009b9ff8ecSreyk fatal("send server");
301b7b6a941Sreyk }
302bd1bab2fSreyk /* ...and now send the locations */
303bd1bab2fSreyk TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
304bd1bab2fSreyk if ((srv->srv_conf.flags & SRVFLAG_LOCATION) == 0)
305bd1bab2fSreyk continue;
306bd1bab2fSreyk if (config_setserver(env, srv) == -1)
307bd1bab2fSreyk fatal("send location");
308bd1bab2fSreyk }
309b7b6a941Sreyk
310b7b6a941Sreyk /* The servers need to reload their config. */
311844c3615Sreyk env->sc_reload = env->sc_prefork_server + 1;
312b7b6a941Sreyk
313b7b6a941Sreyk for (id = 0; id < PROC_MAX; id++) {
314b7b6a941Sreyk if (id == privsep_process)
315b7b6a941Sreyk continue;
316b7b6a941Sreyk cf.cf_opts = env->sc_opts;
317b7b6a941Sreyk cf.cf_flags = env->sc_flags;
318fe006a11Sclaudio memcpy(cf.cf_tls_sid, env->sc_tls_sid, sizeof(cf.cf_tls_sid));
319b7b6a941Sreyk
320f19e65beSreyk proc_compose(env->sc_ps, id, IMSG_CFG_DONE, &cf, sizeof(cf));
321b7b6a941Sreyk }
322b7b6a941Sreyk
323b7b6a941Sreyk ret = 0;
324b7b6a941Sreyk
325da6b6566Sclaudio config_purge(env, CONFIG_ALL & ~CONFIG_SERVERS);
326b7b6a941Sreyk return (ret);
327b7b6a941Sreyk }
328b7b6a941Sreyk
329b7b6a941Sreyk void
parent_reload(struct httpd * env,unsigned int reset,const char * filename)3304703e0faSreyk parent_reload(struct httpd *env, unsigned int reset, const char *filename)
331b7b6a941Sreyk {
332b7b6a941Sreyk if (env->sc_reload) {
333b7b6a941Sreyk log_debug("%s: already in progress: %d pending",
334b7b6a941Sreyk __func__, env->sc_reload);
335b7b6a941Sreyk return;
336b7b6a941Sreyk }
337b7b6a941Sreyk
338b7b6a941Sreyk /* Switch back to the default config file */
339b7b6a941Sreyk if (filename == NULL || *filename == '\0')
340b7b6a941Sreyk filename = env->sc_conffile;
341b7b6a941Sreyk
342b7b6a941Sreyk log_debug("%s: level %d config file %s", __func__, reset, filename);
343b7b6a941Sreyk
344b7b6a941Sreyk config_purge(env, CONFIG_ALL);
345b7b6a941Sreyk
346b7b6a941Sreyk if (reset == CONFIG_RELOAD) {
347b7b6a941Sreyk if (load_config(filename, env) == -1) {
348b7b6a941Sreyk log_debug("%s: failed to load config file %s",
349b7b6a941Sreyk __func__, filename);
350b7b6a941Sreyk }
351b7b6a941Sreyk
352b7b6a941Sreyk config_setreset(env, CONFIG_ALL);
353b7b6a941Sreyk
354b7b6a941Sreyk if (parent_configure(env) == -1) {
355b7b6a941Sreyk log_debug("%s: failed to commit config from %s",
356b7b6a941Sreyk __func__, filename);
357b7b6a941Sreyk }
358b7b6a941Sreyk } else
359b7b6a941Sreyk config_setreset(env, reset);
360b7b6a941Sreyk }
361b7b6a941Sreyk
362b7b6a941Sreyk void
parent_reopen(struct httpd * env)363844c3615Sreyk parent_reopen(struct httpd *env)
364844c3615Sreyk {
365f19e65beSreyk proc_compose(env->sc_ps, PROC_LOGGER, IMSG_CTL_REOPEN, NULL, 0);
366844c3615Sreyk }
367844c3615Sreyk
368844c3615Sreyk void
parent_configure_done(struct httpd * env)369b7b6a941Sreyk parent_configure_done(struct httpd *env)
370b7b6a941Sreyk {
371b7b6a941Sreyk int id;
372b7b6a941Sreyk
373b7b6a941Sreyk if (env->sc_reload == 0) {
374b7b6a941Sreyk log_warnx("%s: configuration already finished", __func__);
375b7b6a941Sreyk return;
376b7b6a941Sreyk }
377b7b6a941Sreyk
378b7b6a941Sreyk env->sc_reload--;
379b7b6a941Sreyk if (env->sc_reload == 0) {
380b7b6a941Sreyk for (id = 0; id < PROC_MAX; id++) {
381b7b6a941Sreyk if (id == privsep_process)
382b7b6a941Sreyk continue;
383b7b6a941Sreyk
384f19e65beSreyk proc_compose(env->sc_ps, id, IMSG_CTL_START, NULL, 0);
385b7b6a941Sreyk }
386b7b6a941Sreyk }
387b7b6a941Sreyk }
388b7b6a941Sreyk
389b7b6a941Sreyk void
parent_shutdown(struct httpd * env)390b7b6a941Sreyk parent_shutdown(struct httpd *env)
391b7b6a941Sreyk {
392b7b6a941Sreyk config_purge(env, CONFIG_ALL);
393b7b6a941Sreyk
394b7b6a941Sreyk proc_kill(env->sc_ps);
395b7b6a941Sreyk control_cleanup(&env->sc_ps->ps_csock);
3965f32a1dbSflorian if (env->sc_ps->ps_csock.cs_name != NULL)
3975f32a1dbSflorian (void)unlink(env->sc_ps->ps_csock.cs_name);
398b7b6a941Sreyk
399b7b6a941Sreyk free(env->sc_ps);
400b7b6a941Sreyk free(env);
401b7b6a941Sreyk
402b7b6a941Sreyk log_info("parent terminating, pid %d", getpid());
403b7b6a941Sreyk
404b7b6a941Sreyk exit(0);
405b7b6a941Sreyk }
406b7b6a941Sreyk
407b7b6a941Sreyk int
parent_dispatch_server(int fd,struct privsep_proc * p,struct imsg * imsg)408b7b6a941Sreyk parent_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
409b7b6a941Sreyk {
41069b8d8bcSreyk struct privsep *ps = p->p_ps;
41169b8d8bcSreyk struct httpd *env = ps->ps_env;
412b7b6a941Sreyk
413b7b6a941Sreyk switch (imsg->hdr.type) {
414b7b6a941Sreyk case IMSG_CFG_DONE:
415b7b6a941Sreyk parent_configure_done(env);
416b7b6a941Sreyk break;
417b7b6a941Sreyk default:
418b7b6a941Sreyk return (-1);
419b7b6a941Sreyk }
420b7b6a941Sreyk
421b7b6a941Sreyk return (0);
422b7b6a941Sreyk }
423b7b6a941Sreyk
424844c3615Sreyk int
parent_dispatch_logger(int fd,struct privsep_proc * p,struct imsg * imsg)425844c3615Sreyk parent_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
426844c3615Sreyk {
42769b8d8bcSreyk struct privsep *ps = p->p_ps;
42869b8d8bcSreyk struct httpd *env = ps->ps_env;
4294703e0faSreyk unsigned int v;
430844c3615Sreyk char *str = NULL;
431844c3615Sreyk
432844c3615Sreyk switch (imsg->hdr.type) {
433844c3615Sreyk case IMSG_CTL_RESET:
434844c3615Sreyk IMSG_SIZE_CHECK(imsg, &v);
435844c3615Sreyk memcpy(&v, imsg->data, sizeof(v));
436844c3615Sreyk parent_reload(env, v, NULL);
437844c3615Sreyk break;
438844c3615Sreyk case IMSG_CTL_RELOAD:
439844c3615Sreyk if (IMSG_DATA_SIZE(imsg) > 0)
440844c3615Sreyk str = get_string(imsg->data, IMSG_DATA_SIZE(imsg));
441844c3615Sreyk parent_reload(env, CONFIG_RELOAD, str);
442844c3615Sreyk free(str);
443844c3615Sreyk break;
444844c3615Sreyk case IMSG_CTL_SHUTDOWN:
445844c3615Sreyk parent_shutdown(env);
446844c3615Sreyk break;
447844c3615Sreyk case IMSG_CTL_REOPEN:
448844c3615Sreyk parent_reopen(env);
449844c3615Sreyk break;
450844c3615Sreyk case IMSG_CFG_DONE:
451844c3615Sreyk parent_configure_done(env);
452844c3615Sreyk break;
453cf605f40Sreyk case IMSG_LOG_OPEN:
454cf605f40Sreyk if (logger_open_priv(imsg) == -1)
455cf605f40Sreyk fatalx("failed to open log file");
456cf605f40Sreyk break;
457844c3615Sreyk default:
458844c3615Sreyk return (-1);
459844c3615Sreyk }
460844c3615Sreyk
461844c3615Sreyk return (0);
462844c3615Sreyk }
463844c3615Sreyk
464fe006a11Sclaudio void
parent_tls_ticket_rekey_start(struct server * srv)465fe006a11Sclaudio parent_tls_ticket_rekey_start(struct server *srv)
466fe006a11Sclaudio {
467fe006a11Sclaudio struct timeval tv;
468fe006a11Sclaudio
469fe006a11Sclaudio server_generate_ticket_key(&srv->srv_conf);
470fe006a11Sclaudio
471fe006a11Sclaudio evtimer_set(&srv->srv_evt, parent_tls_ticket_rekey, srv);
472fe006a11Sclaudio timerclear(&tv);
473fe006a11Sclaudio tv.tv_sec = srv->srv_conf.tls_ticket_lifetime / 4;
474fe006a11Sclaudio evtimer_add(&srv->srv_evt, &tv);
475fe006a11Sclaudio }
476fe006a11Sclaudio
477fe006a11Sclaudio void
parent_tls_ticket_rekey(int fd,short events,void * arg)478fe006a11Sclaudio parent_tls_ticket_rekey(int fd, short events, void *arg)
479fe006a11Sclaudio {
480fe006a11Sclaudio struct server *srv = arg;
481fe006a11Sclaudio struct timeval tv;
482fe006a11Sclaudio
483fe006a11Sclaudio server_generate_ticket_key(&srv->srv_conf);
484fe006a11Sclaudio proc_compose_imsg(httpd_env->sc_ps, PROC_SERVER, -1,
485fe006a11Sclaudio IMSG_TLSTICKET_REKEY, -1, -1, &srv->srv_conf.tls_ticket_key,
486fe006a11Sclaudio sizeof(srv->srv_conf.tls_ticket_key));
487fe006a11Sclaudio explicit_bzero(&srv->srv_conf.tls_ticket_key,
488fe006a11Sclaudio sizeof(srv->srv_conf.tls_ticket_key));
489fe006a11Sclaudio
490fe006a11Sclaudio evtimer_set(&srv->srv_evt, parent_tls_ticket_rekey, srv);
491fe006a11Sclaudio timerclear(&tv);
492fe006a11Sclaudio tv.tv_sec = srv->srv_conf.tls_ticket_lifetime / 4;
493fe006a11Sclaudio evtimer_add(&srv->srv_evt, &tv);
494fe006a11Sclaudio }
495fe006a11Sclaudio
496b7b6a941Sreyk /*
497b7b6a941Sreyk * Utility functions
498b7b6a941Sreyk */
499b7b6a941Sreyk
500b7b6a941Sreyk void
event_again(struct event * ev,int fd,short event,void (* fn)(int,short,void *),struct timeval * start,struct timeval * end,void * arg)501b7b6a941Sreyk event_again(struct event *ev, int fd, short event,
502b7b6a941Sreyk void (*fn)(int, short, void *),
503b7b6a941Sreyk struct timeval *start, struct timeval *end, void *arg)
504b7b6a941Sreyk {
505b7b6a941Sreyk struct timeval tv_next, tv_now, tv;
506b7b6a941Sreyk
507b7b6a941Sreyk getmonotime(&tv_now);
508b7b6a941Sreyk memcpy(&tv_next, end, sizeof(tv_next));
509b7b6a941Sreyk timersub(&tv_now, start, &tv_now);
510b7b6a941Sreyk timersub(&tv_next, &tv_now, &tv_next);
511b7b6a941Sreyk
512b7b6a941Sreyk memset(&tv, 0, sizeof(tv));
513b7b6a941Sreyk if (timercmp(&tv_next, &tv, >))
514b7b6a941Sreyk memcpy(&tv, &tv_next, sizeof(tv));
515b7b6a941Sreyk
516b7b6a941Sreyk event_del(ev);
517b7b6a941Sreyk event_set(ev, fd, event, fn, arg);
518b7b6a941Sreyk event_add(ev, &tv);
519b7b6a941Sreyk }
520b7b6a941Sreyk
521586dade4Sreyk int
expand_string(char * label,size_t len,const char * srch,const char * repl)522586dade4Sreyk expand_string(char *label, size_t len, const char *srch, const char *repl)
523586dade4Sreyk {
524586dade4Sreyk char *tmp;
525586dade4Sreyk char *p, *q;
526586dade4Sreyk
527586dade4Sreyk if ((tmp = calloc(1, len)) == NULL) {
528586dade4Sreyk log_debug("%s: calloc", __func__);
529586dade4Sreyk return (-1);
530586dade4Sreyk }
531586dade4Sreyk p = q = label;
532586dade4Sreyk while ((q = strstr(p, srch)) != NULL) {
533586dade4Sreyk *q = '\0';
534586dade4Sreyk if ((strlcat(tmp, p, len) >= len) ||
535586dade4Sreyk (strlcat(tmp, repl, len) >= len)) {
536586dade4Sreyk log_debug("%s: string too long", __func__);
537586dade4Sreyk free(tmp);
538586dade4Sreyk return (-1);
539586dade4Sreyk }
540586dade4Sreyk q += strlen(srch);
541586dade4Sreyk p = q;
542586dade4Sreyk }
543586dade4Sreyk if (strlcat(tmp, p, len) >= len) {
544586dade4Sreyk log_debug("%s: string too long", __func__);
545586dade4Sreyk free(tmp);
546586dade4Sreyk return (-1);
547586dade4Sreyk }
548586dade4Sreyk (void)strlcpy(label, tmp, len); /* always fits */
549586dade4Sreyk free(tmp);
550586dade4Sreyk
551586dade4Sreyk return (0);
552586dade4Sreyk }
553586dade4Sreyk
554b7b6a941Sreyk const char *
url_decode(char * url)555a383dca2Sreyk url_decode(char *url)
556a383dca2Sreyk {
557a383dca2Sreyk char *p, *q;
558a383dca2Sreyk char hex[3];
5594703e0faSreyk unsigned long x;
560a383dca2Sreyk
561a383dca2Sreyk hex[2] = '\0';
562a383dca2Sreyk p = q = url;
563a383dca2Sreyk
564a383dca2Sreyk while (*p != '\0') {
565a383dca2Sreyk switch (*p) {
566a383dca2Sreyk case '%':
567a383dca2Sreyk /* Encoding character is followed by two hex chars */
56800f3986fSreyk if (!(isxdigit((unsigned char)p[1]) &&
56900f3986fSreyk isxdigit((unsigned char)p[2])))
570a383dca2Sreyk return (NULL);
571a383dca2Sreyk
572a383dca2Sreyk hex[0] = p[1];
573a383dca2Sreyk hex[1] = p[2];
574a383dca2Sreyk
575a383dca2Sreyk /*
576a383dca2Sreyk * We don't have to validate "hex" because it is
577a383dca2Sreyk * guaranteed to include two hex chars followed by nul.
578a383dca2Sreyk */
579a383dca2Sreyk x = strtoul(hex, NULL, 16);
580a383dca2Sreyk *q = (char)x;
581a383dca2Sreyk p += 2;
582a383dca2Sreyk break;
583a383dca2Sreyk default:
584a383dca2Sreyk *q = *p;
585a383dca2Sreyk break;
586a383dca2Sreyk }
587a383dca2Sreyk p++;
588a383dca2Sreyk q++;
589a383dca2Sreyk }
590a383dca2Sreyk *q = '\0';
591a383dca2Sreyk
592a383dca2Sreyk return (url);
593a383dca2Sreyk }
594a383dca2Sreyk
595a383dca2Sreyk const char *
canonicalize_path(const char * input,char * path,size_t len)596c9351fd6Sreyk canonicalize_path(const char *input, char *path, size_t len)
59733cf7fc3Sreyk {
59833cf7fc3Sreyk const char *i;
59933cf7fc3Sreyk char *p, *start, *end;
60033cf7fc3Sreyk
60133cf7fc3Sreyk /* assuming input starts with '/' and is nul-terminated */
60233cf7fc3Sreyk i = input;
60333cf7fc3Sreyk p = path;
60433cf7fc3Sreyk
60533cf7fc3Sreyk if (*input != '/' || len < 3)
60633cf7fc3Sreyk return (NULL);
60733cf7fc3Sreyk
60833cf7fc3Sreyk start = p;
60933cf7fc3Sreyk end = p + (len - 1);
61033cf7fc3Sreyk
611d5b513f5Sreyk while (*i != '\0') {
612d5b513f5Sreyk /* Detect truncation */
613d5b513f5Sreyk if (p >= end)
614d5b513f5Sreyk return (NULL);
615d5b513f5Sreyk
61633cf7fc3Sreyk /* 1. check for special path elements */
61733cf7fc3Sreyk if (i[0] == '/') {
61833cf7fc3Sreyk if (i[1] == '/') {
61933cf7fc3Sreyk /* a) skip repeating '//' slashes */
62033cf7fc3Sreyk while (i[1] == '/')
62133cf7fc3Sreyk i++;
62233cf7fc3Sreyk continue;
62333cf7fc3Sreyk } else if (i[1] == '.' && i[2] == '.' &&
62433cf7fc3Sreyk (i[3] == '/' || i[3] == '\0')) {
62533cf7fc3Sreyk /* b) revert '..' to previous directory */
62633cf7fc3Sreyk i += 3;
62733cf7fc3Sreyk while (p > start && *p != '/')
62833cf7fc3Sreyk p--;
62933cf7fc3Sreyk *p = '\0';
63033cf7fc3Sreyk continue;
63133cf7fc3Sreyk } else if (i[1] == '.' &&
63233cf7fc3Sreyk (i[2] == '/' || i[2] == '\0')) {
63333cf7fc3Sreyk /* c) skip unnecessary '.' current dir */
63433cf7fc3Sreyk i += 2;
63533cf7fc3Sreyk continue;
63633cf7fc3Sreyk }
63733cf7fc3Sreyk }
63833cf7fc3Sreyk
63933cf7fc3Sreyk /* 2. copy any other characters */
64033cf7fc3Sreyk *p++ = *i;
64133cf7fc3Sreyk i++;
64233cf7fc3Sreyk }
64333cf7fc3Sreyk if (p == start)
64433cf7fc3Sreyk *p++ = '/';
64533cf7fc3Sreyk *p++ = '\0';
64633cf7fc3Sreyk
64733cf7fc3Sreyk return (path);
64833cf7fc3Sreyk }
64933cf7fc3Sreyk
65041241ca0Sreyk size_t
path_info(char * path)65141241ca0Sreyk path_info(char *path)
652ef353f36Sreyk {
65341241ca0Sreyk char *p, *start, *end, ch;
654ef353f36Sreyk struct stat st;
65541241ca0Sreyk int ret;
656ef353f36Sreyk
657ef353f36Sreyk start = path;
658ef353f36Sreyk end = start + strlen(path);
659ef353f36Sreyk
66041241ca0Sreyk for (p = end; p > start; p--) {
66141241ca0Sreyk /* Scan every path component from the end and at each '/' */
662f5d236a8Sjung if (p < end && *p != '/')
663ef353f36Sreyk continue;
66441241ca0Sreyk
66541241ca0Sreyk /* Temporarily cut the path component out */
66641241ca0Sreyk ch = *p;
667ef353f36Sreyk *p = '\0';
66841241ca0Sreyk ret = stat(path, &st);
66941241ca0Sreyk *p = ch;
67041241ca0Sreyk
67141241ca0Sreyk /* Break if the initial path component was found */
67241241ca0Sreyk if (ret == 0)
67341241ca0Sreyk break;
674ef353f36Sreyk }
675ef353f36Sreyk
67641241ca0Sreyk return (p - start);
677ef353f36Sreyk }
678ef353f36Sreyk
6797fa6cf2cSflorian char *
url_encode(const char * src)68011caa597Sreyk url_encode(const char *src)
6817fa6cf2cSflorian {
6827fa6cf2cSflorian static char hex[] = "0123456789ABCDEF";
6837fa6cf2cSflorian char *dp, *dst;
6847fa6cf2cSflorian unsigned char c;
6857fa6cf2cSflorian
6867fa6cf2cSflorian /* We need 3 times the memory if every letter is encoded. */
6877fa6cf2cSflorian if ((dst = calloc(3, strlen(src) + 1)) == NULL)
68811caa597Sreyk return (NULL);
6897fa6cf2cSflorian
6907fa6cf2cSflorian for (dp = dst; *src != 0; src++) {
6917fa6cf2cSflorian c = (unsigned char) *src;
6927fa6cf2cSflorian if (c == ' ' || c == '#' || c == '%' || c == '?' || c == '"' ||
6937fa6cf2cSflorian c == '&' || c == '<' || c <= 0x1f || c >= 0x7f) {
6947fa6cf2cSflorian *dp++ = '%';
6957fa6cf2cSflorian *dp++ = hex[c >> 4];
6967fa6cf2cSflorian *dp++ = hex[c & 0x0f];
6977fa6cf2cSflorian } else
6987fa6cf2cSflorian *dp++ = *src;
6997fa6cf2cSflorian }
7007fa6cf2cSflorian return (dst);
7017fa6cf2cSflorian }
7027fa6cf2cSflorian
7037fa6cf2cSflorian char*
escape_html(const char * src)7047fa6cf2cSflorian escape_html(const char* src)
7057fa6cf2cSflorian {
7067fa6cf2cSflorian char *dp, *dst;
7077fa6cf2cSflorian
708a41c3467Sflorian /* We need 5 times the memory if every letter is "&" */
7097fa6cf2cSflorian if ((dst = calloc(5, strlen(src) + 1)) == NULL)
7107fa6cf2cSflorian return NULL;
7117fa6cf2cSflorian
7127fa6cf2cSflorian for (dp = dst; *src != 0; src++) {
7137fa6cf2cSflorian if (*src == '<') {
7147fa6cf2cSflorian *dp++ = '&';
7157fa6cf2cSflorian *dp++ = 'l';
7167fa6cf2cSflorian *dp++ = 't';
7177fa6cf2cSflorian *dp++ = ';';
7187fa6cf2cSflorian } else if (*src == '>') {
7197fa6cf2cSflorian *dp++ = '&';
7207fa6cf2cSflorian *dp++ = 'g';
7217fa6cf2cSflorian *dp++ = 't';
7227fa6cf2cSflorian *dp++ = ';';
7237fa6cf2cSflorian } else if (*src == '&') {
7247fa6cf2cSflorian *dp++ = '&';
7257fa6cf2cSflorian *dp++ = 'a';
7267fa6cf2cSflorian *dp++ = 'm';
7277fa6cf2cSflorian *dp++ = 'p';
7287fa6cf2cSflorian *dp++ = ';';
7297fa6cf2cSflorian } else
7307fa6cf2cSflorian *dp++ = *src;
7317fa6cf2cSflorian }
7327fa6cf2cSflorian return (dst);
7337fa6cf2cSflorian }
7347fa6cf2cSflorian
735b7b6a941Sreyk void
socket_rlimit(int maxfd)736b7b6a941Sreyk socket_rlimit(int maxfd)
737b7b6a941Sreyk {
738b7b6a941Sreyk struct rlimit rl;
739b7b6a941Sreyk
740b7b6a941Sreyk if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
741a3d8d4e4Sbenno fatal("%s: failed to get resource limit", __func__);
742b7b6a941Sreyk log_debug("%s: max open files %llu", __func__, rl.rlim_max);
743b7b6a941Sreyk
744b7b6a941Sreyk /*
745b7b6a941Sreyk * Allow the maximum number of open file descriptors for this
746b7b6a941Sreyk * login class (which should be the class "daemon" by default).
747b7b6a941Sreyk */
748b7b6a941Sreyk if (maxfd == -1)
749b7b6a941Sreyk rl.rlim_cur = rl.rlim_max;
750b7b6a941Sreyk else
751b9fc9a72Sderaadt rl.rlim_cur = MAXIMUM(rl.rlim_max, (rlim_t)maxfd);
752b7b6a941Sreyk if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
753a3d8d4e4Sbenno fatal("%s: failed to set resource limit", __func__);
754b7b6a941Sreyk }
755b7b6a941Sreyk
756b7b6a941Sreyk char *
evbuffer_getline(struct evbuffer * evb)75757c60317Sreyk evbuffer_getline(struct evbuffer *evb)
75857c60317Sreyk {
7594703e0faSreyk uint8_t *ptr = EVBUFFER_DATA(evb);
76057c60317Sreyk size_t len = EVBUFFER_LENGTH(evb);
76157c60317Sreyk char *str;
7629660c10cStedu size_t i;
76357c60317Sreyk
76457c60317Sreyk /* Safe version of evbuffer_readline() */
76557c60317Sreyk if ((str = get_string(ptr, len)) == NULL)
76657c60317Sreyk return (NULL);
76757c60317Sreyk
76857c60317Sreyk for (i = 0; str[i] != '\0'; i++) {
76957c60317Sreyk if (str[i] == '\r' || str[i] == '\n')
77057c60317Sreyk break;
77157c60317Sreyk }
77257c60317Sreyk
77357c60317Sreyk if (i == len) {
77457c60317Sreyk free(str);
77557c60317Sreyk return (NULL);
77657c60317Sreyk }
77757c60317Sreyk
77857c60317Sreyk str[i] = '\0';
77957c60317Sreyk
78057c60317Sreyk if ((i + 1) < len) {
78157c60317Sreyk if (ptr[i] == '\r' && ptr[i + 1] == '\n')
78257c60317Sreyk i++;
78357c60317Sreyk }
78457c60317Sreyk
78557c60317Sreyk evbuffer_drain(evb, ++i);
78657c60317Sreyk
78757c60317Sreyk return (str);
78857c60317Sreyk }
78957c60317Sreyk
79057c60317Sreyk char *
get_string(uint8_t * ptr,size_t len)7914703e0faSreyk get_string(uint8_t *ptr, size_t len)
792b7b6a941Sreyk {
793b7b6a941Sreyk size_t i;
794b7b6a941Sreyk
795b7b6a941Sreyk for (i = 0; i < len; i++)
7960c6814e1Sreyk if (!(isprint((unsigned char)ptr[i]) ||
7970c6814e1Sreyk isspace((unsigned char)ptr[i])))
798b7b6a941Sreyk break;
799b7b6a941Sreyk
800b52b3078Smmcc return strndup(ptr, i);
801b7b6a941Sreyk }
802b7b6a941Sreyk
803b7b6a941Sreyk void *
get_data(uint8_t * ptr,size_t len)8044703e0faSreyk get_data(uint8_t *ptr, size_t len)
805b7b6a941Sreyk {
8064703e0faSreyk uint8_t *data;
807b7b6a941Sreyk
808b52b3078Smmcc if ((data = malloc(len)) == NULL)
809b7b6a941Sreyk return (NULL);
810b7b6a941Sreyk memcpy(data, ptr, len);
811b7b6a941Sreyk
812b7b6a941Sreyk return (data);
813b7b6a941Sreyk }
814b7b6a941Sreyk
815b7b6a941Sreyk int
sockaddr_cmp(struct sockaddr * a,struct sockaddr * b,int prefixlen)816d9bba0abSreyk sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen)
817d9bba0abSreyk {
818d9bba0abSreyk struct sockaddr_in *a4, *b4;
819d9bba0abSreyk struct sockaddr_in6 *a6, *b6;
8204703e0faSreyk uint32_t av[4], bv[4], mv[4];
821d9bba0abSreyk
822d9bba0abSreyk if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC)
823d9bba0abSreyk return (0);
824d9bba0abSreyk else if (a->sa_family > b->sa_family)
825d9bba0abSreyk return (1);
826d9bba0abSreyk else if (a->sa_family < b->sa_family)
827d9bba0abSreyk return (-1);
828d9bba0abSreyk
829d9bba0abSreyk if (prefixlen == -1)
830d9bba0abSreyk memset(&mv, 0xff, sizeof(mv));
831d9bba0abSreyk
832d9bba0abSreyk switch (a->sa_family) {
833d9bba0abSreyk case AF_INET:
834d9bba0abSreyk a4 = (struct sockaddr_in *)a;
835d9bba0abSreyk b4 = (struct sockaddr_in *)b;
836d9bba0abSreyk
837d9bba0abSreyk av[0] = a4->sin_addr.s_addr;
838d9bba0abSreyk bv[0] = b4->sin_addr.s_addr;
839d9bba0abSreyk if (prefixlen != -1)
840d9bba0abSreyk mv[0] = prefixlen2mask(prefixlen);
841d9bba0abSreyk
842d9bba0abSreyk if ((av[0] & mv[0]) > (bv[0] & mv[0]))
843d9bba0abSreyk return (1);
844d9bba0abSreyk if ((av[0] & mv[0]) < (bv[0] & mv[0]))
845d9bba0abSreyk return (-1);
846d9bba0abSreyk break;
847d9bba0abSreyk case AF_INET6:
848d9bba0abSreyk a6 = (struct sockaddr_in6 *)a;
849d9bba0abSreyk b6 = (struct sockaddr_in6 *)b;
850d9bba0abSreyk
851d9bba0abSreyk memcpy(&av, &a6->sin6_addr.s6_addr, 16);
852d9bba0abSreyk memcpy(&bv, &b6->sin6_addr.s6_addr, 16);
853d9bba0abSreyk if (prefixlen != -1)
854d9bba0abSreyk prefixlen2mask6(prefixlen, mv);
855d9bba0abSreyk
856d9bba0abSreyk if ((av[3] & mv[3]) > (bv[3] & mv[3]))
857d9bba0abSreyk return (1);
858d9bba0abSreyk if ((av[3] & mv[3]) < (bv[3] & mv[3]))
859d9bba0abSreyk return (-1);
860d9bba0abSreyk if ((av[2] & mv[2]) > (bv[2] & mv[2]))
861d9bba0abSreyk return (1);
862d9bba0abSreyk if ((av[2] & mv[2]) < (bv[2] & mv[2]))
863d9bba0abSreyk return (-1);
864d9bba0abSreyk if ((av[1] & mv[1]) > (bv[1] & mv[1]))
865d9bba0abSreyk return (1);
866d9bba0abSreyk if ((av[1] & mv[1]) < (bv[1] & mv[1]))
867d9bba0abSreyk return (-1);
868d9bba0abSreyk if ((av[0] & mv[0]) > (bv[0] & mv[0]))
869d9bba0abSreyk return (1);
870d9bba0abSreyk if ((av[0] & mv[0]) < (bv[0] & mv[0]))
871d9bba0abSreyk return (-1);
872d9bba0abSreyk break;
873d9bba0abSreyk }
874d9bba0abSreyk
875d9bba0abSreyk return (0);
876d9bba0abSreyk }
877d9bba0abSreyk
8784703e0faSreyk uint32_t
prefixlen2mask(uint8_t prefixlen)8794703e0faSreyk prefixlen2mask(uint8_t prefixlen)
880d9bba0abSreyk {
881d9bba0abSreyk if (prefixlen == 0)
882d9bba0abSreyk return (0);
883d9bba0abSreyk
884d9bba0abSreyk if (prefixlen > 32)
885d9bba0abSreyk prefixlen = 32;
886d9bba0abSreyk
887d9bba0abSreyk return (htonl(0xffffffff << (32 - prefixlen)));
888d9bba0abSreyk }
889d9bba0abSreyk
890d9bba0abSreyk struct in6_addr *
prefixlen2mask6(uint8_t prefixlen,uint32_t * mask)8914703e0faSreyk prefixlen2mask6(uint8_t prefixlen, uint32_t *mask)
892d9bba0abSreyk {
893d9bba0abSreyk static struct in6_addr s6;
894d9bba0abSreyk int i;
895d9bba0abSreyk
896d9bba0abSreyk if (prefixlen > 128)
897d9bba0abSreyk prefixlen = 128;
898d9bba0abSreyk
899fe9c457cSreyk memset(&s6, 0, sizeof(s6));
900d9bba0abSreyk for (i = 0; i < prefixlen / 8; i++)
901d9bba0abSreyk s6.s6_addr[i] = 0xff;
902d9bba0abSreyk i = prefixlen % 8;
903d9bba0abSreyk if (i)
904d9bba0abSreyk s6.s6_addr[prefixlen / 8] = 0xff00 >> i;
905d9bba0abSreyk
906d9bba0abSreyk memcpy(mask, &s6, sizeof(s6));
907d9bba0abSreyk
908d9bba0abSreyk return (&s6);
909d9bba0abSreyk }
910d9bba0abSreyk
911d9bba0abSreyk int
accept_reserve(int sockfd,struct sockaddr * addr,socklen_t * addrlen,int reserve,volatile int * counter)912b7b6a941Sreyk accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
913b7b6a941Sreyk int reserve, volatile int *counter)
914b7b6a941Sreyk {
915b7b6a941Sreyk int ret;
916b7b6a941Sreyk if (getdtablecount() + reserve +
917b7b6a941Sreyk *counter >= getdtablesize()) {
918b7b6a941Sreyk errno = EMFILE;
919b7b6a941Sreyk return (-1);
920b7b6a941Sreyk }
921b7b6a941Sreyk
922838637bcSreyk if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK)) > -1) {
923b7b6a941Sreyk (*counter)++;
924b7b6a941Sreyk DPRINTF("%s: inflight incremented, now %d",__func__, *counter);
925b7b6a941Sreyk }
926b7b6a941Sreyk return (ret);
927b7b6a941Sreyk }
928b7b6a941Sreyk
929b7b6a941Sreyk struct kv *
kv_add(struct kvtree * keys,char * key,char * value)930b7b6a941Sreyk kv_add(struct kvtree *keys, char *key, char *value)
931b7b6a941Sreyk {
932b7b6a941Sreyk struct kv *kv, *oldkv;
933b7b6a941Sreyk
934b7b6a941Sreyk if (key == NULL)
935b7b6a941Sreyk return (NULL);
936b7b6a941Sreyk if ((kv = calloc(1, sizeof(*kv))) == NULL)
937b7b6a941Sreyk return (NULL);
938b7b6a941Sreyk if ((kv->kv_key = strdup(key)) == NULL) {
939b7b6a941Sreyk free(kv);
940b7b6a941Sreyk return (NULL);
941b7b6a941Sreyk }
942b7b6a941Sreyk if (value != NULL &&
943b7b6a941Sreyk (kv->kv_value = strdup(value)) == NULL) {
944b7b6a941Sreyk free(kv->kv_key);
945b7b6a941Sreyk free(kv);
946b7b6a941Sreyk return (NULL);
947b7b6a941Sreyk }
948b7b6a941Sreyk TAILQ_INIT(&kv->kv_children);
949b7b6a941Sreyk
950b7b6a941Sreyk if ((oldkv = RB_INSERT(kvtree, keys, kv)) != NULL) {
951b7b6a941Sreyk TAILQ_INSERT_TAIL(&oldkv->kv_children, kv, kv_entry);
952b7b6a941Sreyk kv->kv_parent = oldkv;
953b7b6a941Sreyk }
954b7b6a941Sreyk
955b7b6a941Sreyk return (kv);
956b7b6a941Sreyk }
957b7b6a941Sreyk
958b7b6a941Sreyk int
kv_set(struct kv * kv,char * fmt,...)959b7b6a941Sreyk kv_set(struct kv *kv, char *fmt, ...)
960b7b6a941Sreyk {
961b7b6a941Sreyk va_list ap;
962b7b6a941Sreyk char *value = NULL;
963b7b6a941Sreyk struct kv *ckv;
9646b535b52Sjung int ret;
965b7b6a941Sreyk
966b7b6a941Sreyk va_start(ap, fmt);
9676b535b52Sjung ret = vasprintf(&value, fmt, ap);
968b7b6a941Sreyk va_end(ap);
9696b535b52Sjung if (ret == -1)
9706b535b52Sjung return (-1);
971b7b6a941Sreyk
972b7b6a941Sreyk /* Remove all children */
973b7b6a941Sreyk while ((ckv = TAILQ_FIRST(&kv->kv_children)) != NULL) {
974b7b6a941Sreyk TAILQ_REMOVE(&kv->kv_children, ckv, kv_entry);
975b7b6a941Sreyk kv_free(ckv);
976b7b6a941Sreyk free(ckv);
977b7b6a941Sreyk }
978b7b6a941Sreyk
979b7b6a941Sreyk /* Set the new value */
980b7b6a941Sreyk free(kv->kv_value);
981b7b6a941Sreyk kv->kv_value = value;
982b7b6a941Sreyk
983b7b6a941Sreyk return (0);
984b7b6a941Sreyk }
985b7b6a941Sreyk
986b7b6a941Sreyk int
kv_setkey(struct kv * kv,char * fmt,...)987b7b6a941Sreyk kv_setkey(struct kv *kv, char *fmt, ...)
988b7b6a941Sreyk {
989b7b6a941Sreyk va_list ap;
990b7b6a941Sreyk char *key = NULL;
9916b535b52Sjung int ret;
992b7b6a941Sreyk
993b7b6a941Sreyk va_start(ap, fmt);
9946b535b52Sjung ret = vasprintf(&key, fmt, ap);
995b7b6a941Sreyk va_end(ap);
9966b535b52Sjung if (ret == -1)
9976b535b52Sjung return (-1);
998b7b6a941Sreyk
999b7b6a941Sreyk free(kv->kv_key);
1000b7b6a941Sreyk kv->kv_key = key;
1001b7b6a941Sreyk
1002b7b6a941Sreyk return (0);
1003b7b6a941Sreyk }
1004b7b6a941Sreyk
1005b7b6a941Sreyk void
kv_delete(struct kvtree * keys,struct kv * kv)1006b7b6a941Sreyk kv_delete(struct kvtree *keys, struct kv *kv)
1007b7b6a941Sreyk {
1008b7b6a941Sreyk struct kv *ckv;
1009b7b6a941Sreyk
1010b7b6a941Sreyk RB_REMOVE(kvtree, keys, kv);
1011b7b6a941Sreyk
1012b7b6a941Sreyk /* Remove all children */
1013b7b6a941Sreyk while ((ckv = TAILQ_FIRST(&kv->kv_children)) != NULL) {
1014b7b6a941Sreyk TAILQ_REMOVE(&kv->kv_children, ckv, kv_entry);
1015b7b6a941Sreyk kv_free(ckv);
1016b7b6a941Sreyk free(ckv);
1017b7b6a941Sreyk }
1018b7b6a941Sreyk
1019b7b6a941Sreyk kv_free(kv);
1020b7b6a941Sreyk free(kv);
1021b7b6a941Sreyk }
1022b7b6a941Sreyk
1023b7b6a941Sreyk struct kv *
kv_extend(struct kvtree * keys,struct kv * kv,char * value)1024b7b6a941Sreyk kv_extend(struct kvtree *keys, struct kv *kv, char *value)
1025b7b6a941Sreyk {
1026b7b6a941Sreyk char *newvalue;
1027b7b6a941Sreyk
1028b7b6a941Sreyk if (kv == NULL) {
1029b7b6a941Sreyk return (NULL);
1030b7b6a941Sreyk } else if (kv->kv_value != NULL) {
1031b7b6a941Sreyk if (asprintf(&newvalue, "%s%s", kv->kv_value, value) == -1)
1032b7b6a941Sreyk return (NULL);
1033b7b6a941Sreyk
1034b7b6a941Sreyk free(kv->kv_value);
1035b7b6a941Sreyk kv->kv_value = newvalue;
1036b7b6a941Sreyk } else if ((kv->kv_value = strdup(value)) == NULL)
1037b7b6a941Sreyk return (NULL);
1038b7b6a941Sreyk
1039b7b6a941Sreyk return (kv);
1040b7b6a941Sreyk }
1041b7b6a941Sreyk
1042b7b6a941Sreyk void
kv_purge(struct kvtree * keys)1043b7b6a941Sreyk kv_purge(struct kvtree *keys)
1044b7b6a941Sreyk {
1045b7b6a941Sreyk struct kv *kv;
1046b7b6a941Sreyk
1047b7b6a941Sreyk while ((kv = RB_MIN(kvtree, keys)) != NULL)
1048b7b6a941Sreyk kv_delete(keys, kv);
1049b7b6a941Sreyk }
1050b7b6a941Sreyk
1051b7b6a941Sreyk void
kv_free(struct kv * kv)1052b7b6a941Sreyk kv_free(struct kv *kv)
1053b7b6a941Sreyk {
1054b7b6a941Sreyk free(kv->kv_key);
1055b7b6a941Sreyk kv->kv_key = NULL;
1056b7b6a941Sreyk free(kv->kv_value);
1057b7b6a941Sreyk kv->kv_value = NULL;
1058b7b6a941Sreyk memset(kv, 0, sizeof(*kv));
1059b7b6a941Sreyk }
1060b7b6a941Sreyk
1061b7b6a941Sreyk struct kv *
kv_find(struct kvtree * keys,struct kv * kv)1062b7b6a941Sreyk kv_find(struct kvtree *keys, struct kv *kv)
1063b7b6a941Sreyk {
106422531200Sflorian return (RB_FIND(kvtree, keys, kv));
1065b7b6a941Sreyk }
1066b7b6a941Sreyk
1067b7b6a941Sreyk int
kv_cmp(struct kv * a,struct kv * b)1068b7b6a941Sreyk kv_cmp(struct kv *a, struct kv *b)
1069b7b6a941Sreyk {
1070b7b6a941Sreyk return (strcasecmp(a->kv_key, b->kv_key));
1071b7b6a941Sreyk }
1072b7b6a941Sreyk
1073b7b6a941Sreyk RB_GENERATE(kvtree, kv, kv_node, kv_cmp);
10749b9ff8ecSreyk
10759b9ff8ecSreyk struct media_type *
media_add(struct mediatypes * types,struct media_type * media)10769b9ff8ecSreyk media_add(struct mediatypes *types, struct media_type *media)
10779b9ff8ecSreyk {
10789b9ff8ecSreyk struct media_type *entry;
10799b9ff8ecSreyk
10809b9ff8ecSreyk if ((entry = RB_FIND(mediatypes, types, media)) != NULL) {
10812c434b9bSbenno log_debug("%s: entry overwritten for \"%s\"", __func__,
10829b9ff8ecSreyk media->media_name);
10832c434b9bSbenno media_delete(types, entry);
10849b9ff8ecSreyk }
10859b9ff8ecSreyk
10869b9ff8ecSreyk if ((entry = malloc(sizeof(*media))) == NULL)
10879b9ff8ecSreyk return (NULL);
10889b9ff8ecSreyk
10899b9ff8ecSreyk memcpy(entry, media, sizeof(*entry));
1090ff70ca31Sreyk if (media->media_encoding != NULL &&
1091ff70ca31Sreyk (entry->media_encoding = strdup(media->media_encoding)) == NULL) {
1092ff70ca31Sreyk free(entry);
1093ff70ca31Sreyk return (NULL);
1094ff70ca31Sreyk }
10959b9ff8ecSreyk RB_INSERT(mediatypes, types, entry);
10969b9ff8ecSreyk
10979b9ff8ecSreyk return (entry);
10989b9ff8ecSreyk }
10999b9ff8ecSreyk
11009b9ff8ecSreyk void
media_delete(struct mediatypes * types,struct media_type * media)11019b9ff8ecSreyk media_delete(struct mediatypes *types, struct media_type *media)
11029b9ff8ecSreyk {
11039b9ff8ecSreyk RB_REMOVE(mediatypes, types, media);
11043fe25232Sreyk
11059b9ff8ecSreyk free(media->media_encoding);
11069b9ff8ecSreyk free(media);
11079b9ff8ecSreyk }
11089b9ff8ecSreyk
11099b9ff8ecSreyk void
media_purge(struct mediatypes * types)11109b9ff8ecSreyk media_purge(struct mediatypes *types)
11119b9ff8ecSreyk {
11129b9ff8ecSreyk struct media_type *media;
11139b9ff8ecSreyk
11149b9ff8ecSreyk while ((media = RB_MIN(mediatypes, types)) != NULL)
11159b9ff8ecSreyk media_delete(types, media);
11169b9ff8ecSreyk }
11179b9ff8ecSreyk
11189b9ff8ecSreyk struct media_type *
media_find(struct mediatypes * types,const char * file)1119d24f6b1eSreyk media_find(struct mediatypes *types, const char *file)
11209b9ff8ecSreyk {
11219b9ff8ecSreyk struct media_type *match, media;
11229b9ff8ecSreyk char *p;
11239b9ff8ecSreyk
11247d515748Sschwarze /* Last component of the file name */
11257d515748Sschwarze p = strchr(file, '\0');
11267d515748Sschwarze while (p > file && p[-1] != '.' && p[-1] != '/')
11277d515748Sschwarze p--;
11287d515748Sschwarze if (*p == '\0')
11299b9ff8ecSreyk return (NULL);
11307d515748Sschwarze
11319b9ff8ecSreyk if (strlcpy(media.media_name, p,
11329b9ff8ecSreyk sizeof(media.media_name)) >=
11339b9ff8ecSreyk sizeof(media.media_name)) {
11349b9ff8ecSreyk return (NULL);
11359b9ff8ecSreyk }
11369b9ff8ecSreyk
11379b9ff8ecSreyk /* Find media type by extension name */
11389b9ff8ecSreyk match = RB_FIND(mediatypes, types, &media);
11399b9ff8ecSreyk
11409b9ff8ecSreyk return (match);
11419b9ff8ecSreyk }
11429b9ff8ecSreyk
1143d24f6b1eSreyk struct media_type *
media_find_config(struct httpd * env,struct server_config * srv_conf,const char * file)1144d24f6b1eSreyk media_find_config(struct httpd *env, struct server_config *srv_conf,
1145d24f6b1eSreyk const char *file)
1146d24f6b1eSreyk {
1147d24f6b1eSreyk struct media_type *match;
1148d24f6b1eSreyk
1149d24f6b1eSreyk if ((match = media_find(env->sc_mediatypes, file)) != NULL)
1150d24f6b1eSreyk return (match);
1151d24f6b1eSreyk else if (srv_conf->flags & SRVFLAG_DEFAULT_TYPE)
1152d24f6b1eSreyk return (&srv_conf->default_type);
1153d24f6b1eSreyk
1154d24f6b1eSreyk /* fallback to the global default type */
1155d24f6b1eSreyk return (&env->sc_default_type);
1156d24f6b1eSreyk }
1157d24f6b1eSreyk
11589b9ff8ecSreyk int
media_cmp(struct media_type * a,struct media_type * b)11599b9ff8ecSreyk media_cmp(struct media_type *a, struct media_type *b)
11609b9ff8ecSreyk {
11619b9ff8ecSreyk return (strcasecmp(a->media_name, b->media_name));
11629b9ff8ecSreyk }
11639b9ff8ecSreyk
11649b9ff8ecSreyk RB_GENERATE(mediatypes, media_type, media_entry, media_cmp);
1165602531d9Sreyk
1166602531d9Sreyk struct auth *
auth_add(struct serverauth * serverauth,struct auth * auth)1167602531d9Sreyk auth_add(struct serverauth *serverauth, struct auth *auth)
1168602531d9Sreyk {
1169602531d9Sreyk struct auth *entry;
1170602531d9Sreyk
1171602531d9Sreyk TAILQ_FOREACH(entry, serverauth, auth_entry) {
1172602531d9Sreyk if (strcmp(entry->auth_htpasswd, auth->auth_htpasswd) == 0)
1173602531d9Sreyk return (entry);
1174602531d9Sreyk }
1175602531d9Sreyk
1176602531d9Sreyk if ((entry = calloc(1, sizeof(*entry))) == NULL)
1177602531d9Sreyk return (NULL);
1178602531d9Sreyk
1179602531d9Sreyk memcpy(entry, auth, sizeof(*entry));
1180602531d9Sreyk
1181602531d9Sreyk TAILQ_INSERT_TAIL(serverauth, entry, auth_entry);
1182602531d9Sreyk
1183602531d9Sreyk return (entry);
1184602531d9Sreyk }
1185602531d9Sreyk
1186602531d9Sreyk struct auth *
auth_byid(struct serverauth * serverauth,uint32_t id)11874703e0faSreyk auth_byid(struct serverauth *serverauth, uint32_t id)
1188602531d9Sreyk {
1189602531d9Sreyk struct auth *auth;
1190602531d9Sreyk
1191602531d9Sreyk TAILQ_FOREACH(auth, serverauth, auth_entry) {
1192602531d9Sreyk if (auth->auth_id == id)
1193602531d9Sreyk return (auth);
1194602531d9Sreyk }
1195602531d9Sreyk
1196602531d9Sreyk return (NULL);
1197602531d9Sreyk }
1198602531d9Sreyk
1199602531d9Sreyk void
auth_free(struct serverauth * serverauth,struct auth * auth)1200602531d9Sreyk auth_free(struct serverauth *serverauth, struct auth *auth)
1201602531d9Sreyk {
1202602531d9Sreyk TAILQ_REMOVE(serverauth, auth, auth_entry);
1203602531d9Sreyk }
120408141198Sreyk
120508141198Sreyk
120608141198Sreyk const char *
print_host(struct sockaddr_storage * ss,char * buf,size_t len)120708141198Sreyk print_host(struct sockaddr_storage *ss, char *buf, size_t len)
120808141198Sreyk {
120908141198Sreyk if (getnameinfo((struct sockaddr *)ss, ss->ss_len,
121008141198Sreyk buf, len, NULL, 0, NI_NUMERICHOST) != 0) {
121108141198Sreyk buf[0] = '\0';
121208141198Sreyk return (NULL);
121308141198Sreyk }
121408141198Sreyk return (buf);
121508141198Sreyk }
121608141198Sreyk
121708141198Sreyk const char *
printb_flags(const uint32_t v,const char * bits)121808141198Sreyk printb_flags(const uint32_t v, const char *bits)
121908141198Sreyk {
122008141198Sreyk static char buf[2][BUFSIZ];
122108141198Sreyk static int idx = 0;
122208141198Sreyk int i, any = 0;
122308141198Sreyk char c, *p, *r;
122408141198Sreyk
122508141198Sreyk p = r = buf[++idx % 2];
122608141198Sreyk memset(p, 0, BUFSIZ);
122708141198Sreyk
122808141198Sreyk if (bits) {
122908141198Sreyk bits++;
123008141198Sreyk while ((i = *bits++)) {
123108141198Sreyk if (v & (1 << (i - 1))) {
123208141198Sreyk if (any) {
123308141198Sreyk *p++ = ',';
123408141198Sreyk *p++ = ' ';
123508141198Sreyk }
123608141198Sreyk any = 1;
123708141198Sreyk for (; (c = *bits) > 32; bits++) {
123808141198Sreyk if (c == '_')
123908141198Sreyk *p++ = ' ';
124008141198Sreyk else
124108141198Sreyk *p++ =
124208141198Sreyk tolower((unsigned char)c);
124308141198Sreyk }
124408141198Sreyk } else
124508141198Sreyk for (; *bits > 32; bits++)
124608141198Sreyk ;
124708141198Sreyk }
124808141198Sreyk }
124908141198Sreyk
125008141198Sreyk return (r);
125108141198Sreyk }
125208141198Sreyk
125308141198Sreyk void
getmonotime(struct timeval * tv)125408141198Sreyk getmonotime(struct timeval *tv)
125508141198Sreyk {
125608141198Sreyk struct timespec ts;
125708141198Sreyk
125808141198Sreyk if (clock_gettime(CLOCK_MONOTONIC, &ts))
125908141198Sreyk fatal("clock_gettime");
126008141198Sreyk
126108141198Sreyk TIMESPEC_TO_TIMEVAL(tv, &ts);
126208141198Sreyk }
1263