| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: httpd.c,v 1.67 2017/05/28 10:37:26 benno Exp $ */  | 
    ||
2  | 
    |||
3  | 
    /*  | 
    ||
4  | 
    * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>  | 
    ||
5  | 
    *  | 
    ||
6  | 
    * Permission to use, copy, modify, and distribute this software for any  | 
    ||
7  | 
    * purpose with or without fee is hereby granted, provided that the above  | 
    ||
8  | 
    * copyright notice and this permission notice appear in all copies.  | 
    ||
9  | 
    *  | 
    ||
10  | 
    * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES  | 
    ||
11  | 
    * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF  | 
    ||
12  | 
    * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  | 
    ||
13  | 
    * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES  | 
    ||
14  | 
    * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  | 
    ||
15  | 
    * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  | 
    ||
16  | 
    * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  | 
    ||
17  | 
    */  | 
    ||
18  | 
    |||
19  | 
    #include <sys/types.h>  | 
    ||
20  | 
    #include <sys/queue.h>  | 
    ||
21  | 
    #include <sys/socket.h>  | 
    ||
22  | 
    #include <sys/stat.h>  | 
    ||
23  | 
    #include <sys/resource.h>  | 
    ||
24  | 
    |||
25  | 
    #include <netinet/in.h>  | 
    ||
26  | 
    #include <arpa/inet.h>  | 
    ||
27  | 
    |||
28  | 
    #include <stdio.h>  | 
    ||
29  | 
    #include <stdlib.h>  | 
    ||
30  | 
    #include <stdarg.h>  | 
    ||
31  | 
    #include <string.h>  | 
    ||
32  | 
    #include <signal.h>  | 
    ||
33  | 
    #include <getopt.h>  | 
    ||
34  | 
    #include <netdb.h>  | 
    ||
35  | 
    #include <fnmatch.h>  | 
    ||
36  | 
    #include <err.h>  | 
    ||
37  | 
    #include <errno.h>  | 
    ||
38  | 
    #include <event.h>  | 
    ||
39  | 
    #include <syslog.h>  | 
    ||
40  | 
    #include <unistd.h>  | 
    ||
41  | 
    #include <ctype.h>  | 
    ||
42  | 
    #include <pwd.h>  | 
    ||
43  | 
    |||
44  | 
    #include "httpd.h"  | 
    ||
45  | 
    |||
46  | 
    #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))  | 
    ||
47  | 
    |||
48  | 
    __dead void usage(void);  | 
    ||
49  | 
    |||
50  | 
    int parent_configure(struct httpd *);  | 
    ||
51  | 
    void parent_configure_done(struct httpd *);  | 
    ||
52  | 
    void parent_reload(struct httpd *, unsigned int, const char *);  | 
    ||
53  | 
    void parent_reopen(struct httpd *);  | 
    ||
54  | 
    void parent_sig_handler(int, short, void *);  | 
    ||
55  | 
    void parent_shutdown(struct httpd *);  | 
    ||
56  | 
    int parent_dispatch_server(int, struct privsep_proc *,  | 
    ||
57  | 
    struct imsg *);  | 
    ||
58  | 
    int parent_dispatch_logger(int, struct privsep_proc *,  | 
    ||
59  | 
    struct imsg *);  | 
    ||
60  | 
    void parent_tls_ticket_rekey_start(struct server *);  | 
    ||
61  | 
    void parent_tls_ticket_rekey(int, short, void *);  | 
    ||
62  | 
    |||
63  | 
    struct httpd *httpd_env;  | 
    ||
64  | 
    |||
65  | 
    static struct privsep_proc procs[] = { | 
    ||
66  | 
    	{ "server",	PROC_SERVER, parent_dispatch_server, server }, | 
    ||
67  | 
    	{ "logger",	PROC_LOGGER, parent_dispatch_logger, logger } | 
    ||
68  | 
    };  | 
    ||
69  | 
    |||
70  | 
    void  | 
    ||
71  | 
    parent_sig_handler(int sig, short event, void *arg)  | 
    ||
72  | 
    { | 
    ||
73  | 
    144  | 
    struct privsep *ps = arg;  | 
    |
74  | 
    |||
75  | 
    ✗✓✗✗ ✗✗  | 
    72  | 
    	switch (sig) { | 
    
76  | 
    case SIGTERM:  | 
    ||
77  | 
    case SIGINT:  | 
    ||
78  | 
    72  | 
    parent_shutdown(ps->ps_env);  | 
    |
79  | 
    72  | 
    break;  | 
    |
80  | 
    case SIGHUP:  | 
    ||
81  | 
    		log_info("%s: reload requested with SIGHUP", __func__); | 
    ||
82  | 
    |||
83  | 
    /*  | 
    ||
84  | 
    * This is safe because libevent uses async signal handlers  | 
    ||
85  | 
    * that run in the event loop and not in signal context.  | 
    ||
86  | 
    */  | 
    ||
87  | 
    parent_reload(ps->ps_env, CONFIG_RELOAD, NULL);  | 
    ||
88  | 
    break;  | 
    ||
89  | 
    case SIGPIPE:  | 
    ||
90  | 
    /* ignore */  | 
    ||
91  | 
    break;  | 
    ||
92  | 
    case SIGUSR1:  | 
    ||
93  | 
    		log_info("%s: reopen requested with SIGUSR1", __func__); | 
    ||
94  | 
    |||
95  | 
    parent_reopen(ps->ps_env);  | 
    ||
96  | 
    break;  | 
    ||
97  | 
    default:  | 
    ||
98  | 
    		fatalx("unexpected signal"); | 
    ||
99  | 
    }  | 
    ||
100  | 
    }  | 
    ||
101  | 
    |||
102  | 
    __dead void  | 
    ||
103  | 
    usage(void)  | 
    ||
104  | 
    { | 
    ||
105  | 
    extern char *__progname;  | 
    ||
106  | 
    |||
107  | 
    fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",  | 
    ||
108  | 
    __progname);  | 
    ||
109  | 
    exit(1);  | 
    ||
110  | 
    }  | 
    ||
111  | 
    |||
112  | 
    int  | 
    ||
113  | 
    main(int argc, char *argv[])  | 
    ||
114  | 
    { | 
    ||
115  | 
    int c;  | 
    ||
116  | 
    unsigned int proc;  | 
    ||
117  | 
    int debug = 0, verbose = 0;  | 
    ||
118  | 
    uint32_t opts = 0;  | 
    ||
119  | 
    struct httpd *env;  | 
    ||
120  | 
    struct privsep *ps;  | 
    ||
121  | 
    const char *conffile = CONF_FILE;  | 
    ||
122  | 
    enum privsep_procid proc_id = PROC_PARENT;  | 
    ||
123  | 
    int proc_instance = 0;  | 
    ||
124  | 
    144  | 
    const char *errp, *title = NULL;  | 
    |
125  | 
    int argc0 = argc;  | 
    ||
126  | 
    |||
127  | 
    ✓✓ | 432  | 
    	while ((c = getopt(argc, argv, "dD:nf:I:P:v")) != -1) { | 
    
128  | 
    ✓✗✗✓ ✓✗✗✗  | 
    288  | 
    		switch (c) { | 
    
129  | 
    case 'd':  | 
    ||
130  | 
    debug = 2;  | 
    ||
131  | 
    72  | 
    break;  | 
    |
132  | 
    case 'D':  | 
    ||
133  | 
    if (cmdline_symset(optarg) < 0)  | 
    ||
134  | 
    				log_warnx("could not parse macro definition %s", | 
    ||
135  | 
    optarg);  | 
    ||
136  | 
    break;  | 
    ||
137  | 
    case 'n':  | 
    ||
138  | 
    debug = 2;  | 
    ||
139  | 
    opts |= HTTPD_OPT_NOACTION;  | 
    ||
140  | 
    break;  | 
    ||
141  | 
    case 'f':  | 
    ||
142  | 
    72  | 
    conffile = optarg;  | 
    |
143  | 
    72  | 
    break;  | 
    |
144  | 
    case 'v':  | 
    ||
145  | 
    144  | 
    verbose++;  | 
    |
146  | 
    144  | 
    opts |= HTTPD_OPT_VERBOSE;  | 
    |
147  | 
    144  | 
    break;  | 
    |
148  | 
    case 'P':  | 
    ||
149  | 
    title = optarg;  | 
    ||
150  | 
    proc_id = proc_getid(procs, nitems(procs), title);  | 
    ||
151  | 
    if (proc_id == PROC_MAX)  | 
    ||
152  | 
    				fatalx("invalid process name"); | 
    ||
153  | 
    break;  | 
    ||
154  | 
    case 'I':  | 
    ||
155  | 
    proc_instance = strtonum(optarg, 0,  | 
    ||
156  | 
    PROC_MAX_INSTANCES, &errp);  | 
    ||
157  | 
    if (errp)  | 
    ||
158  | 
    				fatalx("invalid process instance"); | 
    ||
159  | 
    break;  | 
    ||
160  | 
    default:  | 
    ||
161  | 
    usage();  | 
    ||
162  | 
    }  | 
    ||
163  | 
    }  | 
    ||
164  | 
    |||
165  | 
    /* log to stderr until daemonized */  | 
    ||
166  | 
    72  | 
    log_init(debug ? debug : 1, LOG_DAEMON);  | 
    |
167  | 
    |||
168  | 
    72  | 
    argc -= optind;  | 
    |
169  | 
    ✗✓ | 72  | 
    if (argc > 0)  | 
    
170  | 
    usage();  | 
    ||
171  | 
    |||
172  | 
    ✓✗✗✓ | 
    144  | 
    if ((env = calloc(1, sizeof(*env))) == NULL ||  | 
    
173  | 
    72  | 
    (ps = calloc(1, sizeof(*ps))) == NULL)  | 
    |
174  | 
    exit(1);  | 
    ||
175  | 
    |||
176  | 
    72  | 
    httpd_env = env;  | 
    |
177  | 
    72  | 
    env->sc_ps = ps;  | 
    |
178  | 
    72  | 
    ps->ps_env = env;  | 
    |
179  | 
    72  | 
    TAILQ_INIT(&ps->ps_rcsocks);  | 
    |
180  | 
    72  | 
    env->sc_conffile = conffile;  | 
    |
181  | 
    72  | 
    env->sc_opts = opts;  | 
    |
182  | 
    |||
183  | 
    ✗✓ | 72  | 
    if (parse_config(env->sc_conffile, env) == -1)  | 
    
184  | 
    exit(1);  | 
    ||
185  | 
    |||
186  | 
    ✗✓ | 72  | 
    if (geteuid())  | 
    
187  | 
    errx(1, "need root privileges");  | 
    ||
188  | 
    |||
189  | 
    ✗✓ | 72  | 
    if ((ps->ps_pw = getpwnam(HTTPD_USER)) == NULL)  | 
    
190  | 
    errx(1, "unknown user %s", HTTPD_USER);  | 
    ||
191  | 
    |||
192  | 
    /* Configure the control socket */  | 
    ||
193  | 
    72  | 
    ps->ps_csock.cs_name = NULL;  | 
    |
194  | 
    |||
195  | 
    72  | 
    log_init(debug, LOG_DAEMON);  | 
    |
196  | 
    72  | 
    log_setverbose(verbose);  | 
    |
197  | 
    |||
198  | 
    ✗✓ | 72  | 
    if (env->sc_opts & HTTPD_OPT_NOACTION)  | 
    
199  | 
    ps->ps_noaction = 1;  | 
    ||
200  | 
    |||
201  | 
    72  | 
    ps->ps_instances[PROC_SERVER] = env->sc_prefork_server;  | 
    |
202  | 
    72  | 
    ps->ps_instance = proc_instance;  | 
    |
203  | 
    ✗✓ | 72  | 
    if (title != NULL)  | 
    
204  | 
    ps->ps_title[proc_id] = title;  | 
    ||
205  | 
    |||
206  | 
    ✗✓ | 72  | 
    if (env->sc_chroot == NULL)  | 
    
207  | 
    env->sc_chroot = ps->ps_pw->pw_dir;  | 
    ||
208  | 
    ✓✓ | 432  | 
    for (proc = 0; proc < nitems(procs); proc++)  | 
    
209  | 
    144  | 
    procs[proc].p_chroot = env->sc_chroot;  | 
    |
210  | 
    |||
211  | 
    ✗✓ | 72  | 
    	if (env->sc_logdir == NULL) { | 
    
212  | 
    if (asprintf(&env->sc_logdir, "%s%s", env->sc_chroot,  | 
    ||
213  | 
    HTTPD_LOGROOT) == -1)  | 
    ||
214  | 
    errx(1, "malloc failed");  | 
    ||
215  | 
    }  | 
    ||
216  | 
    |||
217  | 
    /* only the parent returns */  | 
    ||
218  | 
    72  | 
    proc_init(ps, procs, nitems(procs), argc0, argv, proc_id);  | 
    |
219  | 
    |||
220  | 
    72  | 
    	log_procinit("parent"); | 
    |
221  | 
    ✗✓✗✗ | 
    72  | 
    if (!debug && daemon(1, 0) == -1)  | 
    
222  | 
    err(1, "failed to daemonize");  | 
    ||
223  | 
    |||
224  | 
    ✓✗ | 72  | 
    if (ps->ps_noaction == 0)  | 
    
225  | 
    72  | 
    		log_info("startup"); | 
    |
226  | 
    |||
227  | 
    ✗✓ | 72  | 
    	if (pledge("stdio rpath wpath cpath inet dns sendfd flock", NULL) == -1) | 
    
228  | 
    		fatal("pledge"); | 
    ||
229  | 
    |||
230  | 
    72  | 
    event_init();  | 
    |
231  | 
    |||
232  | 
    72  | 
    signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps);  | 
    |
233  | 
    72  | 
    signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps);  | 
    |
234  | 
    72  | 
    signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);  | 
    |
235  | 
    72  | 
    signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);  | 
    |
236  | 
    72  | 
    signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps);  | 
    |
237  | 
    |||
238  | 
    72  | 
    signal_add(&ps->ps_evsigint, NULL);  | 
    |
239  | 
    72  | 
    signal_add(&ps->ps_evsigterm, NULL);  | 
    |
240  | 
    72  | 
    signal_add(&ps->ps_evsighup, NULL);  | 
    |
241  | 
    72  | 
    signal_add(&ps->ps_evsigpipe, NULL);  | 
    |
242  | 
    72  | 
    signal_add(&ps->ps_evsigusr1, NULL);  | 
    |
243  | 
    |||
244  | 
    72  | 
    proc_connect(ps);  | 
    |
245  | 
    |||
246  | 
    ✗✓ | 72  | 
    	if (load_config(env->sc_conffile, env) == -1) { | 
    
247  | 
    proc_kill(env->sc_ps);  | 
    ||
248  | 
    exit(1);  | 
    ||
249  | 
    }  | 
    ||
250  | 
    |||
251  | 
    ✗✓ | 72  | 
    	if (env->sc_opts & HTTPD_OPT_NOACTION) { | 
    
252  | 
    fprintf(stderr, "configuration OK\n");  | 
    ||
253  | 
    proc_kill(env->sc_ps);  | 
    ||
254  | 
    exit(0);  | 
    ||
255  | 
    }  | 
    ||
256  | 
    |||
257  | 
    /* initialize the TLS session id to a random key for all procs */  | 
    ||
258  | 
    72  | 
    arc4random_buf(env->sc_tls_sid, sizeof(env->sc_tls_sid));  | 
    |
259  | 
    |||
260  | 
    ✗✓ | 72  | 
    if (parent_configure(env) == -1)  | 
    
261  | 
    		fatalx("configuration failed"); | 
    ||
262  | 
    |||
263  | 
    72  | 
    event_dispatch();  | 
    |
264  | 
    |||
265  | 
    72  | 
    parent_shutdown(env);  | 
    |
266  | 
    /* NOTREACHED */  | 
    ||
267  | 
    |||
268  | 
    72  | 
    return (0);  | 
    |
269  | 
    72  | 
    }  | 
    |
270  | 
    |||
271  | 
    int  | 
    ||
272  | 
    parent_configure(struct httpd *env)  | 
    ||
273  | 
    { | 
    ||
274  | 
    int id;  | 
    ||
275  | 
    144  | 
    struct ctl_flags cf;  | 
    |
276  | 
    int ret = -1;  | 
    ||
277  | 
    struct server *srv;  | 
    ||
278  | 
    struct media_type *media;  | 
    ||
279  | 
    struct auth *auth;  | 
    ||
280  | 
    |||
281  | 
    ✓✓ | 1440  | 
    	RB_FOREACH(media, mediatypes, env->sc_mediatypes) { | 
    
282  | 
    ✗✓ | 648  | 
    if (config_setmedia(env, media) == -1)  | 
    
283  | 
    			fatal("send media"); | 
    ||
284  | 
    }  | 
    ||
285  | 
    |||
286  | 
    ✗✓ | 144  | 
    	TAILQ_FOREACH(auth, env->sc_auth, auth_entry) { | 
    
287  | 
    if (config_setauth(env, auth) == -1)  | 
    ||
288  | 
    			fatal("send auth"); | 
    ||
289  | 
    }  | 
    ||
290  | 
    |||
291  | 
    /* First send the servers... */  | 
    ||
292  | 
    ✓✓ | 288  | 
    	TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { | 
    
293  | 
    ✓✗ | 72  | 
    if (srv->srv_conf.flags & SRVFLAG_LOCATION)  | 
    
294  | 
    continue;  | 
    ||
295  | 
    /* start the rekey of the tls ticket keys */  | 
    ||
296  | 
    ✓✓✗✓ | 
    96  | 
    if (srv->srv_conf.flags & SRVFLAG_TLS &&  | 
    
297  | 
    24  | 
    srv->srv_conf.tls_ticket_lifetime)  | 
    |
298  | 
    parent_tls_ticket_rekey_start(srv);  | 
    ||
299  | 
    ✗✓ | 72  | 
    if (config_setserver(env, srv) == -1)  | 
    
300  | 
    			fatal("send server"); | 
    ||
301  | 
    }  | 
    ||
302  | 
    /* ...and now send the locations */  | 
    ||
303  | 
    ✓✓ | 288  | 
    	TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { | 
    
304  | 
    ✗✓ | 72  | 
    if ((srv->srv_conf.flags & SRVFLAG_LOCATION) == 0)  | 
    
305  | 
    continue;  | 
    ||
306  | 
    if (config_setserver(env, srv) == -1)  | 
    ||
307  | 
    			fatal("send location"); | 
    ||
308  | 
    }  | 
    ||
309  | 
    |||
310  | 
    /* The servers need to reload their config. */  | 
    ||
311  | 
    72  | 
    env->sc_reload = env->sc_prefork_server + 1;  | 
    |
312  | 
    |||
313  | 
    ✓✓ | 576  | 
    	for (id = 0; id < PROC_MAX; id++) { | 
    
314  | 
    ✓✓ | 216  | 
    if (id == privsep_process)  | 
    
315  | 
    continue;  | 
    ||
316  | 
    144  | 
    cf.cf_opts = env->sc_opts;  | 
    |
317  | 
    144  | 
    cf.cf_flags = env->sc_flags;  | 
    |
318  | 
    144  | 
    memcpy(cf.cf_tls_sid, env->sc_tls_sid, sizeof(cf.cf_tls_sid));  | 
    |
319  | 
    |||
320  | 
    144  | 
    proc_compose(env->sc_ps, id, IMSG_CFG_DONE, &cf, sizeof(cf));  | 
    |
321  | 
    144  | 
    }  | 
    |
322  | 
    |||
323  | 
    ret = 0;  | 
    ||
324  | 
    |||
325  | 
    72  | 
    config_purge(env, CONFIG_ALL & ~CONFIG_SERVERS);  | 
    |
326  | 
    72  | 
    return (ret);  | 
    |
327  | 
    72  | 
    }  | 
    |
328  | 
    |||
329  | 
    void  | 
    ||
330  | 
    parent_reload(struct httpd *env, unsigned int reset, const char *filename)  | 
    ||
331  | 
    { | 
    ||
332  | 
    	if (env->sc_reload) { | 
    ||
333  | 
    		log_debug("%s: already in progress: %d pending", | 
    ||
334  | 
    __func__, env->sc_reload);  | 
    ||
335  | 
    return;  | 
    ||
336  | 
    }  | 
    ||
337  | 
    |||
338  | 
    /* Switch back to the default config file */  | 
    ||
339  | 
    if (filename == NULL || *filename == '\0')  | 
    ||
340  | 
    filename = env->sc_conffile;  | 
    ||
341  | 
    |||
342  | 
    	log_debug("%s: level %d config file %s", __func__, reset, filename); | 
    ||
343  | 
    |||
344  | 
    config_purge(env, CONFIG_ALL);  | 
    ||
345  | 
    |||
346  | 
    	if (reset == CONFIG_RELOAD) { | 
    ||
347  | 
    		if (load_config(filename, env) == -1) { | 
    ||
348  | 
    			log_debug("%s: failed to load config file %s", | 
    ||
349  | 
    __func__, filename);  | 
    ||
350  | 
    }  | 
    ||
351  | 
    |||
352  | 
    config_setreset(env, CONFIG_ALL);  | 
    ||
353  | 
    |||
354  | 
    		if (parent_configure(env) == -1) { | 
    ||
355  | 
    			log_debug("%s: failed to commit config from %s", | 
    ||
356  | 
    __func__, filename);  | 
    ||
357  | 
    }  | 
    ||
358  | 
    } else  | 
    ||
359  | 
    config_setreset(env, reset);  | 
    ||
360  | 
    }  | 
    ||
361  | 
    |||
362  | 
    void  | 
    ||
363  | 
    parent_reopen(struct httpd *env)  | 
    ||
364  | 
    { | 
    ||
365  | 
    proc_compose(env->sc_ps, PROC_LOGGER, IMSG_CTL_REOPEN, NULL, 0);  | 
    ||
366  | 
    }  | 
    ||
367  | 
    |||
368  | 
    void  | 
    ||
369  | 
    parent_configure_done(struct httpd *env)  | 
    ||
370  | 
    { | 
    ||
371  | 
    int id;  | 
    ||
372  | 
    |||
373  | 
    ✗✓ | 288  | 
    	if (env->sc_reload == 0) { | 
    
374  | 
    		log_warnx("%s: configuration already finished", __func__); | 
    ||
375  | 
    return;  | 
    ||
376  | 
    }  | 
    ||
377  | 
    |||
378  | 
    144  | 
    env->sc_reload--;  | 
    |
379  | 
    ✓✓ | 144  | 
    	if (env->sc_reload == 0) { | 
    
380  | 
    ✓✓ | 576  | 
    		for (id = 0; id < PROC_MAX; id++) { | 
    
381  | 
    ✓✓ | 216  | 
    if (id == privsep_process)  | 
    
382  | 
    continue;  | 
    ||
383  | 
    |||
384  | 
    144  | 
    proc_compose(env->sc_ps, id, IMSG_CTL_START, NULL, 0);  | 
    |
385  | 
    144  | 
    }  | 
    |
386  | 
    }  | 
    ||
387  | 
    288  | 
    }  | 
    |
388  | 
    |||
389  | 
    void  | 
    ||
390  | 
    parent_shutdown(struct httpd *env)  | 
    ||
391  | 
    { | 
    ||
392  | 
    144  | 
    config_purge(env, CONFIG_ALL);  | 
    |
393  | 
    |||
394  | 
    72  | 
    proc_kill(env->sc_ps);  | 
    |
395  | 
    72  | 
    control_cleanup(&env->sc_ps->ps_csock);  | 
    |
396  | 
    ✗✓ | 72  | 
    if (env->sc_ps->ps_csock.cs_name != NULL)  | 
    
397  | 
    (void)unlink(env->sc_ps->ps_csock.cs_name);  | 
    ||
398  | 
    |||
399  | 
    free(env->sc_ps);  | 
    ||
400  | 
    free(env);  | 
    ||
401  | 
    |||
402  | 
    	log_info("parent terminating, pid %d", getpid()); | 
    ||
403  | 
    |||
404  | 
    exit(0);  | 
    ||
405  | 
    }  | 
    ||
406  | 
    |||
407  | 
    int  | 
    ||
408  | 
    parent_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)  | 
    ||
409  | 
    { | 
    ||
410  | 
    144  | 
    struct privsep *ps = p->p_ps;  | 
    |
411  | 
    72  | 
    struct httpd *env = ps->ps_env;  | 
    |
412  | 
    |||
413  | 
    ✓✗ | 72  | 
    	switch (imsg->hdr.type) { | 
    
414  | 
    case IMSG_CFG_DONE:  | 
    ||
415  | 
    72  | 
    parent_configure_done(env);  | 
    |
416  | 
    break;  | 
    ||
417  | 
    default:  | 
    ||
418  | 
    return (-1);  | 
    ||
419  | 
    }  | 
    ||
420  | 
    |||
421  | 
    72  | 
    return (0);  | 
    |
422  | 
    72  | 
    }  | 
    |
423  | 
    |||
424  | 
    int  | 
    ||
425  | 
    parent_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)  | 
    ||
426  | 
    { | 
    ||
427  | 
    432  | 
    struct privsep *ps = p->p_ps;  | 
    |
428  | 
    216  | 
    struct httpd *env = ps->ps_env;  | 
    |
429  | 
    unsigned int v;  | 
    ||
430  | 
    char *str = NULL;  | 
    ||
431  | 
    |||
432  | 
    ✗✗✗✗ ✓✓✗  | 
    216  | 
    	switch (imsg->hdr.type) { | 
    
433  | 
    case IMSG_CTL_RESET:  | 
    ||
434  | 
    IMSG_SIZE_CHECK(imsg, &v);  | 
    ||
435  | 
    memcpy(&v, imsg->data, sizeof(v));  | 
    ||
436  | 
    parent_reload(env, v, NULL);  | 
    ||
437  | 
    break;  | 
    ||
438  | 
    case IMSG_CTL_RELOAD:  | 
    ||
439  | 
    if (IMSG_DATA_SIZE(imsg) > 0)  | 
    ||
440  | 
    str = get_string(imsg->data, IMSG_DATA_SIZE(imsg));  | 
    ||
441  | 
    parent_reload(env, CONFIG_RELOAD, str);  | 
    ||
442  | 
    free(str);  | 
    ||
443  | 
    break;  | 
    ||
444  | 
    case IMSG_CTL_SHUTDOWN:  | 
    ||
445  | 
    parent_shutdown(env);  | 
    ||
446  | 
    break;  | 
    ||
447  | 
    case IMSG_CTL_REOPEN:  | 
    ||
448  | 
    parent_reopen(env);  | 
    ||
449  | 
    break;  | 
    ||
450  | 
    case IMSG_CFG_DONE:  | 
    ||
451  | 
    72  | 
    parent_configure_done(env);  | 
    |
452  | 
    72  | 
    break;  | 
    |
453  | 
    case IMSG_LOG_OPEN:  | 
    ||
454  | 
    ✗✓ | 144  | 
    if (logger_open_priv(imsg) == -1)  | 
    
455  | 
    			fatalx("failed to open log file"); | 
    ||
456  | 
    break;  | 
    ||
457  | 
    default:  | 
    ||
458  | 
    return (-1);  | 
    ||
459  | 
    }  | 
    ||
460  | 
    |||
461  | 
    216  | 
    return (0);  | 
    |
462  | 
    216  | 
    }  | 
    |
463  | 
    |||
464  | 
    void  | 
    ||
465  | 
    parent_tls_ticket_rekey_start(struct server *srv)  | 
    ||
466  | 
    { | 
    ||
467  | 
    struct timeval tv;  | 
    ||
468  | 
    |||
469  | 
    server_generate_ticket_key(&srv->srv_conf);  | 
    ||
470  | 
    |||
471  | 
    evtimer_set(&srv->srv_evt, parent_tls_ticket_rekey, srv);  | 
    ||
472  | 
    timerclear(&tv);  | 
    ||
473  | 
    tv.tv_sec = srv->srv_conf.tls_ticket_lifetime / 4;  | 
    ||
474  | 
    evtimer_add(&srv->srv_evt, &tv);  | 
    ||
475  | 
    }  | 
    ||
476  | 
    |||
477  | 
    void  | 
    ||
478  | 
    parent_tls_ticket_rekey(int fd, short events, void *arg)  | 
    ||
479  | 
    { | 
    ||
480  | 
    struct server *srv = arg;  | 
    ||
481  | 
    struct timeval tv;  | 
    ||
482  | 
    |||
483  | 
    server_generate_ticket_key(&srv->srv_conf);  | 
    ||
484  | 
    proc_compose_imsg(httpd_env->sc_ps, PROC_SERVER, -1,  | 
    ||
485  | 
    IMSG_TLSTICKET_REKEY, -1, -1, &srv->srv_conf.tls_ticket_key,  | 
    ||
486  | 
    sizeof(srv->srv_conf.tls_ticket_key));  | 
    ||
487  | 
    explicit_bzero(&srv->srv_conf.tls_ticket_key,  | 
    ||
488  | 
    sizeof(srv->srv_conf.tls_ticket_key));  | 
    ||
489  | 
    |||
490  | 
    evtimer_set(&srv->srv_evt, parent_tls_ticket_rekey, srv);  | 
    ||
491  | 
    timerclear(&tv);  | 
    ||
492  | 
    tv.tv_sec = srv->srv_conf.tls_ticket_lifetime / 4;  | 
    ||
493  | 
    evtimer_add(&srv->srv_evt, &tv);  | 
    ||
494  | 
    }  | 
    ||
495  | 
    |||
496  | 
    /*  | 
    ||
497  | 
    * Utility functions  | 
    ||
498  | 
    */  | 
    ||
499  | 
    |||
500  | 
    void  | 
    ||
501  | 
    event_again(struct event *ev, int fd, short event,  | 
    ||
502  | 
    void (*fn)(int, short, void *),  | 
    ||
503  | 
    struct timeval *start, struct timeval *end, void *arg)  | 
    ||
504  | 
    { | 
    ||
505  | 
    struct timeval tv_next, tv_now, tv;  | 
    ||
506  | 
    |||
507  | 
    getmonotime(&tv_now);  | 
    ||
508  | 
    memcpy(&tv_next, end, sizeof(tv_next));  | 
    ||
509  | 
    timersub(&tv_now, start, &tv_now);  | 
    ||
510  | 
    timersub(&tv_next, &tv_now, &tv_next);  | 
    ||
511  | 
    |||
512  | 
    memset(&tv, 0, sizeof(tv));  | 
    ||
513  | 
    if (timercmp(&tv_next, &tv, >))  | 
    ||
514  | 
    memcpy(&tv, &tv_next, sizeof(tv));  | 
    ||
515  | 
    |||
516  | 
    event_del(ev);  | 
    ||
517  | 
    event_set(ev, fd, event, fn, arg);  | 
    ||
518  | 
    event_add(ev, &tv);  | 
    ||
519  | 
    }  | 
    ||
520  | 
    |||
521  | 
    int  | 
    ||
522  | 
    expand_string(char *label, size_t len, const char *srch, const char *repl)  | 
    ||
523  | 
    { | 
    ||
524  | 
    char *tmp;  | 
    ||
525  | 
    char *p, *q;  | 
    ||
526  | 
    |||
527  | 
    	if ((tmp = calloc(1, len)) == NULL) { | 
    ||
528  | 
    		log_debug("%s: calloc", __func__); | 
    ||
529  | 
    return (-1);  | 
    ||
530  | 
    }  | 
    ||
531  | 
    p = q = label;  | 
    ||
532  | 
    	while ((q = strstr(p, srch)) != NULL) { | 
    ||
533  | 
    *q = '\0';  | 
    ||
534  | 
    if ((strlcat(tmp, p, len) >= len) ||  | 
    ||
535  | 
    		    (strlcat(tmp, repl, len) >= len)) { | 
    ||
536  | 
    			log_debug("%s: string too long", __func__); | 
    ||
537  | 
    free(tmp);  | 
    ||
538  | 
    return (-1);  | 
    ||
539  | 
    }  | 
    ||
540  | 
    q += strlen(srch);  | 
    ||
541  | 
    p = q;  | 
    ||
542  | 
    }  | 
    ||
543  | 
    	if (strlcat(tmp, p, len) >= len) { | 
    ||
544  | 
    		log_debug("%s: string too long", __func__); | 
    ||
545  | 
    free(tmp);  | 
    ||
546  | 
    return (-1);  | 
    ||
547  | 
    }  | 
    ||
548  | 
    (void)strlcpy(label, tmp, len); /* always fits */  | 
    ||
549  | 
    free(tmp);  | 
    ||
550  | 
    |||
551  | 
    return (0);  | 
    ||
552  | 
    }  | 
    ||
553  | 
    |||
554  | 
    const char *  | 
    ||
555  | 
    canonicalize_host(const char *host, char *name, size_t len)  | 
    ||
556  | 
    { | 
    ||
557  | 
    struct sockaddr_in sin4;  | 
    ||
558  | 
    struct sockaddr_in6 sin6;  | 
    ||
559  | 
    size_t i, j;  | 
    ||
560  | 
    size_t plen;  | 
    ||
561  | 
    char c;  | 
    ||
562  | 
    |||
563  | 
    if (len < 2)  | 
    ||
564  | 
    goto fail;  | 
    ||
565  | 
    |||
566  | 
    /*  | 
    ||
567  | 
    * Canonicalize an IPv4/6 address  | 
    ||
568  | 
    */  | 
    ||
569  | 
    if (inet_pton(AF_INET, host, &sin4) == 1)  | 
    ||
570  | 
    return (inet_ntop(AF_INET, &sin4, name, len));  | 
    ||
571  | 
    if (inet_pton(AF_INET6, host, &sin6) == 1)  | 
    ||
572  | 
    return (inet_ntop(AF_INET6, &sin6, name, len));  | 
    ||
573  | 
    |||
574  | 
    /*  | 
    ||
575  | 
    * Canonicalize a hostname  | 
    ||
576  | 
    */  | 
    ||
577  | 
    |||
578  | 
    /* 1. remove repeated dots and convert upper case to lower case */  | 
    ||
579  | 
    plen = strlen(host);  | 
    ||
580  | 
    memset(name, 0, len);  | 
    ||
581  | 
    	for (i = j = 0; i < plen; i++) { | 
    ||
582  | 
    if (j >= (len - 1))  | 
    ||
583  | 
    goto fail;  | 
    ||
584  | 
    c = tolower((unsigned char)host[i]);  | 
    ||
585  | 
    if ((c == '.') && (j == 0 || name[j - 1] == '.'))  | 
    ||
586  | 
    continue;  | 
    ||
587  | 
    name[j++] = c;  | 
    ||
588  | 
    }  | 
    ||
589  | 
    |||
590  | 
    /* 2. remove trailing dots */  | 
    ||
591  | 
    	for (i = j; i > 0; i--) { | 
    ||
592  | 
    if (name[i - 1] != '.')  | 
    ||
593  | 
    break;  | 
    ||
594  | 
    name[i - 1] = '\0';  | 
    ||
595  | 
    j--;  | 
    ||
596  | 
    }  | 
    ||
597  | 
    if (j <= 0)  | 
    ||
598  | 
    goto fail;  | 
    ||
599  | 
    |||
600  | 
    return (name);  | 
    ||
601  | 
    |||
602  | 
    fail:  | 
    ||
603  | 
    errno = EINVAL;  | 
    ||
604  | 
    return (NULL);  | 
    ||
605  | 
    }  | 
    ||
606  | 
    |||
607  | 
    const char *  | 
    ||
608  | 
    url_decode(char *url)  | 
    ||
609  | 
    { | 
    ||
610  | 
    char *p, *q;  | 
    ||
611  | 
    char hex[3];  | 
    ||
612  | 
    unsigned long x;  | 
    ||
613  | 
    |||
614  | 
    hex[2] = '\0';  | 
    ||
615  | 
    p = q = url;  | 
    ||
616  | 
    |||
617  | 
    	while (*p != '\0') { | 
    ||
618  | 
    		switch (*p) { | 
    ||
619  | 
    case '%':  | 
    ||
620  | 
    /* Encoding character is followed by two hex chars */  | 
    ||
621  | 
    if (!(isxdigit((unsigned char)p[1]) &&  | 
    ||
622  | 
    isxdigit((unsigned char)p[2])))  | 
    ||
623  | 
    return (NULL);  | 
    ||
624  | 
    |||
625  | 
    hex[0] = p[1];  | 
    ||
626  | 
    hex[1] = p[2];  | 
    ||
627  | 
    |||
628  | 
    /*  | 
    ||
629  | 
    * We don't have to validate "hex" because it is  | 
    ||
630  | 
    * guaranteed to include two hex chars followed by nul.  | 
    ||
631  | 
    */  | 
    ||
632  | 
    x = strtoul(hex, NULL, 16);  | 
    ||
633  | 
    *q = (char)x;  | 
    ||
634  | 
    p += 2;  | 
    ||
635  | 
    break;  | 
    ||
636  | 
    default:  | 
    ||
637  | 
    *q = *p;  | 
    ||
638  | 
    break;  | 
    ||
639  | 
    }  | 
    ||
640  | 
    p++;  | 
    ||
641  | 
    q++;  | 
    ||
642  | 
    }  | 
    ||
643  | 
    *q = '\0';  | 
    ||
644  | 
    |||
645  | 
    return (url);  | 
    ||
646  | 
    }  | 
    ||
647  | 
    |||
648  | 
    const char *  | 
    ||
649  | 
    canonicalize_path(const char *input, char *path, size_t len)  | 
    ||
650  | 
    { | 
    ||
651  | 
    const char *i;  | 
    ||
652  | 
    char *p, *start, *end;  | 
    ||
653  | 
    |||
654  | 
    /* assuming input starts with '/' and is nul-terminated */  | 
    ||
655  | 
    i = input;  | 
    ||
656  | 
    p = path;  | 
    ||
657  | 
    |||
658  | 
    ✗✓ | 288  | 
    if (*input != '/' || len < 3)  | 
    
659  | 
    return (NULL);  | 
    ||
660  | 
    |||
661  | 
    start = p;  | 
    ||
662  | 
    144  | 
    end = p + (len - 1);  | 
    |
663  | 
    |||
664  | 
    ✓✓ | 3312  | 
    	while (*i != '\0') { | 
    
665  | 
    /* Detect truncation */  | 
    ||
666  | 
    ✗✓ | 1512  | 
    if (p >= end)  | 
    
667  | 
    return (NULL);  | 
    ||
668  | 
    |||
669  | 
    /* 1. check for special path elements */  | 
    ||
670  | 
    ✓✓ | 1512  | 
    		if (i[0] == '/') { | 
    
671  | 
    ✗✓ | 144  | 
    			if (i[1] == '/') { | 
    
672  | 
    /* a) skip repeating '//' slashes */  | 
    ||
673  | 
    while (i[1] == '/')  | 
    ||
674  | 
    i++;  | 
    ||
675  | 
    continue;  | 
    ||
676  | 
    ✗✓✗✗ ✗✗  | 
    144  | 
    } else if (i[1] == '.' && i[2] == '.' &&  | 
    
677  | 
    			    (i[3] == '/' || i[3] == '\0')) { | 
    ||
678  | 
    /* b) revert '..' to previous directory */  | 
    ||
679  | 
    i += 3;  | 
    ||
680  | 
    while (p > start && *p != '/')  | 
    ||
681  | 
    p--;  | 
    ||
682  | 
    *p = '\0';  | 
    ||
683  | 
    continue;  | 
    ||
684  | 
    ✗✓✗✗ | 
    144  | 
    } else if (i[1] == '.' &&  | 
    
685  | 
    			    (i[2] == '/' || i[2] == '\0')) { | 
    ||
686  | 
    /* c) skip unnecessary '.' current dir */  | 
    ||
687  | 
    i += 2;  | 
    ||
688  | 
    continue;  | 
    ||
689  | 
    }  | 
    ||
690  | 
    }  | 
    ||
691  | 
    |||
692  | 
    /* 2. copy any other characters */  | 
    ||
693  | 
    1512  | 
    *p++ = *i;  | 
    |
694  | 
    1512  | 
    i++;  | 
    |
695  | 
    }  | 
    ||
696  | 
    ✗✓ | 144  | 
    if (p == start)  | 
    
697  | 
    *p++ = '/';  | 
    ||
698  | 
    144  | 
    *p++ = '\0';  | 
    |
699  | 
    |||
700  | 
    144  | 
    return (path);  | 
    |
701  | 
    144  | 
    }  | 
    |
702  | 
    |||
703  | 
    size_t  | 
    ||
704  | 
    path_info(char *path)  | 
    ||
705  | 
    { | 
    ||
706  | 
    char *p, *start, *end, ch;  | 
    ||
707  | 
    struct stat st;  | 
    ||
708  | 
    int ret;  | 
    ||
709  | 
    |||
710  | 
    start = path;  | 
    ||
711  | 
    end = start + strlen(path);  | 
    ||
712  | 
    |||
713  | 
    	for (p = end; p > start; p--) { | 
    ||
714  | 
    /* Scan every path component from the end and at each '/' */  | 
    ||
715  | 
    if (p < end && *p != '/')  | 
    ||
716  | 
    continue;  | 
    ||
717  | 
    |||
718  | 
    /* Temporarily cut the path component out */  | 
    ||
719  | 
    ch = *p;  | 
    ||
720  | 
    *p = '\0';  | 
    ||
721  | 
    ret = stat(path, &st);  | 
    ||
722  | 
    *p = ch;  | 
    ||
723  | 
    |||
724  | 
    /* Break if the initial path component was found */  | 
    ||
725  | 
    if (ret == 0)  | 
    ||
726  | 
    break;  | 
    ||
727  | 
    }  | 
    ||
728  | 
    |||
729  | 
    return (p - start);  | 
    ||
730  | 
    }  | 
    ||
731  | 
    |||
732  | 
    char *  | 
    ||
733  | 
    url_encode(const char *src)  | 
    ||
734  | 
    { | 
    ||
735  | 
    static char hex[] = "0123456789ABCDEF";  | 
    ||
736  | 
    char *dp, *dst;  | 
    ||
737  | 
    unsigned char c;  | 
    ||
738  | 
    |||
739  | 
    /* We need 3 times the memory if every letter is encoded. */  | 
    ||
740  | 
    if ((dst = calloc(3, strlen(src) + 1)) == NULL)  | 
    ||
741  | 
    return (NULL);  | 
    ||
742  | 
    |||
743  | 
    	for (dp = dst; *src != 0; src++) { | 
    ||
744  | 
    c = (unsigned char) *src;  | 
    ||
745  | 
    if (c == ' ' || c == '#' || c == '%' || c == '?' || c == '"' ||  | 
    ||
746  | 
    		    c == '&' || c == '<' || c <= 0x1f || c >= 0x7f) { | 
    ||
747  | 
    *dp++ = '%';  | 
    ||
748  | 
    *dp++ = hex[c >> 4];  | 
    ||
749  | 
    *dp++ = hex[c & 0x0f];  | 
    ||
750  | 
    } else  | 
    ||
751  | 
    *dp++ = *src;  | 
    ||
752  | 
    }  | 
    ||
753  | 
    return (dst);  | 
    ||
754  | 
    }  | 
    ||
755  | 
    |||
756  | 
    char*  | 
    ||
757  | 
    escape_html(const char* src)  | 
    ||
758  | 
    { | 
    ||
759  | 
    char *dp, *dst;  | 
    ||
760  | 
    |||
761  | 
    /* We need 5 times the memory if every letter is "&" */  | 
    ||
762  | 
    if ((dst = calloc(5, strlen(src) + 1)) == NULL)  | 
    ||
763  | 
    return NULL;  | 
    ||
764  | 
    |||
765  | 
    	for (dp = dst; *src != 0; src++) { | 
    ||
766  | 
    		if (*src == '<') { | 
    ||
767  | 
    *dp++ = '&';  | 
    ||
768  | 
    *dp++ = 'l';  | 
    ||
769  | 
    *dp++ = 't';  | 
    ||
770  | 
    *dp++ = ';';  | 
    ||
771  | 
    		} else if (*src == '>') { | 
    ||
772  | 
    *dp++ = '&';  | 
    ||
773  | 
    *dp++ = 'g';  | 
    ||
774  | 
    *dp++ = 't';  | 
    ||
775  | 
    *dp++ = ';';  | 
    ||
776  | 
    		} else if (*src == '&') { | 
    ||
777  | 
    *dp++ = '&';  | 
    ||
778  | 
    *dp++ = 'a';  | 
    ||
779  | 
    *dp++ = 'm';  | 
    ||
780  | 
    *dp++ = 'p';  | 
    ||
781  | 
    *dp++ = ';';  | 
    ||
782  | 
    } else  | 
    ||
783  | 
    *dp++ = *src;  | 
    ||
784  | 
    }  | 
    ||
785  | 
    return (dst);  | 
    ||
786  | 
    }  | 
    ||
787  | 
    |||
788  | 
    void  | 
    ||
789  | 
    socket_rlimit(int maxfd)  | 
    ||
790  | 
    { | 
    ||
791  | 
    struct rlimit rl;  | 
    ||
792  | 
    |||
793  | 
    if (getrlimit(RLIMIT_NOFILE, &rl) == -1)  | 
    ||
794  | 
    		fatal("%s: failed to get resource limit", __func__); | 
    ||
795  | 
    	log_debug("%s: max open files %llu", __func__, rl.rlim_max); | 
    ||
796  | 
    |||
797  | 
    /*  | 
    ||
798  | 
    * Allow the maximum number of open file descriptors for this  | 
    ||
799  | 
    * login class (which should be the class "daemon" by default).  | 
    ||
800  | 
    */  | 
    ||
801  | 
    if (maxfd == -1)  | 
    ||
802  | 
    rl.rlim_cur = rl.rlim_max;  | 
    ||
803  | 
    else  | 
    ||
804  | 
    rl.rlim_cur = MAXIMUM(rl.rlim_max, (rlim_t)maxfd);  | 
    ||
805  | 
    if (setrlimit(RLIMIT_NOFILE, &rl) == -1)  | 
    ||
806  | 
    		fatal("%s: failed to set resource limit", __func__); | 
    ||
807  | 
    }  | 
    ||
808  | 
    |||
809  | 
    char *  | 
    ||
810  | 
    evbuffer_getline(struct evbuffer *evb)  | 
    ||
811  | 
    { | 
    ||
812  | 
    uint8_t *ptr = EVBUFFER_DATA(evb);  | 
    ||
813  | 
    size_t len = EVBUFFER_LENGTH(evb);  | 
    ||
814  | 
    char *str;  | 
    ||
815  | 
    size_t i;  | 
    ||
816  | 
    |||
817  | 
    /* Safe version of evbuffer_readline() */  | 
    ||
818  | 
    if ((str = get_string(ptr, len)) == NULL)  | 
    ||
819  | 
    return (NULL);  | 
    ||
820  | 
    |||
821  | 
    	for (i = 0; str[i] != '\0'; i++) { | 
    ||
822  | 
    if (str[i] == '\r' || str[i] == '\n')  | 
    ||
823  | 
    break;  | 
    ||
824  | 
    }  | 
    ||
825  | 
    |||
826  | 
    	if (i == len) { | 
    ||
827  | 
    free(str);  | 
    ||
828  | 
    return (NULL);  | 
    ||
829  | 
    }  | 
    ||
830  | 
    |||
831  | 
    str[i] = '\0';  | 
    ||
832  | 
    |||
833  | 
    	if ((i + 1) < len) { | 
    ||
834  | 
    if (ptr[i] == '\r' && ptr[i + 1] == '\n')  | 
    ||
835  | 
    i++;  | 
    ||
836  | 
    }  | 
    ||
837  | 
    |||
838  | 
    evbuffer_drain(evb, ++i);  | 
    ||
839  | 
    |||
840  | 
    return (str);  | 
    ||
841  | 
    }  | 
    ||
842  | 
    |||
843  | 
    char *  | 
    ||
844  | 
    get_string(uint8_t *ptr, size_t len)  | 
    ||
845  | 
    { | 
    ||
846  | 
    size_t i;  | 
    ||
847  | 
    |||
848  | 
    for (i = 0; i < len; i++)  | 
    ||
849  | 
    if (!(isprint((unsigned char)ptr[i]) ||  | 
    ||
850  | 
    isspace((unsigned char)ptr[i])))  | 
    ||
851  | 
    break;  | 
    ||
852  | 
    |||
853  | 
    return strndup(ptr, i);  | 
    ||
854  | 
    }  | 
    ||
855  | 
    |||
856  | 
    void *  | 
    ||
857  | 
    get_data(uint8_t *ptr, size_t len)  | 
    ||
858  | 
    { | 
    ||
859  | 
    uint8_t *data;  | 
    ||
860  | 
    |||
861  | 
    if ((data = malloc(len)) == NULL)  | 
    ||
862  | 
    return (NULL);  | 
    ||
863  | 
    memcpy(data, ptr, len);  | 
    ||
864  | 
    |||
865  | 
    return (data);  | 
    ||
866  | 
    }  | 
    ||
867  | 
    |||
868  | 
    int  | 
    ||
869  | 
    sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen)  | 
    ||
870  | 
    { | 
    ||
871  | 
    struct sockaddr_in *a4, *b4;  | 
    ||
872  | 
    struct sockaddr_in6 *a6, *b6;  | 
    ||
873  | 
    uint32_t av[4], bv[4], mv[4];  | 
    ||
874  | 
    |||
875  | 
    if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC)  | 
    ||
876  | 
    return (0);  | 
    ||
877  | 
    else if (a->sa_family > b->sa_family)  | 
    ||
878  | 
    return (1);  | 
    ||
879  | 
    else if (a->sa_family < b->sa_family)  | 
    ||
880  | 
    return (-1);  | 
    ||
881  | 
    |||
882  | 
    if (prefixlen == -1)  | 
    ||
883  | 
    memset(&mv, 0xff, sizeof(mv));  | 
    ||
884  | 
    |||
885  | 
    	switch (a->sa_family) { | 
    ||
886  | 
    case AF_INET:  | 
    ||
887  | 
    a4 = (struct sockaddr_in *)a;  | 
    ||
888  | 
    b4 = (struct sockaddr_in *)b;  | 
    ||
889  | 
    |||
890  | 
    av[0] = a4->sin_addr.s_addr;  | 
    ||
891  | 
    bv[0] = b4->sin_addr.s_addr;  | 
    ||
892  | 
    if (prefixlen != -1)  | 
    ||
893  | 
    mv[0] = prefixlen2mask(prefixlen);  | 
    ||
894  | 
    |||
895  | 
    if ((av[0] & mv[0]) > (bv[0] & mv[0]))  | 
    ||
896  | 
    return (1);  | 
    ||
897  | 
    if ((av[0] & mv[0]) < (bv[0] & mv[0]))  | 
    ||
898  | 
    return (-1);  | 
    ||
899  | 
    break;  | 
    ||
900  | 
    case AF_INET6:  | 
    ||
901  | 
    a6 = (struct sockaddr_in6 *)a;  | 
    ||
902  | 
    b6 = (struct sockaddr_in6 *)b;  | 
    ||
903  | 
    |||
904  | 
    memcpy(&av, &a6->sin6_addr.s6_addr, 16);  | 
    ||
905  | 
    memcpy(&bv, &b6->sin6_addr.s6_addr, 16);  | 
    ||
906  | 
    if (prefixlen != -1)  | 
    ||
907  | 
    prefixlen2mask6(prefixlen, mv);  | 
    ||
908  | 
    |||
909  | 
    if ((av[3] & mv[3]) > (bv[3] & mv[3]))  | 
    ||
910  | 
    return (1);  | 
    ||
911  | 
    if ((av[3] & mv[3]) < (bv[3] & mv[3]))  | 
    ||
912  | 
    return (-1);  | 
    ||
913  | 
    if ((av[2] & mv[2]) > (bv[2] & mv[2]))  | 
    ||
914  | 
    return (1);  | 
    ||
915  | 
    if ((av[2] & mv[2]) < (bv[2] & mv[2]))  | 
    ||
916  | 
    return (-1);  | 
    ||
917  | 
    if ((av[1] & mv[1]) > (bv[1] & mv[1]))  | 
    ||
918  | 
    return (1);  | 
    ||
919  | 
    if ((av[1] & mv[1]) < (bv[1] & mv[1]))  | 
    ||
920  | 
    return (-1);  | 
    ||
921  | 
    if ((av[0] & mv[0]) > (bv[0] & mv[0]))  | 
    ||
922  | 
    return (1);  | 
    ||
923  | 
    if ((av[0] & mv[0]) < (bv[0] & mv[0]))  | 
    ||
924  | 
    return (-1);  | 
    ||
925  | 
    break;  | 
    ||
926  | 
    }  | 
    ||
927  | 
    |||
928  | 
    return (0);  | 
    ||
929  | 
    }  | 
    ||
930  | 
    |||
931  | 
    uint32_t  | 
    ||
932  | 
    prefixlen2mask(uint8_t prefixlen)  | 
    ||
933  | 
    { | 
    ||
934  | 
    if (prefixlen == 0)  | 
    ||
935  | 
    return (0);  | 
    ||
936  | 
    |||
937  | 
    if (prefixlen > 32)  | 
    ||
938  | 
    prefixlen = 32;  | 
    ||
939  | 
    |||
940  | 
    return (htonl(0xffffffff << (32 - prefixlen)));  | 
    ||
941  | 
    }  | 
    ||
942  | 
    |||
943  | 
    struct in6_addr *  | 
    ||
944  | 
    prefixlen2mask6(uint8_t prefixlen, uint32_t *mask)  | 
    ||
945  | 
    { | 
    ||
946  | 
    static struct in6_addr s6;  | 
    ||
947  | 
    int i;  | 
    ||
948  | 
    |||
949  | 
    if (prefixlen > 128)  | 
    ||
950  | 
    prefixlen = 128;  | 
    ||
951  | 
    |||
952  | 
    memset(&s6, 0, sizeof(s6));  | 
    ||
953  | 
    for (i = 0; i < prefixlen / 8; i++)  | 
    ||
954  | 
    s6.s6_addr[i] = 0xff;  | 
    ||
955  | 
    i = prefixlen % 8;  | 
    ||
956  | 
    if (i)  | 
    ||
957  | 
    s6.s6_addr[prefixlen / 8] = 0xff00 >> i;  | 
    ||
958  | 
    |||
959  | 
    memcpy(mask, &s6, sizeof(s6));  | 
    ||
960  | 
    |||
961  | 
    return (&s6);  | 
    ||
962  | 
    }  | 
    ||
963  | 
    |||
964  | 
    int  | 
    ||
965  | 
    accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,  | 
    ||
966  | 
    int reserve, volatile int *counter)  | 
    ||
967  | 
    { | 
    ||
968  | 
    int ret;  | 
    ||
969  | 
    if (getdtablecount() + reserve +  | 
    ||
970  | 
    	    *counter >= getdtablesize()) { | 
    ||
971  | 
    errno = EMFILE;  | 
    ||
972  | 
    return (-1);  | 
    ||
973  | 
    }  | 
    ||
974  | 
    |||
975  | 
    	if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK)) > -1) { | 
    ||
976  | 
    (*counter)++;  | 
    ||
977  | 
    		DPRINTF("%s: inflight incremented, now %d",__func__, *counter); | 
    ||
978  | 
    }  | 
    ||
979  | 
    return (ret);  | 
    ||
980  | 
    }  | 
    ||
981  | 
    |||
982  | 
    struct kv *  | 
    ||
983  | 
    kv_add(struct kvtree *keys, char *key, char *value)  | 
    ||
984  | 
    { | 
    ||
985  | 
    struct kv *kv, *oldkv;  | 
    ||
986  | 
    |||
987  | 
    if (key == NULL)  | 
    ||
988  | 
    return (NULL);  | 
    ||
989  | 
    if ((kv = calloc(1, sizeof(*kv))) == NULL)  | 
    ||
990  | 
    return (NULL);  | 
    ||
991  | 
    	if ((kv->kv_key = strdup(key)) == NULL) { | 
    ||
992  | 
    free(kv);  | 
    ||
993  | 
    return (NULL);  | 
    ||
994  | 
    }  | 
    ||
995  | 
    if (value != NULL &&  | 
    ||
996  | 
    	    (kv->kv_value = strdup(value)) == NULL) { | 
    ||
997  | 
    free(kv->kv_key);  | 
    ||
998  | 
    free(kv);  | 
    ||
999  | 
    return (NULL);  | 
    ||
1000  | 
    }  | 
    ||
1001  | 
    TAILQ_INIT(&kv->kv_children);  | 
    ||
1002  | 
    |||
1003  | 
    	if ((oldkv = RB_INSERT(kvtree, keys, kv)) != NULL) { | 
    ||
1004  | 
    TAILQ_INSERT_TAIL(&oldkv->kv_children, kv, kv_entry);  | 
    ||
1005  | 
    kv->kv_parent = oldkv;  | 
    ||
1006  | 
    }  | 
    ||
1007  | 
    |||
1008  | 
    return (kv);  | 
    ||
1009  | 
    }  | 
    ||
1010  | 
    |||
1011  | 
    int  | 
    ||
1012  | 
    kv_set(struct kv *kv, char *fmt, ...)  | 
    ||
1013  | 
    { | 
    ||
1014  | 
    va_list ap;  | 
    ||
1015  | 
    char *value = NULL;  | 
    ||
1016  | 
    struct kv *ckv;  | 
    ||
1017  | 
    int ret;  | 
    ||
1018  | 
    |||
1019  | 
    va_start(ap, fmt);  | 
    ||
1020  | 
    ret = vasprintf(&value, fmt, ap);  | 
    ||
1021  | 
    va_end(ap);  | 
    ||
1022  | 
    if (ret == -1)  | 
    ||
1023  | 
    return (-1);  | 
    ||
1024  | 
    |||
1025  | 
    /* Remove all children */  | 
    ||
1026  | 
    	while ((ckv = TAILQ_FIRST(&kv->kv_children)) != NULL) { | 
    ||
1027  | 
    TAILQ_REMOVE(&kv->kv_children, ckv, kv_entry);  | 
    ||
1028  | 
    kv_free(ckv);  | 
    ||
1029  | 
    free(ckv);  | 
    ||
1030  | 
    }  | 
    ||
1031  | 
    |||
1032  | 
    /* Set the new value */  | 
    ||
1033  | 
    free(kv->kv_value);  | 
    ||
1034  | 
    kv->kv_value = value;  | 
    ||
1035  | 
    |||
1036  | 
    return (0);  | 
    ||
1037  | 
    }  | 
    ||
1038  | 
    |||
1039  | 
    int  | 
    ||
1040  | 
    kv_setkey(struct kv *kv, char *fmt, ...)  | 
    ||
1041  | 
    { | 
    ||
1042  | 
    va_list ap;  | 
    ||
1043  | 
    char *key = NULL;  | 
    ||
1044  | 
    int ret;  | 
    ||
1045  | 
    |||
1046  | 
    va_start(ap, fmt);  | 
    ||
1047  | 
    ret = vasprintf(&key, fmt, ap);  | 
    ||
1048  | 
    va_end(ap);  | 
    ||
1049  | 
    if (ret == -1)  | 
    ||
1050  | 
    return (-1);  | 
    ||
1051  | 
    |||
1052  | 
    free(kv->kv_key);  | 
    ||
1053  | 
    kv->kv_key = key;  | 
    ||
1054  | 
    |||
1055  | 
    return (0);  | 
    ||
1056  | 
    }  | 
    ||
1057  | 
    |||
1058  | 
    void  | 
    ||
1059  | 
    kv_delete(struct kvtree *keys, struct kv *kv)  | 
    ||
1060  | 
    { | 
    ||
1061  | 
    struct kv *ckv;  | 
    ||
1062  | 
    |||
1063  | 
    RB_REMOVE(kvtree, keys, kv);  | 
    ||
1064  | 
    |||
1065  | 
    /* Remove all children */  | 
    ||
1066  | 
    	while ((ckv = TAILQ_FIRST(&kv->kv_children)) != NULL) { | 
    ||
1067  | 
    TAILQ_REMOVE(&kv->kv_children, ckv, kv_entry);  | 
    ||
1068  | 
    kv_free(ckv);  | 
    ||
1069  | 
    free(ckv);  | 
    ||
1070  | 
    }  | 
    ||
1071  | 
    |||
1072  | 
    kv_free(kv);  | 
    ||
1073  | 
    free(kv);  | 
    ||
1074  | 
    }  | 
    ||
1075  | 
    |||
1076  | 
    struct kv *  | 
    ||
1077  | 
    kv_extend(struct kvtree *keys, struct kv *kv, char *value)  | 
    ||
1078  | 
    { | 
    ||
1079  | 
    char *newvalue;  | 
    ||
1080  | 
    |||
1081  | 
    	if (kv == NULL) { | 
    ||
1082  | 
    return (NULL);  | 
    ||
1083  | 
    	} else if (kv->kv_value != NULL) { | 
    ||
1084  | 
    if (asprintf(&newvalue, "%s%s", kv->kv_value, value) == -1)  | 
    ||
1085  | 
    return (NULL);  | 
    ||
1086  | 
    |||
1087  | 
    free(kv->kv_value);  | 
    ||
1088  | 
    kv->kv_value = newvalue;  | 
    ||
1089  | 
    } else if ((kv->kv_value = strdup(value)) == NULL)  | 
    ||
1090  | 
    return (NULL);  | 
    ||
1091  | 
    |||
1092  | 
    return (kv);  | 
    ||
1093  | 
    }  | 
    ||
1094  | 
    |||
1095  | 
    void  | 
    ||
1096  | 
    kv_purge(struct kvtree *keys)  | 
    ||
1097  | 
    { | 
    ||
1098  | 
    struct kv *kv;  | 
    ||
1099  | 
    |||
1100  | 
    while ((kv = RB_MIN(kvtree, keys)) != NULL)  | 
    ||
1101  | 
    kv_delete(keys, kv);  | 
    ||
1102  | 
    }  | 
    ||
1103  | 
    |||
1104  | 
    void  | 
    ||
1105  | 
    kv_free(struct kv *kv)  | 
    ||
1106  | 
    { | 
    ||
1107  | 
    free(kv->kv_key);  | 
    ||
1108  | 
    kv->kv_key = NULL;  | 
    ||
1109  | 
    free(kv->kv_value);  | 
    ||
1110  | 
    kv->kv_value = NULL;  | 
    ||
1111  | 
    memset(kv, 0, sizeof(*kv));  | 
    ||
1112  | 
    }  | 
    ||
1113  | 
    |||
1114  | 
    struct kv *  | 
    ||
1115  | 
    kv_inherit(struct kv *dst, struct kv *src)  | 
    ||
1116  | 
    { | 
    ||
1117  | 
    memset(dst, 0, sizeof(*dst));  | 
    ||
1118  | 
    memcpy(dst, src, sizeof(*dst));  | 
    ||
1119  | 
    TAILQ_INIT(&dst->kv_children);  | 
    ||
1120  | 
    |||
1121  | 
    	if (src->kv_key != NULL) { | 
    ||
1122  | 
    		if ((dst->kv_key = strdup(src->kv_key)) == NULL) { | 
    ||
1123  | 
    kv_free(dst);  | 
    ||
1124  | 
    return (NULL);  | 
    ||
1125  | 
    }  | 
    ||
1126  | 
    }  | 
    ||
1127  | 
    	if (src->kv_value != NULL) { | 
    ||
1128  | 
    		if ((dst->kv_value = strdup(src->kv_value)) == NULL) { | 
    ||
1129  | 
    kv_free(dst);  | 
    ||
1130  | 
    return (NULL);  | 
    ||
1131  | 
    }  | 
    ||
1132  | 
    }  | 
    ||
1133  | 
    |||
1134  | 
    return (dst);  | 
    ||
1135  | 
    }  | 
    ||
1136  | 
    |||
1137  | 
    int  | 
    ||
1138  | 
    kv_log(struct evbuffer *log, struct kv *kv)  | 
    ||
1139  | 
    { | 
    ||
1140  | 
    char *msg;  | 
    ||
1141  | 
    |||
1142  | 
    if (log == NULL)  | 
    ||
1143  | 
    return (0);  | 
    ||
1144  | 
    if (asprintf(&msg, " [%s%s%s]",  | 
    ||
1145  | 
    kv->kv_key == NULL ? "(unknown)" : kv->kv_key,  | 
    ||
1146  | 
    kv->kv_value == NULL ? "" : ": ",  | 
    ||
1147  | 
    kv->kv_value == NULL ? "" : kv->kv_value) == -1)  | 
    ||
1148  | 
    return (-1);  | 
    ||
1149  | 
    	if (evbuffer_add(log, msg, strlen(msg)) == -1) { | 
    ||
1150  | 
    free(msg);  | 
    ||
1151  | 
    return (-1);  | 
    ||
1152  | 
    }  | 
    ||
1153  | 
    free(msg);  | 
    ||
1154  | 
    |||
1155  | 
    return (0);  | 
    ||
1156  | 
    }  | 
    ||
1157  | 
    |||
1158  | 
    struct kv *  | 
    ||
1159  | 
    kv_find(struct kvtree *keys, struct kv *kv)  | 
    ||
1160  | 
    { | 
    ||
1161  | 
    struct kv *match;  | 
    ||
1162  | 
    const char *key;  | 
    ||
1163  | 
    |||
1164  | 
    	if (kv->kv_flags & KV_FLAG_GLOBBING) { | 
    ||
1165  | 
    /* Test header key using shell globbing rules */  | 
    ||
1166  | 
    key = kv->kv_key == NULL ? "" : kv->kv_key;  | 
    ||
1167  | 
    		RB_FOREACH(match, kvtree, keys) { | 
    ||
1168  | 
    if (fnmatch(key, match->kv_key, FNM_CASEFOLD) == 0)  | 
    ||
1169  | 
    break;  | 
    ||
1170  | 
    }  | 
    ||
1171  | 
    	} else { | 
    ||
1172  | 
    /* Fast tree-based lookup only works without globbing */  | 
    ||
1173  | 
    match = RB_FIND(kvtree, keys, kv);  | 
    ||
1174  | 
    }  | 
    ||
1175  | 
    |||
1176  | 
    return (match);  | 
    ||
1177  | 
    }  | 
    ||
1178  | 
    |||
1179  | 
    int  | 
    ||
1180  | 
    kv_cmp(struct kv *a, struct kv *b)  | 
    ||
1181  | 
    { | 
    ||
1182  | 
    return (strcasecmp(a->kv_key, b->kv_key));  | 
    ||
1183  | 
    }  | 
    ||
1184  | 
    |||
1185  | 
    RB_GENERATE(kvtree, kv, kv_node, kv_cmp);  | 
    ||
1186  | 
    |||
1187  | 
    struct media_type *  | 
    ||
1188  | 
    media_add(struct mediatypes *types, struct media_type *media)  | 
    ||
1189  | 
    { | 
    ||
1190  | 
    struct media_type *entry;  | 
    ||
1191  | 
    |||
1192  | 
    ✗✓ | 1296  | 
    	if ((entry = RB_FIND(mediatypes, types, media)) != NULL) { | 
    
1193  | 
    		log_debug("%s: duplicated entry for \"%s\"", __func__, | 
    ||
1194  | 
    media->media_name);  | 
    ||
1195  | 
    return (NULL);  | 
    ||
1196  | 
    }  | 
    ||
1197  | 
    |||
1198  | 
    ✗✓ | 648  | 
    if ((entry = malloc(sizeof(*media))) == NULL)  | 
    
1199  | 
    return (NULL);  | 
    ||
1200  | 
    |||
1201  | 
    648  | 
    memcpy(entry, media, sizeof(*entry));  | 
    |
1202  | 
    ✗✓✗✗ | 
    648  | 
    if (media->media_encoding != NULL &&  | 
    
1203  | 
    	    (entry->media_encoding = strdup(media->media_encoding)) == NULL) { | 
    ||
1204  | 
    free(entry);  | 
    ||
1205  | 
    return (NULL);  | 
    ||
1206  | 
    }  | 
    ||
1207  | 
    648  | 
    RB_INSERT(mediatypes, types, entry);  | 
    |
1208  | 
    |||
1209  | 
    648  | 
    return (entry);  | 
    |
1210  | 
    648  | 
    }  | 
    |
1211  | 
    |||
1212  | 
    void  | 
    ||
1213  | 
    media_delete(struct mediatypes *types, struct media_type *media)  | 
    ||
1214  | 
    { | 
    ||
1215  | 
    1296  | 
    RB_REMOVE(mediatypes, types, media);  | 
    |
1216  | 
    |||
1217  | 
    648  | 
    free(media->media_encoding);  | 
    |
1218  | 
    648  | 
    free(media);  | 
    |
1219  | 
    648  | 
    }  | 
    |
1220  | 
    |||
1221  | 
    void  | 
    ||
1222  | 
    media_purge(struct mediatypes *types)  | 
    ||
1223  | 
    { | 
    ||
1224  | 
    struct media_type *media;  | 
    ||
1225  | 
    |||
1226  | 
    ✓✓ | 1728  | 
    while ((media = RB_MIN(mediatypes, types)) != NULL)  | 
    
1227  | 
    648  | 
    media_delete(types, media);  | 
    |
1228  | 
    144  | 
    }  | 
    |
1229  | 
    |||
1230  | 
    struct media_type *  | 
    ||
1231  | 
    media_find(struct mediatypes *types, const char *file)  | 
    ||
1232  | 
    { | 
    ||
1233  | 
    struct media_type *match, media;  | 
    ||
1234  | 
    char *p;  | 
    ||
1235  | 
    |||
1236  | 
    /* Last component of the file name */  | 
    ||
1237  | 
    p = strchr(file, '\0');  | 
    ||
1238  | 
    while (p > file && p[-1] != '.' && p[-1] != '/')  | 
    ||
1239  | 
    p--;  | 
    ||
1240  | 
    if (*p == '\0')  | 
    ||
1241  | 
    return (NULL);  | 
    ||
1242  | 
    |||
1243  | 
    if (strlcpy(media.media_name, p,  | 
    ||
1244  | 
    sizeof(media.media_name)) >=  | 
    ||
1245  | 
    	    sizeof(media.media_name)) { | 
    ||
1246  | 
    return (NULL);  | 
    ||
1247  | 
    }  | 
    ||
1248  | 
    |||
1249  | 
    /* Find media type by extension name */  | 
    ||
1250  | 
    match = RB_FIND(mediatypes, types, &media);  | 
    ||
1251  | 
    |||
1252  | 
    return (match);  | 
    ||
1253  | 
    }  | 
    ||
1254  | 
    |||
1255  | 
    struct media_type *  | 
    ||
1256  | 
    media_find_config(struct httpd *env, struct server_config *srv_conf,  | 
    ||
1257  | 
    const char *file)  | 
    ||
1258  | 
    { | 
    ||
1259  | 
    struct media_type *match;  | 
    ||
1260  | 
    |||
1261  | 
    if ((match = media_find(env->sc_mediatypes, file)) != NULL)  | 
    ||
1262  | 
    return (match);  | 
    ||
1263  | 
    else if (srv_conf->flags & SRVFLAG_DEFAULT_TYPE)  | 
    ||
1264  | 
    return (&srv_conf->default_type);  | 
    ||
1265  | 
    |||
1266  | 
    /* fallback to the global default type */  | 
    ||
1267  | 
    return (&env->sc_default_type);  | 
    ||
1268  | 
    }  | 
    ||
1269  | 
    |||
1270  | 
    int  | 
    ||
1271  | 
    media_cmp(struct media_type *a, struct media_type *b)  | 
    ||
1272  | 
    { | 
    ||
1273  | 
    6048  | 
    return (strcasecmp(a->media_name, b->media_name));  | 
    |
1274  | 
    }  | 
    ||
1275  | 
    |||
1276  | 
    ✓✓✓✗ ✓✓✓✓ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✓✗✓✓ ✓✓✓✗ ✓✓✓✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✓✓ ✓✗✓✓ ✓✓✓✓ ✓✓✓✗ ✓✗✗✓ ✓✗✗✓ ✓✗✓✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✓✓ ✓✓✓✗ ✓✓✓✗ ✗✓✓✓ ✗✓✗✓ ✗✗✗✓ ✗✗✓✗ ✗✓✓✗ ✓✗✗✓ ✓✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗  | 
    53784  | 
    RB_GENERATE(mediatypes, media_type, media_entry, media_cmp);  | 
    
1277  | 
    |||
1278  | 
    struct auth *  | 
    ||
1279  | 
    auth_add(struct serverauth *serverauth, struct auth *auth)  | 
    ||
1280  | 
    { | 
    ||
1281  | 
    struct auth *entry;  | 
    ||
1282  | 
    |||
1283  | 
    	TAILQ_FOREACH(entry, serverauth, auth_entry) { | 
    ||
1284  | 
    if (strcmp(entry->auth_htpasswd, auth->auth_htpasswd) == 0)  | 
    ||
1285  | 
    return (entry);  | 
    ||
1286  | 
    }  | 
    ||
1287  | 
    |||
1288  | 
    if ((entry = calloc(1, sizeof(*entry))) == NULL)  | 
    ||
1289  | 
    return (NULL);  | 
    ||
1290  | 
    |||
1291  | 
    memcpy(entry, auth, sizeof(*entry));  | 
    ||
1292  | 
    |||
1293  | 
    TAILQ_INSERT_TAIL(serverauth, entry, auth_entry);  | 
    ||
1294  | 
    |||
1295  | 
    return (entry);  | 
    ||
1296  | 
    }  | 
    ||
1297  | 
    |||
1298  | 
    struct auth *  | 
    ||
1299  | 
    auth_byid(struct serverauth *serverauth, uint32_t id)  | 
    ||
1300  | 
    { | 
    ||
1301  | 
    struct auth *auth;  | 
    ||
1302  | 
    |||
1303  | 
    	TAILQ_FOREACH(auth, serverauth, auth_entry) { | 
    ||
1304  | 
    if (auth->auth_id == id)  | 
    ||
1305  | 
    return (auth);  | 
    ||
1306  | 
    }  | 
    ||
1307  | 
    |||
1308  | 
    return (NULL);  | 
    ||
1309  | 
    }  | 
    ||
1310  | 
    |||
1311  | 
    void  | 
    ||
1312  | 
    auth_free(struct serverauth *serverauth, struct auth *auth)  | 
    ||
1313  | 
    { | 
    ||
1314  | 
    TAILQ_REMOVE(serverauth, auth, auth_entry);  | 
    ||
1315  | 
    }  | 
    ||
1316  | 
    |||
1317  | 
    |||
1318  | 
    const char *  | 
    ||
1319  | 
    print_host(struct sockaddr_storage *ss, char *buf, size_t len)  | 
    ||
1320  | 
    { | 
    ||
1321  | 
    if (getnameinfo((struct sockaddr *)ss, ss->ss_len,  | 
    ||
1322  | 
    	    buf, len, NULL, 0, NI_NUMERICHOST) != 0) { | 
    ||
1323  | 
    buf[0] = '\0';  | 
    ||
1324  | 
    return (NULL);  | 
    ||
1325  | 
    }  | 
    ||
1326  | 
    return (buf);  | 
    ||
1327  | 
    }  | 
    ||
1328  | 
    |||
1329  | 
    const char *  | 
    ||
1330  | 
    print_time(struct timeval *a, struct timeval *b, char *buf, size_t len)  | 
    ||
1331  | 
    { | 
    ||
1332  | 
    struct timeval tv;  | 
    ||
1333  | 
    unsigned long h, sec, min;  | 
    ||
1334  | 
    |||
1335  | 
    timerclear(&tv);  | 
    ||
1336  | 
    timersub(a, b, &tv);  | 
    ||
1337  | 
    sec = tv.tv_sec % 60;  | 
    ||
1338  | 
    min = tv.tv_sec / 60 % 60;  | 
    ||
1339  | 
    h = tv.tv_sec / 60 / 60;  | 
    ||
1340  | 
    |||
1341  | 
    snprintf(buf, len, "%.2lu:%.2lu:%.2lu", h, min, sec);  | 
    ||
1342  | 
    return (buf);  | 
    ||
1343  | 
    }  | 
    ||
1344  | 
    |||
1345  | 
    const char *  | 
    ||
1346  | 
    printb_flags(const uint32_t v, const char *bits)  | 
    ||
1347  | 
    { | 
    ||
1348  | 
    static char buf[2][BUFSIZ];  | 
    ||
1349  | 
    static int idx = 0;  | 
    ||
1350  | 
    int i, any = 0;  | 
    ||
1351  | 
    char c, *p, *r;  | 
    ||
1352  | 
    |||
1353  | 
    p = r = buf[++idx % 2];  | 
    ||
1354  | 
    memset(p, 0, BUFSIZ);  | 
    ||
1355  | 
    |||
1356  | 
    	if (bits) { | 
    ||
1357  | 
    bits++;  | 
    ||
1358  | 
    		while ((i = *bits++)) { | 
    ||
1359  | 
    			if (v & (1 << (i - 1))) { | 
    ||
1360  | 
    				if (any) { | 
    ||
1361  | 
    *p++ = ',';  | 
    ||
1362  | 
    *p++ = ' ';  | 
    ||
1363  | 
    }  | 
    ||
1364  | 
    any = 1;  | 
    ||
1365  | 
    				for (; (c = *bits) > 32; bits++) { | 
    ||
1366  | 
    if (c == '_')  | 
    ||
1367  | 
    *p++ = ' ';  | 
    ||
1368  | 
    else  | 
    ||
1369  | 
    *p++ =  | 
    ||
1370  | 
    tolower((unsigned char)c);  | 
    ||
1371  | 
    }  | 
    ||
1372  | 
    } else  | 
    ||
1373  | 
    for (; *bits > 32; bits++)  | 
    ||
1374  | 
    ;  | 
    ||
1375  | 
    }  | 
    ||
1376  | 
    }  | 
    ||
1377  | 
    |||
1378  | 
    return (r);  | 
    ||
1379  | 
    }  | 
    ||
1380  | 
    |||
1381  | 
    void  | 
    ||
1382  | 
    getmonotime(struct timeval *tv)  | 
    ||
1383  | 
    { | 
    ||
1384  | 
    struct timespec ts;  | 
    ||
1385  | 
    |||
1386  | 
    if (clock_gettime(CLOCK_MONOTONIC, &ts))  | 
    ||
1387  | 
    		fatal("clock_gettime"); | 
    ||
1388  | 
    |||
1389  | 
    TIMESPEC_TO_TIMEVAL(tv, &ts);  | 
    ||
1390  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |