xref: /openbsd-src/usr.sbin/httpd/httpd.c (revision 4c131d564adf044b93f7799413ffca8765a6d798)
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