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) |